Skip to content

Latest commit

 

History

History
720 lines (649 loc) · 28.6 KB

API Definitions.md

File metadata and controls

720 lines (649 loc) · 28.6 KB

Overview

The terms REST and RESTful spark in the mind varying definitions for everyone who reads them. The following definitions are provided to avoid confusion. These definitions may, or may not, comport with the canonical definition of REST, let alone even begin to argue what that concept is. The purpose served here is to remove any uncertainty for users of this particular API.

Resource Operations

The only goal is to achieve consistent use within the protocol definition here. To aid in this consistency, especially with regard to related projects, this API supports the resource operation definitions described in the Kubernetes API Specification.

This simple approach uses a functional mapping between the standard Create, Read, Update, and Delete (CRUD) operations and HTTP methods. The functional mapping of resource operations to HTTP verbs is defined as follows:

  • Create - POST
  • Read - GET
  • Update - PUT (replace in whole) or PATCH (update in part)
  • Delete - DELETE

In addition, the API supports all the standard protocol conventions with respect to the idempotency of actions related to HTTP Requests and caching of HTTP Responses.

API Specification

The API Specification for the project's master branch is available here: Swagger.io Editor

IMPORTANT NOTE: The only authoritative definition of the resources and endpoints defined for any instance of the API, as well as the methods that are supported for a given resource, is the OpenAPI (aka Swagger 2.0) definition generated by the running API Server itself. The API Specification link above is provided for convenience, but it is the representation of the API design that is accessible on the master branch of this online repository only.

Resources

In an effort to keep this project as DRY as possible, the following is minimalist description of the resources defined for the interface.

The following are the major categories of resources administered by the API.

  • OpenAPI - The OpenAPI (Swagger 2.0) specification for this interface
  • Swagger - The Swagger 2.0 (OpenAPI) specification for this interface
  • Projects Collection - The primary unit of isolation for users, tennant, teams, etc...
  • Namespaces Collection - The project's namespace(s), e.g. "proj-dev", "prog-test", "prog-prod"
  • Applications Collection - The namespace's installed applications
  • Cluster - The namespace's Kraken orchestrated Kubernetes cluster resources object

Order of Operations

There are two restriction on the ordering of operations for the API.

  1. The namespace resource must be created (POST) before a cluster resource can be created. Any attempt to perform an operation on the cluster endpoint when no namespace endpoint is defined will return an error response code.

  2. The cluster resource must be created (POST) before an application resource can be created. Any attempt to perform an operation on the application endpoint when no cluster endpoint is defined will return an error response code. While not strictly necessary, it is preferable to read the status of the cluster endpoint and check that the cluster resources are in an "active" state, as shown below before performing additional operations. If the cluster resources are not ready when an application is deployed, then the create request will still be accepted however the application deployment may timeout (faile) and subsequently need to be resubmitted.

Status: 200 (Ok)
{
 "type": "cluster",
 "id": "de2760b1",
 "nodePoolSize": 3,
 "state": "create_requested"
 "created_at": "1988-11-12T15:18:46-08:00",
}

Example Usage

The following example uses the commonly available curl command to create and retrieve objects from the API. Note that in all the following examples the JSON output has been run through a formatter for improved readability. The JSON pretty printing process is not shown here.

  1. Create (POST) an initial project, then read (GET) it back
$ curl -XPOST -H "Content-Type: application/json" \
      -d '{"name":"acme"}' http://localhost:8080/v1/projects
{
    "created_at": "2017-08-11T22:03:46.021222713-07:00",
    "id": "d1226f6a",
    "name": "acme",
    "namespaces": null,
    "type": "project"
}

# GET the projects collection
$ curl http://localhost:8080/v1/projects
[{
    "created_at": "2017-08-11T22:03:46.021222713-07:00",
    "id": "d1226f6a",
    "name": "acme",
    "namespaces": null,
    "type": "project"
}]

