Application Programming Interface

Spackmon implements a set of endpoints that make it possible for spack to communicate with the database via RESTful requests. This document outlines these endpoints, which we call the Spack Monitor Schema. You should read about Authentication if you want to first create a user to interact with these endpoints, and then check out the API Tutorial for a walkthrough.

Overview

Introduction

The Spack Monitor Specification defines an API protocol to standardize the requests and responses for spack to communicate with a monitoring server. It is created in the same spirit as the opencontainers distribution spec.

Definitions

The following terms are used commonly in this document, and a list of definitions is provided for reference:

  • server: a service that provides the endpoints defined in this specification
  • spack: a local spack installation where you intend to monitor builds of software

Notational Conventions

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” are to be interpreted as described in RFC 2119. (Bradner, S., “Key words for use in RFCs to Indicate Requirement Levels”, BCP 14, RFC 2119, March 1997).

Conformance

Currently, we don’t have any tools to test conformance, and the requirements are outlined here.

Determining Support

To check whether or not the server implements the spack monitor spec, the client SHOULD perform a GET request to the /ms1/ (service info) endpoint. If the response is 200 OK, then the server implements the specification. This particular endpoint MAY be used for authentication, however authentication is outside of the scope of this spec.

For example, given a url prefix of http://127.0.0.0 the client would issue a GET request to:

GET http://127.0.0.1:5000/ms1/

And see the service info section below for more details on this request and response. This endpoint serves to either return a successful response to the calling spack client, or direct the client to use a differently named endpoint.

All of the following would be valid:

https://spack.org/ms1/
http://spack.org/ms1/
http://localhost/ms1/
http://127.0.0.1/ms1/
http://127.0.0.1:8282/ms1/

