From b7353c8ff6f9f432b0ad3727e883c425a8357109 Mon Sep 17 00:00:00 2001 From: Chris Doherty Date: Tue, 8 Dec 2015 15:52:12 -0800 Subject: Implement /cookbook_artifacts endpoints. --- lib/chef_zero/chef_data/data_normalizer.rb | 27 +++++---- .../cookbook_artifacts_cookbook_endpoint.rb | 24 ++++++++ .../cookbook_artifacts_cookbook_identifier.rb | 67 ++++++++++++++++++++++ .../endpoints/cookbook_artifacts_endpoint.rb | 34 +++++++++++ .../endpoints/cookbook_version_endpoint.rb | 8 +-- lib/chef_zero/server.rb | 6 ++ spec/run_oc_pedant.rb | 6 +- 7 files changed, 155 insertions(+), 17 deletions(-) create mode 100644 lib/chef_zero/endpoints/cookbook_artifacts_cookbook_endpoint.rb create mode 100644 lib/chef_zero/endpoints/cookbook_artifacts_cookbook_identifier.rb create mode 100644 lib/chef_zero/endpoints/cookbook_artifacts_endpoint.rb diff --git a/lib/chef_zero/chef_data/data_normalizer.rb b/lib/chef_zero/chef_data/data_normalizer.rb index 87e7c8a..9a6d1ae 100644 --- a/lib/chef_zero/chef_data/data_normalizer.rb +++ b/lib/chef_zero/chef_data/data_normalizer.rb @@ -79,7 +79,8 @@ module ChefZero data_bag_item end - def self.normalize_cookbook(endpoint, org_prefix, cookbook, name, version, base_uri, method) + def self.normalize_cookbook(endpoint, org_prefix, cookbook, name, version, base_uri, method, + is_cookbook_artifact=false) # TODO I feel dirty if method != 'PUT' cookbook.each_pair do |key, value| @@ -92,24 +93,28 @@ module ChefZero end end cookbook['name'] ||= "#{name}-#{version}" - # TODO this feels wrong, but the real chef server doesn't expand this default - # cookbook['version'] ||= version - cookbook['cookbook_name'] ||= name + # TODO it feels wrong, but the real chef server doesn't expand 'version', so we don't either. + cookbook['frozen?'] ||= false cookbook['metadata'] ||= {} cookbook['metadata']['version'] ||= version - # Sad to not be expanding defaults just because Chef doesn't :( - # cookbook['metadata']['name'] ||= name - # cookbook['metadata']['description'] ||= "A fabulous new cookbook" + + # defaults set by the client and not the Server: + # metadata[name, description, maintainer, maintainer_email, license] + cookbook['metadata']['long_description'] ||= "" - # cookbook['metadata']['maintainer'] ||= "YOUR_COMPANY_NAME" - # cookbook['metadata']['maintainer_email'] ||= "YOUR_EMAIL" - # cookbook['metadata']['license'] ||= "none" cookbook['metadata']['dependencies'] ||= {} cookbook['metadata']['attributes'] ||= {} cookbook['metadata']['recipes'] ||= {} end - cookbook['json_class'] ||= 'Chef::CookbookVersion' + + if is_cookbook_artifact + cookbook.delete('json_class') + else + cookbook['cookbook_name'] ||= name + cookbook['json_class'] ||= 'Chef::CookbookVersion' + end + cookbook['chef_type'] ||= 'cookbook_version' if method == 'MIN' cookbook['metadata'].delete('attributes') diff --git a/lib/chef_zero/endpoints/cookbook_artifacts_cookbook_endpoint.rb b/lib/chef_zero/endpoints/cookbook_artifacts_cookbook_endpoint.rb new file mode 100644 index 0000000..841f0c9 --- /dev/null +++ b/lib/chef_zero/endpoints/cookbook_artifacts_cookbook_endpoint.rb @@ -0,0 +1,24 @@ +require 'chef_zero/chef_data/data_normalizer' + +module ChefZero + module Endpoints + class CookbookArtifactsCookbookEndpoint < RestBase + # GET /organizations/ORG/cookbook_artifacts/COOKBOOK + def get(request) + cookbook_name = request.rest_path.last + cookbook_url = build_uri(request.base_uri, request.rest_path) + response_data = {} + versions = [] + + list_data(request).each do |identifier| + artifact_url = build_uri(request.base_uri, request.rest_path + [cookbook_name, identifier]) + versions << { url: artifact_url, identifier: identifier } + end + + response_data[cookbook_name] = { url: cookbook_url, versions: versions } + + return json_response(200, response_data) + end + end + end +end diff --git a/lib/chef_zero/endpoints/cookbook_artifacts_cookbook_identifier.rb b/lib/chef_zero/endpoints/cookbook_artifacts_cookbook_identifier.rb new file mode 100644 index 0000000..4d22d08 --- /dev/null +++ b/lib/chef_zero/endpoints/cookbook_artifacts_cookbook_identifier.rb @@ -0,0 +1,67 @@ +require 'chef_zero/chef_data/data_normalizer' + +module ChefZero + module Endpoints + class CookbookArtifactsCookbookIdentifierEndpoint < ChefZero::Endpoints::CookbookVersionEndpoint + # these endpoints are almost, but not quite, not entirely unlike the corresponding /cookbooks endpoints. + # it could all be refactored for maximum reuse, but they're short REST methods with well-defined + # behavioral specs (pedant), so there's not a huge benefit. + + # GET /organizations/ORG/cookbook_artifacts/NAME/IDENTIFIER + def get(request) + cookbook_data = normalize(request, parse_json(get_data(request))) + return json_response(200, cookbook_data) + end + + # PUT /organizations/ORG/cookbook_artifacts/COOKBOOK/IDENTIFIER + def put(request) + if exists_data?(request) + return error(409, "Cookbooks cannot be modified, and a cookbook with this identifier already exists.") + end + + set_data(request, nil, request.body, :create_dir) + + return already_json_response(201, request.body) + end + + # DELETE /organizations/ORG/cookbook_artifacts/COOKBOOK/IDENTIFIER + def delete(request) + begin + doomed_cookbook_json = get_data(request) + identified_cookbook_data = normalize(request, parse_json(doomed_cookbook_json)) + delete_data(request) + + # go through the recipes and delete stuff in the file store. + hoover_unused_checksums(get_checksums(doomed_cookbook_json), request, 'cookbook_artifacts') + + # if this was the last revision, delete the directory so future requests will 404, instead of + # returning 200 with an empty list. + artifact_path = request.rest_path[0..-2] + if list_data(request, artifact_path).size == 0 + delete_data_dir(request, artifact_path) + end + + json_response(200, identified_cookbook_data) + rescue RestErrorResponse => ex + if ex.response_code == 404 + error(404, "not_found") + else + raise + end + end + end + + private + + def make_file_store_path(rest_path, recipe) + rest_path.first(2) + ["file_store", "checksums", recipe["checksum"]] + end + + def normalize(request, cookbook_artifact_data) + ChefData::DataNormalizer.normalize_cookbook(self, request.rest_path[0..1], + cookbook_artifact_data, request.rest_path[3], request.rest_path[4], + request.base_uri, request.method, true) + end + end + end +end diff --git a/lib/chef_zero/endpoints/cookbook_artifacts_endpoint.rb b/lib/chef_zero/endpoints/cookbook_artifacts_endpoint.rb new file mode 100644 index 0000000..d9fdb20 --- /dev/null +++ b/lib/chef_zero/endpoints/cookbook_artifacts_endpoint.rb @@ -0,0 +1,34 @@ +require 'chef_zero/chef_data/data_normalizer' + +module ChefZero + module Endpoints + class CookbookArtifactsEndpoint < RestBase + # GET /organizations/ORG/cookbook_artifacts + def get(request) + data = {} + + artifacts = begin + list_data(request) + rescue Exception => e + if e.response_code == 404 + return already_json_response(200, "{}") + end + end + + artifacts.each do |cookbook_artifact| + cookbook_url = build_uri(request.base_uri, request.rest_path + [cookbook_artifact]) + + versions = [] + list_data(request, request.rest_path + [cookbook_artifact]).each do |identifier| + artifact_url = build_uri(request.base_uri, request.rest_path + [cookbook_artifact, identifier]) + versions << { url: artifact_url, identifier: identifier } + end + + data[cookbook_artifact] = { url: cookbook_url, versions: versions } + end + + return json_response(200, data) + end + end + end +end diff --git a/lib/chef_zero/endpoints/cookbook_version_endpoint.rb b/lib/chef_zero/endpoints/cookbook_version_endpoint.rb index 8dad508..e78d44c 100644 --- a/lib/chef_zero/endpoints/cookbook_version_endpoint.rb +++ b/lib/chef_zero/endpoints/cookbook_version_endpoint.rb @@ -85,10 +85,10 @@ module ChefZero private - def hoover_unused_checksums(deleted_checksums, request) - data_store.list(request.rest_path[0..1] + ['cookbooks']).each do |cookbook_name| - data_store.list(request.rest_path[0..1] + ['cookbooks', cookbook_name]).each do |version| - cookbook = data_store.get(request.rest_path[0..1] + ['cookbooks', cookbook_name, version], request) + def hoover_unused_checksums(deleted_checksums, request, data_type='cookbooks') + data_store.list(request.rest_path[0..1] + [data_type]).each do |cookbook_name| + data_store.list(request.rest_path[0..1] + [data_type, cookbook_name]).each do |version| + cookbook = data_store.get(request.rest_path[0..1] + [data_type, cookbook_name, version], request) deleted_checksums = deleted_checksums - get_checksums(cookbook) end end diff --git a/lib/chef_zero/server.rb b/lib/chef_zero/server.rb index 2d84a52..c96150e 100644 --- a/lib/chef_zero/server.rb +++ b/lib/chef_zero/server.rb @@ -45,6 +45,9 @@ require 'chef_zero/endpoints/actor_endpoint' require 'chef_zero/endpoints/cookbooks_endpoint' require 'chef_zero/endpoints/cookbook_endpoint' require 'chef_zero/endpoints/cookbook_version_endpoint' +require 'chef_zero/endpoints/cookbook_artifacts_cookbook_endpoint' +require 'chef_zero/endpoints/cookbook_artifacts_cookbook_identifier' +require 'chef_zero/endpoints/cookbook_artifacts_endpoint' require 'chef_zero/endpoints/containers_endpoint' require 'chef_zero/endpoints/container_endpoint' require 'chef_zero/endpoints/dummy_endpoint' @@ -558,6 +561,9 @@ module ChefZero [ "/organizations/*/cookbooks", CookbooksEndpoint.new(self) ], [ "/organizations/*/cookbooks/*", CookbookEndpoint.new(self) ], [ "/organizations/*/cookbooks/*/*", CookbookVersionEndpoint.new(self) ], + [ "/organizations/*/cookbook_artifacts", CookbookArtifactsEndpoint.new(self) ], + [ "/organizations/*/cookbook_artifacts/*", CookbookArtifactsCookbookEndpoint.new(self) ], + [ "/organizations/*/cookbook_artifacts/*/*", CookbookArtifactsCookbookIdentifierEndpoint.new(self) ], [ "/organizations/*/data", DataBagsEndpoint.new(self) ], [ "/organizations/*/data/*", DataBagEndpoint.new(self) ], [ "/organizations/*/data/*/*", DataBagItemEndpoint.new(self) ], diff --git a/spec/run_oc_pedant.rb b/spec/run_oc_pedant.rb index 726279f..3cf873c 100644 --- a/spec/run_oc_pedant.rb +++ b/spec/run_oc_pedant.rb @@ -83,7 +83,10 @@ begin '--skip-users', '--skip-organizations', '--skip-multiuser', - '--skip-policies' # these are expected to be broken, they're what we're trying to fix. + + # will be supported. + '--skip-policies', + '--skip-cookbook-artifacts', ] else [] @@ -104,7 +107,6 @@ begin '--skip-headers', # Chef 12 features not yet 100% supported by Chef Zero - '--skip-cookbook-artifacts', '--skip-containers', '--skip-api-v1' ] + chef_fs_skips) -- cgit v1.2.1