# Alternately - GET just the single project
$ curl http://localhost:8080/v1/projects/a0b4574a
{
    "created_at": "2017-08-11T22:03:46.021222713-07:00",
    "id": "d1226f6a",
    "name": "acme",
    "namespaces": null,
    "type": "project"
}
  1. Create (POST) a namespace in the newly created project, and read (GET) it back.
$ curl -XPOST -H "Content-Type: application/json" \
      -d '{"name":"acme-prod"}' http://localhost:8080/v1/projects/d1226f6a/namespaces
{
    "applications": null,
    "created_at": "2017-08-11T22:04:48.613174018-07:00",
    "id": "20b5bac8",
    "name": "acme-prod",
    "resources": null,
    "type": "namespace"
}

# GET the namespaces collection
$ curl http://localhost:8080/v1/projects/d1226f6a/namespaces
[{
    "applications": null,
    "created_at": "2017-08-11T22:04:48.613174018-07:00",
    "id": "20b5bac8",
    "name": "acme-prod",
    "resources": null,
    "type": "namespace"
}]

# Alternately - GET just the single namespace
$ curl http://localhost:8080/v1/projects/d1226f6a/namespaces/20b5bac8
{
    "applications": null,
    "created_at": "2017-08-11T22:04:48.613174018-07:00",
    "id": "20b5bac8",
    "name": "acme-prod",
    "resources": null,
    "type": "namespace"
}
  1. Create (POST) a cluster resources specification in the newly created namespace, and read (GET) it back.
$ curl -XPOST -H "Content-Type: application/json" \
      -d '{"namespace_id":"20b5bac8", "nodePoolSize": 7}' \
      http://localhost:8080/v1/projects/d1226f6a/cluster
{
    "created_at": "0001-01-01T00:00:00Z",
    "id": "c7454a66",
    "namespace_id": "20b5bac8",
    "nodePoolSize": 7,
    "state": "create_requested",
    "type": "Resource"
}

# GET the cluster resources specification
$ curl http://localhost:8080/v1/projects/d1226f6a/cluster/c7454a66
{
    "created_at": "0001-01-01T00:00:00Z",
    "id": "c7454a66",
    "namespace_id": "20b5bac8",
    "nodePoolSize": 7,
    "state": "create_requested",
    "type": "Resource"
}
  1. Create (POST) an application in to the namespace in the project, and read (GET) it back.
$ curl -XPOST -H "Content-Type: application/json" \
      -d '{"name": "AcmeWidgetPipeline", "version": "v1.0.0-alpha.2", "namespace_id":"20b5bac8"}' \
      http://localhost:8080/v1/projects/d1226f6a/applications
{
    "id": "99bf7a79",
    "name": "AcmeWidgetPipeline",
    "namespace_id": "20b5bac8",
    "status": null,
    "type": "application",
    "version": "v1.0.0-alpha.2"
}

# GET the applications collection for the namespace
$ curl -XGET -H "Content-Type: application/json" \
      -d '{"namespaceid":"20b5bac8"}' http://localhost:8080/v1/projects/d1226f6a/applications
[{
    "id": "99bf7a79",
    "name": "AcmeWidgetPipeline",
    "namespace_id": "20b5bac8",
    "status": null,
    "type": "application",
    "version": "v1.0.0-alpha.2"
}]

# Alternately - GET just the single application
$ curl http://localhost:8080/v1/projects/d1226f6a/applications/99bf7a79
{
    "id": "99bf7a79",
    "name": "AcmeWidgetPipeline",
    "namespace_id": "20b5bac8",
    "status": null,
    "type": "application",
    "version": "v1.0.0-alpha.2"
}
  1. Note that, as we've been creating objects in the API that have relationships with existing objects, those existing objects have been incrementally updated with references to the newly created elements.
