[Pulp-dev] Pulp 3 plugin for Chef cookbooks

Simon Baatz gmbnomis at gmail.com
Tue May 15 18:31:47 UTC 2018

I created the  beginnings of a Pulp 3 plugin to manage Chef cookbooks
[1].  Currently, it supports to create repos, create cookbook content
units, and publish repos.  A published & distributed repo will offer
a "universe" API endpoint for tools like Berkshelf.

I did not implement sync yet. I am waiting for "PendingVersion" to be available

I ran into a couple of problems/uncertainties described below (sorry for the
lengthy mail). I am new to Django, DRF, and, obviously, Pulp 3 so any remarks or
suggestions are welcome:

- Create Content: The plugin reads as much meta-data as possible from the actual
  cookbook Artifact when creating a content unit. The motivation for this is:

  - One doesn't need a special tool to upload content, which makes uploading by e.g.
    a CI job easier.
  - It ensures consistency between metadata stored in Pulp and the actual
    metadata in the cookbook.

  However, this requires to extract a metadata file from a gzipped tar archive.
  Content unit creation is synchronous and doing this work in a synchronous call
  might not be optimal (we had a discussion in this topic on the pulp-dev
  mailing list already).

- Publication/Distribution: The metadata file ("universe") for a published
  cookbook repository contains absolute URLs for download (i.e. these point
  to published artifacts in a distribution).

  The current publication/distribution concept seems to have the underlying
  assumption that a Publication is fully relocatable: PublishedMetadata
  artifacts are created by the publishing task and creating a Distribution is a
  synchronous call that determines the base path of the published artifacts.

  This causes a problem with said "universe" file. Ideally, it could be
  pre-computed (it lists all artifacts in the repo).  However, this can't be
  done AFAIK since the base path is unknown at publication time and one can't
  generate additional metadata artifacts for a specific distribution later.

  The best solution I came up with was to implement a dynamic API. To reduce the
  amount of work to be done, the API does a simple string replacement: During
  publication, the universe file is pre-computed using placeholders. In the
  dynamic API these placeholders are replaced with the actual base URL of the

  However, I would prefer not to be forced to implement a dynamic API for static
  information. Is there a way to solve this differently?

- Content-Type Header: The "universe" file is JSON and must have a corresponding
  "Content-Type" HTTP header.

  However, content type of the development server seems to be "text/html" by
  default for all artifacts. Apparently, I can't set the content-type of a
  (meta-data) artifact(?)

- Getting the base url of a distribution in the dynamic API is surprisingly
  complicated and depends on the inner structure of pulp core (I took the
  implementation from 'pulp_ansible'). IMHO, a well defined way to obtain it
  should be part of the plugin API.

- "Content" class: The way to use only a single artifact in Content (like done
  in pulp_file) seems to require in-depth knowledge of the
  Content/ContentSerializer class and its inner workings.

  The downside of this can already be experienced in the "pulp_file" plugin: The
  fields "id" and "created" are missing, since the implementation there just
  overrides the 'fields' in the serializer).

  I think two Content types should be part of the plugin API: one with
  multiple artifacts, and a simpler one with a single artifact

- Uploading an Artifact that already exists returns an error, which is
  annoying if you use http/curl to import artifacts. Suppose some other user
  uploaded an artifact in the past. You won't get useful
  information from the POST request uploading the same artifact:

  HTTP/1.1 400 Bad Request
  Content-Type: application/json
  Date: Sat, 12 May 2018 17:50:54 GMT
  Server: WSGIServer/0.2 CPython/3.6.2
  Vary: Accept

      "non_field_errors": [
          "sha512 checksum must be unique."

  This forced me to do something like:

    sha256=$(sha256sum "$targz" | awk '{print $1}')
    ARTIFACT_HREF=$(http :8000/pulp/api/v3/artifacts/?sha256=$sha256 | jq -r '.results[0]._href')
    if [[ $ARTIFACT_HREF == "null" ]]; then
        echo uploading artifact $cookbook_name sha256: $sha256
        http --form POST http://localhost:8000/pulp/api/v3/artifacts/ file@$targz
        ARTIFACT_HREF=$(http :8000/pulp/api/v3/artifacts/?sha256=$sha256 | jq -r '.results[0]._href')

  Perhaps a "303 See Other" to the existing artifact would help here.

[1]: https://github.com/gmbnomis/pulp_cookbook

More information about the Pulp-dev mailing list