For each of the above, the client implementing the spec would be provided the url before /ms1/ (e.g., https://spack.org/) and then use that to assemble all the endpoints (e.g., https://spack.org/ms1/).

Endpoint Requirements

Servers conforming to the Spack Monitor specification (like Spack Monitor) must provide the following endpoints:

  1. Service Info (GET /ms1/) endpoint with a 200 or 30* response.

Response Details

Generally, a response will return a json object that shows a message, and a return code. For example, a successful response will have a message of “success” to go along with a 200 or 201 response code, while an unsuccessful response will have a message indicating the error, and an error code (e.g., 400, 500, etc.). Error responses may not have data. Successful reponses will have metadata specific to the endpoint.

{"message": "success", "data" {...}, "code": 201}

Generally, endpoint data will return a lookup of objects updated or created based on the type. For example, the new configuration endpoint has metadata about the spec created under a spec attribute of the data:

{
    "message": "success",
    "data": {
        "spec": {
            "full_hash": "p64nmszwer36ly7pnch5fznni4cnmndg",
            "name": "singularity",
            "version": "3.6.4",
            "spack_version": "1.0.0",
            "specs": {
                "cryptsetup": "tmi4pf6umhalop7mi6zyiv7xjpalyzgb",
                "go": "dehg3ddu6gacrmnoexbxhjv2i2d76yq6",
                "libgpg-error": "4cvsg42wxksiup6x74mlabu6un55wjzc",
                "libseccomp": "kfx6zyjxzudw77e3xk6i73bcgi2cavgh",
                "pkgconf": "al2hlnux3cchfhwiv2sbejnxvnogibac",
                "shadow": "aozeq6ybtsnrs5phtonutwes7fe6yhcy",
                "squashfs": "vpemhhpzqqf7mvpzdvcg6szfah6mwt2q",
                "util-linux-uuid": "g362jjpzlfp3qhfm7gdery6v3xgeh3lg"
            }
        },
        "created": true
    },
    "code": 201
}

Timestamps

For all fields that will return a timestamp, we are tentatively going to use the stringified version of a datetime.now(), which looks like this:

2020-12-15 11:43:24.811860

Endpoint Details

Service Info

GET /ms1/

This particular Endpoint exists to check the status of a running monitor service. The client should issue a GET request to this endpoint without any data, and the response should be any of the following:

  • 404: not implemented
  • 200: success (indicates running)
  • 503: service not available
  • 302: found, change namespace
  • 301: redirect

As the initial entrypoint, this endpoint also can communicate back to the client that the prefix (ms1) has changed (e.g., response 302 with a Location header). More detail about the use case for each return code is provided below. For each of the above, the minimal response returned should include in the body a status message and a version, both strings:

{"status": "running", "version": "1.0.0"}
Service Info 404

In the case of a 404 response, it means that the server does not implement the monitor spec. The client should stop, and then respond appropriately (e.g., giving an error message or warning to the user).

{"status": "not implemented", "version": "1.0.0"}
Service Info 200

A 200 is a successful response, meaning that the endpoint was found, and is running.

{"status": "running", "version": "1.0.0"}
Service Info 503

If the service exists but is not running, a 503 is returned. The client should respond in the same way as the 404, except perhaps trying later.

{"status": "service not available", "version": "1.0.0"}
Service Info 302

A 302 is a special status intended to support version changes in a server. For example, let’s say that an updated specification API is served at /ms2/ and by default, a client knows to send a request to /ms1/. To give the client instruction to use /ms2/ for all further interactions, the server would return a 302 response

{"status": "multiple choices", "version": "1.0.0"}

with a location header to indicate the updated url prefix:

Location: /m2/

And the client would update all further prefixes accordingly.

Service Info 301

A 301 is a more traditional redirect that is intended for one off redirects, but not necessarily indicatig to change the entire client namespace. For example, if the server wanted the client to redirect /ms1/ to be /service-info/ (but only for this one case) the response would be:

{"status": "multiple choices", "version": "1.0.0"}

With a location header for just this request:

Location: /service-info/

For each of the above, if the server does not return a Location header, the client should issue an error.

New Spec

POST /ms1/specs/new/

If you have a spec configuration file, you can load it into Python and issue a request to this endpoint. The response can be any of the following:

  • 404: not implemented
  • 201: success (indicates created)
  • 503: service not available
  • 400: bad request
  • 403: permission denied
  • 200: success (but the config already exists)
New Config Created 201

If the set of specs are created from the configuration file, you’ll get a 201 response with data that includes the configuration id (the full_hash) along with full hashes for each package included:

{
    "message": "success",
    "data": {
        "spec": {
            "full_hash": "p64nmszwer36ly7pnch5fznni4cnmndg",
            "name": "singularity",
            "version": "3.6.4",
            "spack_version": "1.0.0",
            "specs": {
                "cryptsetup": "tmi4pf6umhalop7mi6zyiv7xjpalyzgb",
                "go": "dehg3ddu6gacrmnoexbxhjv2i2d76yq6",
                "libgpg-error": "4cvsg42wxksiup6x74mlabu6un55wjzc",
                "libseccomp": "kfx6zyjxzudw77e3xk6i73bcgi2cavgh",
                "pkgconf": "al2hlnux3cchfhwiv2sbejnxvnogibac",
                "shadow": "aozeq6ybtsnrs5phtonutwes7fe6yhcy",
                "squashfs": "vpemhhpzqqf7mvpzdvcg6szfah6mwt2q",
                "util-linux-uuid": "g362jjpzlfp3qhfm7gdery6v3xgeh3lg"
            }
        },
        "created": true
    },
    "code": 201
}

All of the above are full hashes, which we can use as unique identifiers for the builds.

New Config Already Exists 200

If the configuration in question already exists, you’ll get the same data response, but a status code of 200 to indicate success (but not create).

New Build

POST /ms1/builds/new/

This is the endpoint to use to get or lookup a previously done build, and retrieve a build id that can be used for further requests. A new build means that we have a spec, an environment, and we are starting a build! The Build object can be either created or retrieved (if the comibination already exists), and it will hold a reference to the spec, the host build environment, build phases, and (if the build is successful) a list of objects associated (e.g., libraries and other binaries produced).

  • 404: not implemented or spec not found
  • 200: success
  • 201: success
  • 503: service not available
  • 400: bad request
  • 403: permission denied

In either case of success (200 or 201) the response data is formatted as follows:

{
    "message": "Build get or create was successful.",
    "data": {
        "build_created": true,
        "build_environment_created": true,
        "build": {
            "build_id": 1,
            "spec_full_hash": "p64nmszwer36ly7pnch5fznni4cnmndg",
            "spec_name": "singularity"
        }
    },
    "code": 201
}
New Build Created 201

When a new build is created, the status will be 201 to indicate that.

New Build Success 200

If a build is re-run, it may already have been created. The status will be 200 to indicate this.

Update Build Status

POST /ms1/builds/update/

When Spack is running builds, each build task associated with a spec and host environment can either succeed or fail, or something else. In each case, we need to update Spack Monitor with this status. The default status for a build task is NOTRUN. Once the builds start, given a failure, this means that the spec that failed is marked as FAILURE, and the main spec along with the other specs that were not installed are marked as CANCELLED. In the case of success for any package, we mark with SUCCESS. If Spack has a setting to “rollback” we will need to account for that (not currently implemented).

  • 404: not implemented or spec not found
  • 200: success
  • 503: service not available
  • 400: bad request
  • 403: permission denied
Build Task Updated 200

When you want to update the status of a spec build, a successful update will return a 200 response.

{
    "message": "Status updated",
    "data": {
        "build": {
            "build_id": 1,
            "spec_full_hash": "p64nmszwer36ly7pnch5fznni4cnmndg",
            "spec_name": "singularity"
        }
    },
    "code": 200
}

Update Build Phase

POST /ms1/builds/phases/update/

Build Phases are associated with builds, and this is when we have output and error files. The following responses re valid:

  • 404: not implemented or spec not found
  • 200: success
  • 503: service not available
  • 400: bad request
  • 403: permission denied

The request to update the phase should look like the following - we include the build id (created or retrieved from the get build endpoint) along with simple metadata about the phase, and a status.

{
    "build_id": 47,
    "status": "SUCCESS",
    "output": null,
    "phase_name": "autoreconf"
}
Update Build Phase 200

When a build phase is successfully updated, the response data looks like the following:

{
    "message": "Phase autoconf was successfully updated.",
    "code": 200,
    "data": {
        "build_phase": {
            "id": 1,
            "status": "SUCCESS",
            "name": "autoconf"
        }
    }
}

Analyze Builds Metadata

POST /ms1/analyze/builds/

Analyze endpoints correspond with running spack analyze, and are generally for taking some metadata (environment, install files, etc.) from the installed package directory and adding them to the server. When a spec is finished installing, we have a metadata folder, usually within the spack root located at opt/<system>/<compiler>/<package>/.spack with one or more of the following files:

  • spack-configure-args.txt’
  • spack-build-env.txt’
  • spec.yaml
  • archived-files
  • spack-build-out.txt
  • install_manifest.json
  • install_environment.json
  • repos
  • errors.txt

The install_environment.json can easily be used to look up the build id, and then any kind of metadata can be added. The data keys that you send will correspond to where the metadata is added:

  • environment_variables: indicates a list of environment variables to link to a build
  • install_files: indicates a list of install files to be created as objects
  • config_args: the content of spack-configure-args.txt

Any other attribute is assumed to be a lookup of key value pairs, indexed by an object.

As a user, you are allowed to send as many of these keys and data to the server as you see fit, meaning you can do multiple kinds of analyses at once and then update the monitor server. A complete example of sending a build environment and install files is shown below:

{
    "environ": {
        "SPACK_CC": "/usr/bin/gcc",
        "SPACK_CC_RPATH_ARG": "-Wl,-rpath,",
        "SPACK_COMPILER_SPEC": "gcc@9.3.0",
        "SPACK_CXX": "/usr/bin/g++",
        "SPACK_CXX_RPATH_ARG": "-Wl,-rpath,",
        ...
        "SPACK_TARGET_ARGS": "'-march=skylake -mtune=skylake'"
    },
    "config": "",
    "manifest": {
        "/home/vanessa/Desktop/Code/spack/opt/spack/linux-ubuntu20.04-skylake/gcc-9.3.0/diffutils-3.7-2tm6lq6qmyrj6jjiruf7rxb3nzonnq3i/.spack": {
            "mode": 17901,
            "owner": 1000,
            "group": 1000,
            "type": "dir"
        },
        ...
        "/home/vanessa/Desktop/Code/spack/opt/spack/linux-ubuntu20.04-skylake/gcc-9.3.0/diffutils-3.7-2tm6lq6qmyrj6jjiruf7rxb3nzonnq3i": {
            "mode": 17901,
            "owner": 1000,
            "group": 1000,
            "type": "dir"
        }
    },
    "full_hash": "5wdhxf5usk7g6gznwhydbwzmdibxqhjp"
}

The environment is read in, filtered to a list to include only SPACK_* variables, and split into key value pairs, and the package full hash is provided to look up. If any information does not exist, it is simply not sent. A full request might look like the following: The response can then be any of the following:

  • 404: not implemented
  • 503: service not available
  • 400: bad request
  • 403: permission denied
  • 200: success

Unlike other endpoints, this one does not check if data is already added for the build, it simply re-writes it. This is under the assumption that we might re-do an analysis and update the metadata associated. The response is brief and tells the user that the metadata for the build has been updated:

{
    "message": "Metadata updated",
    "data": {
        "build": {
            "build_id": 1,
            "spec_full_hash": "p64nmszwer36ly7pnch5fznni4cnmndg",
            "spec_name": "singularity"
        }
    },
    "code": 200
}