# Repeat the GET on the project object
$ curl http://localhost:8080/v1/projects/d1226f6a
{
    "created_at": "2017-08-11T22:03:46.021222713-07:00",
    "id": "d1226f6a",
    "name": "acme",
    "namespaces": [{
        "oid": "20b5bac8",
        "url": "/v1/projects/d1226f6a/namespaces/20b5bac8"
    }],
    "type": "project"
}

# Repeat the GET on the namespace object
$ curl http://localhost:8080/v1/projects/d1226f6a/namespaces/20b5bac8
{
    "applications": [{
        "oid": "99bf7a79",
        "url": "/v1/projects/d1226f6a/applications/99bf7a79"
    }],
    "created_at": "2017-08-11T22:04:48.613174018-07:00",
    "id": "20b5bac8",
    "name": "acme-prod",
    "resources": {
        "oid": "c7454a66",
        "url": "/v1/projects/d1226f6a/cluster/c7454a66"
    },
    "type": "namespace"
}

A Longer Example

The following example also uses curl commands to create and retrieve objects from the API. This example doesn't try to demonstrate all of the functionality of various endpoints. Rather, the goal here is to illustrate what might be a typical workflow for creating a couple of projects, their related resources, an application, and then taking down some (or all) of those resources.

Using a cluster named "Meteor", we'll create some constellation related projects.

  1. Initial Conditions Show the running version of the API and the current state of the cluster's deployed resources (should be empty at this stage).
$ curl -i http://10.37.145.20:8080/v1/healthz
HTTP/1.1 200 OK
Content-Type: text/plain
Date: Tue, 26 Sep 2017 05:06:12 GMT
Content-Length: 121

Health OK: 2017-09-26 05:06:12.545093892 +0000 UTC, semVer: 0.3.33-beta+git.sha.2b88072289849a3dc3d5e735b67fd6fbda655870

$ curl -i http://10.37.145.20:8080/v1/projects
HTTP/1.1 200 OK
Content-Type: application/project+json; type=collection
Date: Tue, 26 Sep 2017 05:06:14 GMT
Content-Length: 3

[]
  1. Next are three preparatory steps shown to create a project, namespace, and node pool.
    1. Create the project neptune
    2. Create a namespace for the project, neptune-test
    3. Create the cluster resources associated with this project/namespaces.
# 1.1 Create the project
$ curl -i -XPOST -H "Content-Type: application/json" -d '{"name":"neptune"}' http://10.37.145.20:8080/v1/projects
HTTP/1.1 201 Created
Content-Type: application/project+json
Date: Tue, 26 Sep 2017 05:08:07 GMT
Content-Length: 116

{
   "created_at": "2017-09-26T05:08:07.332657843Z",
   "id": "88fe3382",
   "name": "neptune",
   "namespaces": null,
   "type": "project"
}

# 1.2 Create the first namespace for the project
$ curl -i -XPOST -H "Content-Type: application/json" -d '{"name":"neptune-test"}' http://10.37.145.20:8080/v1/projects
/88fe3382/namespaces
HTTP/1.1 201 Created
Content-Type: application/namespace+json
Date: Tue, 26 Sep 2017 05:09:50 GMT
Content-Length: 142

{
   "applications": null,
   "created_at": "2017-09-26T05:09:50.708529306Z",
   "id": "114eaa29",
   "name": "neptune-test",
   "resources": null,
   "type": "namespace"
}

# 1.3 Create the cluster resources (node pool) for the project/namespace
$ curl -i -XPOST -H "Content-Type: application/json" -d '{"namespace_id":"114eaa29", "nodePoolSize": 4}' http://10.37.
145.20:8080/v1/projects/88fe3382/cluster
HTTP/1.1 202 Accepted
Content-Type: application/cluster+json
Date: Tue, 26 Sep 2017 05:11:37 GMT
Content-Length: 188

{
   "created_at": "2017-09-26T05:11:37.978446011Z",
   "id": "931479e5",
   "namespace_id": "114eaa29",
   "nodePoolSize": 4,
   "state": "create_requested",
   "type": "Resource",
   "updated_at": "0001-01-01T00:00:00Z"
}
  1. This final step can take a significant amount of time. It's not unusual for this to take anywhere from 5 to 15 minutes, sometimes less, almost never more. As such, it is necessary to poll the newly created endpoint periodically to see it make the transition from either "state": "create_requested" or "state": "starting" to the desired ready "state": "active" - shown below.
$ curl -i http://10.37.145.20:8080/v1/projects/88fe3382/cluster/931479e5
HTTP/1.1 200 OK
Content-Type: application/cluster+json
Date: Tue, 26 Sep 2017 05:12:27 GMT
Content-Length: 190

{
   "created_at": "2017-09-26T05:11:37.978446011Z",
   "id": "931479e5",
   "namespace_id": "114eaa29",
   "nodePoolSize": 4,
   "state": "starting",
   "type": "Resource",
   "updated_at": "2017-09-26T05:11:38.038143732Z"
}

We repeat the polling process until a state change is observed:

$ curl -i http://10.37.145.20:8080/v1/projects/88fe3382/cluster/931479e5
HTTP/1.1 200 OK
Content-Type: application/cluster+json
Date: Tue, 26 Sep 2017 05:19:24 GMT
Content-Length: 188

{
   "created_at": "2017-09-26T05:11:37.978446011Z",
   "id": "931479e5",
   "namespace_id": "114eaa29",
   "nodePoolSize": 4,
   "state": "active",
   "type": "Resource",
   "updated_at": "2017-09-26T05:19:11.720229417Z"
}

Important Note: Normally we would like to see the state reach the value, "state": "active" (as in this case). However, sometimes the state of the cluster has reached the point where we can continue, but with a transient error. When the node state has reaches the value "state": "error_starting", it is possible continue with the understanding that a transient error may have occurred. Alternatively, we could delete the cluster resource and reattempt the create method in the hopes that the second attempt will come up clean. Normally, we choose to push forward with the current state, reasonably confident that the errors were likely do to slow cloud provider resource initializations.

From the API servers log output we see that this operation took about 7 1/2 minutes to complete, also noted in the difference between the crated and updated timestamps in the object itself.

I0926 05:19:11.720265       1 runner.go:422] Queued task delted: type: AddProject, name: neptune, namespace: neptune-test, queueing duration: 7m33.740821987s, running duration 7m33.68208599s

Everytime a physical change to the infrastructure of the cluster is requested, the persistent state of the cluster is reflected in both the clusters config.yaml file and the terraform manifest associated with the cluster. For example, after running the sequence above successfully, the config file shows the delta between the current and backup versions of the file.

$ diff config.yaml config.yaml.1506375409
--- config.yaml
+++ config.yaml.1506375409
@@ -548,18 +548,6 @@
          nodeConfig: *gitlabAwsC4xlarge
          keyPair: *colonybeadKeyPair
# |--> NODE_POOL_MARKER <--|--> DO NOT REMOVE: REQUIRED FOR FOR CONFIG AUTOMATION <--|
-        - name: neptuneNodes
-          count: 4
-          kubeConfig: *colonybeadKube
-          containerConfig: *defaultDocker
-          osConfig: *defaultCoreOs
-          nodeConfig:
-            << : *defaultAwsClusterNode
-            taints:
-              - key: customer
-                value: neptune
-                effect: NoSchedule
-          keyPair: *colonybeadKeyPair

Finally we can view the cluster resources directly.

$ kubectl get nodes -l nodepool=neptuneNodes
NAME                                        STATUS    AGE       VERSION
ip-10-0-13-106.us-west-2.compute.internal   Ready     3m        v1.6.4
ip-10-0-236-3.us-west-2.compute.internal    Ready     2m        v1.6.4
ip-10-0-47-202.us-west-2.compute.internal   Ready     3m        v1.6.4
ip-10-0-70-230.us-west-2.compute.internal   Ready     2m        v1.6.4
  1. Next we bring up a MongoDB Application using the newly created resources.

Note: unescaped line breaks are added for readability only

$ curl -i -XPOST -H "Content-Type: application/json"
      -d '{ "deployment_name": "neptune-mongodb", "server": "quay.io",
            "registry": "samsung_cnct", "name": "mongodb-replicaset",
            "version": "latest", "channel": "stable", "namespace_id": "114eaa29"}'
      http://10.37.145.20:8080/v1/projects/88fe3382/applications
HTTP/1.1 202 Accepted
Content-Type: application/application+json
Date: Tue, 26 Sep 2017 05:20:43 GMT
Content-Length: 413

{
   "channel": "stable",
   "config": "",
   "created_at": "2017-09-26T05:20:43.592429728Z",
   "deployment_name": "neptune-mongodb",
   "id": "e14a6b25",
   "json_values": "",
   "name": "mongodb-replicaset",
   "namespace_id": "114eaa29",
   "registry": "samsung_cnct",
   "server": "quay.io",
   "status": {
       "deployed_at": "0001-01-01T00:00:00Z",
       "state": "UNKNOWN"
   },
   "type": "application",
   "updated_at": "2017-09-26T05:20:43.594109357Z",
   "username": "",
   "version": "latest"
}

A short while later, we query that state of the application to determine if the deployment succeeded.

$ curl -i http://10.37.145.20:8080/v1/projects/88fe3382/applications/e14a6b25
HTTP/1.1 200 OK
Content-Type: application/application+json
Date: Tue, 26 Sep 2017 05:21:24 GMT
Content-Length: 424

{
   "channel": "stable",
   "config": "",
   "created_at": "2017-09-26T05:20:43.592429728Z",
   "deployment_name": "neptune-mongodb",
   "id": "e14a6b25",
   "json_values": "",
   "name": "mongodb-replicaset",
   "namespace_id": "114eaa29",
   "registry": "samsung_cnct",
   "server": "quay.io",
   "status": {
       "deployed_at": "2017-09-26T05:20:47.930774281Z",
       "state": "DEPLOYED"
   },
   "type": "application",
   "updated_at": "2017-09-26T05:20:47.930774962Z",
   "username": "",
   "version": "latest"
}

As validation we check the deployment externally through helm.

$ helm list --namespace neptune-test
NAME            REVISION    UPDATED                     STATUS      CHART                       NAMESPACE  
neptune-mongodb 1           Mon Sep 25 22:20:47 2017    DEPLOYED    mongodb-replicaset-1.2.0-0  neptune-test

$ helm status neptune-mongodb
LAST DEPLOYED: Mon Sep 25 22:20:47 2017
NAMESPACE: neptune-test
STATUS: DEPLOYED

RESOURCES:
==> v1/Service
NAME                                CLUSTER-IP  EXTERNAL-IP  PORT(S)    AGE
neptune-mongodb-mongodb-replicaset  None        <none>       27017/TCP  1m

==> v1beta1/StatefulSet
NAME                                DESIRED  CURRENT  AGE
neptune-mongodb-mongodb-replicaset  3        1        1m

==> v1/ConfigMap
NAME                                      DATA  AGE
neptune-mongodb-mongodb-replicaset        1     1m
neptune-mongodb-mongodb-replicaset-tests  1     1m

And we can check the Kubernetes resources associated with the deployment.

$ kubectl get statefulset,svc,ep,pods -n neptune-test
NAME                                              DESIRED   CURRENT   AGE
statefulsets/neptune-mongodb-mongodb-replicaset   3         3         2m

NAME                                     CLUSTER-IP   EXTERNAL-IP   PORT(S)     AGE
svc/neptune-mongodb-mongodb-replicaset   None         <none>        27017/TCP   2m

NAME                                    ENDPOINTS                                               AGE
ep/neptune-mongodb-mongodb-replicaset   10.128.54.2:27017,10.128.55.2:27017,10.128.56.2:27017   2m

NAME                                      READY     STATUS    RESTARTS   AGE
po/neptune-mongodb-mongodb-replicaset-0   1/1       Running   0          2m
po/neptune-mongodb-mongodb-replicaset-1   1/1       Running   0          2m
po/neptune-mongodb-mongodb-replicaset-2   1/1       Running   0          1m
  1. Now that we have a running instance of MongoDB, let's deploy an application that uses it. The application deployment is pulled from a private repository, so we provide a JSON object that specifies the credentials we use to access the application repository (the actual credentials are redacted in the example).
$ curl -i -XPOST -H "Content-Type: application/json" -d '@./post_body.json' http://10.37.145.20:8080/v1/projects/88fe3
382/applications
HTTP/1.1 202 Accepted
Content-Type: application/application+json
Date: Tue, 26 Sep 2017 05:26:00 GMT
Content-Length: 639

{
   "channel": "stable",
   "config": "",
   "created_at": "2017-09-26T05:26:00.928058318Z",
   "deployment_name": "neptune-reaction",
   "id": "fbc82965",
   "json_values": "{\"ingress\":{\"defaultHost\":{\"hostname\":\"neptune.getreaction.io\"}},\"app\":{\"envVars\":[{\"key\":\"ROOT_URL\",\"value\":\"https://neptune.getreaction.io\"}]},\"mongo\":{\"deploymentName\":\"neptune-mongodb\"}}",
   "name": "reactioncommerce",
   "namespace_id": "114eaa29",
   "registry": "reactioncommerce",
   "server": "quay.io",
   "status": {
       "deployed_at": "0001-01-01T00:00:00Z",
       "state": "UNKNOWN"
   },
   "type": "application",
   "updated_at": "2017-09-26T05:26:00.928097784Z",
   "username": "sostheim",
   "version": "0.5.0"
}

The curl command above passes in a local file as the --data argument. The contents of the file are shown below with redactions.

$ cat post_body.json
{
   "password": "-----------------------------------",
   "username": "sostheim",
   "deployment_name": "neptune-reaction",
   "server": "quay.io",
   "registry": "reactioncommerce",
   "name": "reactioncommerce",
   "version": "0.5.0",
   "channel": "stable",
   "namespace_id": "114eaa29",
   "json_values": "{\"ingress\":{\"defaultHost\":{\"hostname\":\"neptune.getreaction.io\"}},\"app\":{\"envVars\":[{\"key\":\"ROOT_URL\",\"value\":\"https://neptune.getreaction.io\"}]},\"mongo\":{\"deploymentName\":\"neptune-mongodb\"}}"
}

As before, a short while later, we query that state of the application to determine if the deployment succeeded.

$ curl -i http://10.37.145.20:8080/v1/projects/88fe3382/applications/fbc82965
HTTP/1.1 200 OK
Content-Type: application/application+json
Date: Tue, 26 Sep 2017 05:26:50 GMT
Content-Length: 650

{
   "channel": "stable",
   "config": "",
   "created_at": "2017-09-26T05:26:00.928058318Z",
   "deployment_name": "neptune-reaction",
   "id": "fbc82965",
   "json_values": "{\"ingress\":{\"defaultHost\":{\"hostname\":\"neptune.getreaction.io\"}},\"app\":{\"envVars\":[{\"key\":\"ROOT_URL\",\"value\":\"https://neptune.getreaction.io\"}]},\"mongo\":{\"deploymentName\":\"neptune-mongodb\"}}",
   "name": "reactioncommerce",
   "namespace_id": "114eaa29",
   "registry": "reactioncommerce",
   "server": "quay.io",
   "status": {
       "deployed_at": "2017-09-26T05:26:07.729224972Z",
       "state": "DEPLOYED"
   },
   "type": "application",
   "updated_at": "2017-09-26T05:26:07.729225563Z",
   "username": "sostheim",
   "version": "0.5.0"
}

As we have previously, we externally check the created elements.

$ helm list --namespace neptune-test
NAME                REVISION    UPDATED                     STATUS      CHART                       NAMESPACE  
neptune-mongodb     1           Mon Sep 25 22:20:47 2017    DEPLOYED    mongodb-replicaset-1.2.0-0  neptune-test
neptune-reaction    1           Mon Sep 25 22:26:06 2017    DEPLOYED    reactioncommerce-0.5.1      neptune-test

$ kubectl get statefulset,deploy,rs,svc,ep,pods -n neptune-test
NAME                                              DESIRED   CURRENT   AGE
statefulsets/neptune-mongodb-mongodb-replicaset   3         3         7m

NAME                                       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deploy/neptune-reaction-reactioncommerce   1         1         1            1           1m

NAME                                              DESIRED   CURRENT   READY     AGE
rs/neptune-reaction-reactioncommerce-1212697681   1         1         1         1m

NAME                                     CLUSTER-IP    EXTERNAL-IP   PORT(S)     AGE
svc/neptune-mongodb-mongodb-replicaset   None          <none>        27017/TCP   7m
svc/neptune-reaction-reactioncommerce    10.39.15.26   <none>        80/TCP      1m

NAME                                    ENDPOINTS                                               AGE
ep/neptune-mongodb-mongodb-replicaset   10.128.54.2:27017,10.128.55.2:27017,10.128.56.2:27017   7m
ep/neptune-reaction-reactioncommerce    10.128.52.3:3000                                        1m

NAME                                                    READY     STATUS    RESTARTS   AGE
po/neptune-mongodb-mongodb-replicaset-0                 1/1       Running   0          7m
po/neptune-mongodb-mongodb-replicaset-1                 1/1       Running   0          6m
po/neptune-mongodb-mongodb-replicaset-2                 1/1       Running   0          5m
po/neptune-reaction-reactioncommerce-1212697681-0lb5d   1/1       Running   0          1m
  1. After sometime, we decide that Neptune Test resources need to be removed them from the cluster.
# We check the resources one last time before beginning to remove them
$ curl -i http://10.37.145.20:8080/v1/projects/88fe3382/namespaces/114eaa29
HTTP/1.1 200 OK
Content-Type: application/namespace+json
Date: Tue, 26 Sep 2017 05:47:05 GMT
Content-Length: 342

{
   "applications": [{
       "oid": "e14a6b25",
       "url": "/v1/projects/88fe3382/applications/e14a6b25"
   }, {
       "oid": "fbc82965",
       "url": "/v1/projects/88fe3382/applications/fbc82965"
   }],
   "created_at": "2017-09-26T05:09:50.708529306Z",
   "id": "114eaa29",
   "name": "neptune-test",
   "resources": {
       "oid": "931479e5",
       "url": "/v1/projects/88fe3382/cluster/931479e5"
   },
   "type": "namespace"
}

$ curl -i http://10.37.145.20:8080/v1/projects/88fe3382/applications/fbc82965
HTTP/1.1 200 OK
Content-Type: application/application+json
Date: Tue, 26 Sep 2017 05:47:29 GMT
Content-Length: 650

{
   "channel": "stable",
   "config": "",
   "created_at": "2017-09-26T05:26:00.928058318Z",
   "deployment_name": "neptune-reaction",
   "id": "fbc82965",
   "json_values": "{\"ingress\":{\"defaultHost\":{\"hostname\":\"neptune.getreaction.io\"}},\"app\":{\"envVars\":[{\"key\":\"ROOT_URL\",\"value\":\"https://neptune.getreaction.io\"}]},\"mongo\":{\"deploymentName\":\"neptune-mongodb\"}}",
   "name": "reactioncommerce",
   "namespace_id": "114eaa29",
   "registry": "reactioncommerce",
   "server": "quay.io",
   "status": {
       "deployed_at": "2017-09-26T05:26:07.729224972Z",
       "state": "DEPLOYED"
   },
   "type": "application",
   "updated_at": "2017-09-26T05:26:07.729225563Z",
   "username": "sostheim",
   "version": "0.5.0"
}

As a first step, we delete just one object, last application that was deployed.

$ curl -i -XDELETE http://10.37.145.20:8080/v1/projects/88fe3382/applications/fbc82965
HTTP/1.1 204 No Content
Date: Tue, 26 Sep 2017 05:48:25 GMT

$ curl -i http://10.37.145.20:8080/v1/projects/88fe3382/applications/fbc82965
HTTP/1.1 404 Not Found
Date: Tue, 26 Sep 2017 05:48:44 GMT
Content-Length: 0
Content-Type: text/plain; charset=utf-8

$ curl -i http://10.37.145.20:8080/v1/projects/88fe3382/namespaces/114eaa29
HTTP/1.1 200 OK
Content-Type: application/namespace+json
Date: Tue, 26 Sep 2017 05:49:02 GMT
Content-Length: 271

{
   "applications": [{
       "oid": "e14a6b25",
       "url": "/v1/projects/88fe3382/applications/e14a6b25"
   }],
   "created_at": "2017-09-26T05:09:50.708529306Z",
   "id": "114eaa29",
   "name": "neptune-test",
   "resources": {
       "oid": "931479e5",
       "url": "/v1/projects/88fe3382/cluster/931479e5"
   },
   "type": "namespace"
}

Note: Much as the resource creation process has state transitions, so does releasing resources. Although the deletion process above took place to quickly to show them, the resources go from "active" to "deleting" and finally to "deleted".

As one last point of validation, we check that the application has been deleted from helm.

$ helm list --namespace neptune-test
NAME            REVISION    UPDATED                     STATUS      CHART                       NAMESPACE  
neptune-mongodb 1           Mon Sep 25 22:20:47 2017    DEPLOYED    mongodb-replicaset-1.2.0-0  neptune-test

After deleting a single object, we decide instead to use a cascading delete of the entire project. Applying a DELETE operation to any endpoint with aggregate objects causes all of the aggregated objects to be deleted in an appropriate sequence.

# One last check of the project state.
$ curl -i http://10.37.145.20:8080/v1/projects/88fe3382
HTTP/1.1 200 OK
Content-Type: application/project+json
Date: Tue, 26 Sep 2017 05:54:49 GMT
Content-Length: 182

{
   "created_at": "2017-09-26T05:08:07.332657843Z",
   "id": "88fe3382",
   "name": "neptune",
   "namespaces": [{
       "oid": "114eaa29",
       "url": "/v1/projects/88fe3382/namespaces/114eaa29"
   }],
   "type": "project"
}

# And now we delete it.
$ curl -i -XDELETE http://10.37.145.20:8080/v1/projects/88fe3382
HTTP/1.1 204 No Content
Date: Tue, 26 Sep 2017 05:56:27 GMT

$ curl -i http://10.37.145.20:8080/v1/projects
HTTP/1.1 200 OK
Content-Type: application/project+json; type=collection
Date: Tue, 26 Sep 2017 05:56:50 GMT
Content-Length: 3

[]

Typically the applications are able to be removed very quickly so we see them disappear almost right away. Very much like the creation process, removing the nodes from our cloud provider takes considerably more time. After a brief period, 3-5 minutes, we check all the cluster elements and see that everything has been successfully removed.

$ helm list --namespace neptune-test
$ kubectl get replicaset,deploy,rs,svc,ep,pods -n neptune-test
No resources found.
$ kubectl get nodes -l nodepool=neptuneNodes
No resources found.