Apcera REST API Recipes

This document provides recipes for performing common tasks with the Apcera API, such as creating an application, updating a package's resources, or exposing a new port on an application. Each recipe includes the API calls necessary to perform the task, as well as the corresponding APC command. The API calls, in fact, exactly mirror the calls made by APC to perform the same task.

This document provides recipes for performing common tasks with the Apcera API, such as creating an application, updating a package's resources, or exposing a new port on an application. Each recipe includes the API calls necessary to perform the task, as well as the corresponding APC command. The API calls, in fact, exactly mirror the calls made by APC to perform the same task.

Create and Start an App with a Custom Start Command

This recipe shows how to create and start an application with a custom start command, as well as enable egress and set some environment variables on the application. This recipe assumes you're deploying a Java web app named sampleapp-bat to the /sandbox/demouser namespace.

The corresponding APC command to do this looks like the following:

app create sampleapp1  \
  --env-set DESIGNATION=sampleapp1-bat  \
  --allow-egress  \
  --disable-routes  \
  --start-cmd foo

Step 1: Lookup job by FQN

The first step is confirming that a job doesn't already exist with the same name and in the same namespace. In this case, the job being created has an FQN of job::/sandbox/demouser::sampleapp1. To check if a job with that FQN already exists you call the GET /jobs endpoint, passing it the job's FQN in the fqn query parameter.

Request

GET /jobs?fqn=job::/sandbox/demouser::sampleapp1 HTTP/1.1
Host: api.demo.proveapcera.io
Authorization: Bearer eyJ0eXAiOiI...

Response

An empty array returned indicates that there wasn't a match, so it's ok to create a job with that FQN.

HTTP/1.1 200 OK
Content-Length: 2
Content-Type: application/json

[]

Step 2: List available runtimes

Use the GET /runtimes to get a list of runtimes, which allows you to determine the appropriate staging pipeline to use to stage the application's package. The /runtimes API returns a list of file patterns you can use to match against files in the user's target deploy directory. Each list of patterns corresponds to one of the built-in staging pipelines.

If you know in advance the type of application being staged, you can proceed to the next step.

Request

GET /runtimes HTTP/1.1
Host: api.demo.proveapcera.io
User-Agent: APC/v0.19.6 (Continuum Client)
Authorization: Bearer eyJ0eXAiOiI...
Hostname: dev.apcera.com-Client-Hostname
Accept-Encoding: gzip

Response

Based on the list, match the file patterns from the response against the application files. Once you have a match you can build the staging pipeline FQN required to stage the application package (for example, stagpipe::/apcera::java or stagpipe::/apcera::ruby).

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Connection: keep-alive
Content-Type: application/json
Date: Fri, 16 Oct 2015 16:33:16 GMT
Minimum-Apc-Version: 0.19.0
Server: nginx
Vary: Accept-Encoding
    [
      {
        "patterns":[
          "bash_start.sh",
          "bash_setup.sh"
        ],
        "runtime":"bash"
      },
      {
        "patterns":[
          "*.rb",
          "Gemfile",
          "Gemfile.lock"
        ],
        "runtime":"ruby"
      },
      {
        "patterns":[
          "package.json",
          "node_modules",
          "*.js"
        ],
        "runtime":"nodejs"
      },
      {
        "patterns":[
          "pom.xml",
          "*.jar",
          "*.war",
          "*.java"
        ],
        "runtime":"java"
      },
      {
        "patterns":[
          "Makefile.PL",
          "Build.PL",
          "META.yml",
          "META.json"
        ],
        "runtime":"perl"
      },
      {
        "patterns":[
          "*.php",
          "php.ini"
        ],
        "runtime":"php"
      },
      {
        "patterns":[
          "*.py",
          "runtime.txt",
          "manage.py"
        ],
        "runtime":"python"
      },
      {
        "patterns":[
          "index.html"
        ],
        "runtime":"static-site"
      },
      {
        "patterns":[
          "*.go"
        ],
        "runtime":"go"
      }
    ]

Step 3: Get Staging Pipeline

Once you know the FQN of the staging pipeline you need to stage your application (stagpipe::/apcera::java, in this case), you make a call to GET /stagingpipelines to get the staging pipeline's UUID, which is required when you later create the package. You pass the staging pipelines FQN as a query parameter to /v1/stagingpipelines so only the pipeline we are interested in is returned.

Request

GET /stagingpipelines?fqn=stagpipe::/apcera::java HTTP/1.1
Host: api.demo.proveapcera.io
Authorization: Bearer eyJ0eXAiOiI...

Response

The response object's uuid field contains the UUID of the staging pipeline (e29eaa10-2f87-40b9-82c2-70cc50822bb9 in this case). Save this value for later use when you create the package.

HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "auto_id":0,
    "created_at":"2015-09-10T15:16:19.080812815Z",
    "created_by":"stagehand@apcera.me",
    "fqn":"stagpipe::/apcera::java",
    "name":"java",
    "stagers":[
      {
        "uuid":"de4024e4-33c3-4a0c-88d1-04b4d637eded"
      }
    ],
    "updated_at":"2015-09-10T15:16:19.080812815Z",
    "updated_by":"",
    "uuid":"e29eaa10-2f87-40b9-82c2-70cc50822bb9",
    "version_id":1
  }
]

Step 4: Package Lookup by FQN

We're almost ready to create the package, but we first we need to check that a package with the same FQN doesn't already exist. To do this, you call GET /packages and pass it the FQN of the package you want to create (package::/sandbox/demouser::sampleapp1, in this case).

Request

GET /packages?fqn=package::/sandbox/demuser::sampleapp1 HTTP/1.1
Host: api.demo.proveapcera.io
Authorization: Bearer eyJ0eXAiOiI...

Response

In this case an empty array is returned, so we know the package doesn't exist. If it had existed, we would want to abort the process.

HTTP/1.1 200 OK
Content-Length: 2
Content-Type: application/json

    []

Step 5: Create the package

Now we're ready to create the package using a call to POST /packages. The POST body is a package object that specifies the package's name, FQN, staging pipeline, and information about the package resource (the Java web app). Specifically, you need to provide the length of the package resource (in bytes) and its calculated SHA. The resource must be a GZIP-compressed tar archive.

Request

POST /packages HTTP/1.1
Host: api.demo.proveapcera.io
Content-Type: application/json

{
  "created_at":"0001-01-01T00:00:00Z",
  "created_by":"",
  "fqn":"package::/sandbox/demouser::sampleapp1",
  "name":"sampleapp1",
  "resource":{
    "length":1890600,
    "sha1":"4141f1497dc2d1e0b829a203cc761e126aee4fc7"
  },
  "staging_pipeline":{
    "uuid":"e29eaa10-2f87-40b9-82c2-70cc50822bb9"
  },
  "state":"",
  "updated_at":"0001-01-01T00:00:00Z",
  "updated_by":""
}

Response

The 200 response indicates the the package was created successfully. The response JSON contains the full package object. Note that the state property of the response JSON is uploading at this point.

HTTP/1.1 200 OK
Content-Type: application/json
Location: http://api.demo.proveapcera.io/v1/packages/f79efd2d-8a83-49d1-8d6d-9bcc61505df9

{
  "created_at":"2015-10-16T16:33:16.725837128Z",
  "created_by":"demouser@apcera.com",
  "fqn":"package::/sandbox/demouser::sampleapp1",
  "name":"sampleapp1",
  "resource":{
    "length":1890600,
    "sha1":"4141f1497dc2d1e0b829a203cc761e126aee4fc7",
    "uuid":"595c7d76-799a-46b0-9903-da398d04a717"
  },
  "stager_job_fqns":[
    "job::/apcera/stagers::java"
  ],
  "staging_pipeline":{
    "uuid":"e29eaa10-2f87-40b9-82c2-70cc50822bb9"
  },
  "staging_pipeline_fqn":"stagpipe::/apcera::java",
  "state":"uploading",
  "updated_at":"2015-10-16T16:33:16.725837128Z",
  "updated_by":"",
  "uuid":"f79efd2d-8a83-49d1-8d6d-9bcc61505df9",
  "version_id":1
}

Step 6: Upload the Package's Resources

Once you've successfully created the package metadata, the next step is uploading its binary resource using the PUT /packages/{packageid}/resources/{resourceid} endpoint. The length and SHA of the binary resource included in the POST body must match the values specified for the length and sha1 fields when you created the package.

Request

The payload is the blob containing the compressed tar archive file:

PUT /packages/resources/f79efd2d-8a83-49d1-8d6d-9bcc61505df9 HTTP/1.1
Authorization: Bearer eyJ0eXAiOiI...
Accept-Encoding: gzip

<package.tar.gz>

Response

An 200 OK response indicates that the upload has completed and that the package is being staged.

HTTP/1.1 200 OK
Content-Length: 4

null

Step 6: Check Progress of Package Staging

Once the package binary has been uploaded, the system being staging the package. Depending on the amount of work the staging pipeline needs to perform (compile source code, install dependencies, and so forth), this process can take anywhere from a few seconds to a few minutes.

To check the status of the staging progress you poll GET /packages/{uuid} and read the value of the response JSON's state field. While the package is being staged, this property is set to "staging". You want to poll the same endpoint until the state value changes to "ready", indicating the package is ready to be run. If the staging process fails for some reason, the value assigned to state is "failed".

Request

Repeat the following request with a 1-2 second delay between each call.

GET /packages/f79efd2d-8a83-49d1-8d6d-9bcc61505df9 HTTP/1.1
Host: api.demo.proveapcera.io

Response

After polling a few times , the response JSON's state property is set to "ready". (Repeat HTTP calls not shown for readability.) Note that the package's dependencies are now set, as well as its start command and start path, which were set by the built-in Java stager.

HTTP/1.1 200 OK
Content-Type: application/json

{
  "created_at":"2015-10-16T16:33:16.725837128Z",
  "created_by":"demouser@apcera.com",
  "dependencies":[
    {
      "name":"build-essential",
      "type":"package"
    },
    {
      "name":"java",
      "type":"runtime"
    }
  ],
  "environment":{
    "START_COMMAND":"java $JAVA_OPTS -jar sampleapp1-0.1-SNAPSHOT.jar $JAVA_FLAGS",
    "START_PATH":"/app"
  },
  "fqn":"package::/sandbox/demouser::sampleapp1",
  "name":"sampleapp1",
  "resource":{
    "length":1890603,
    "sha1":"de13a9d38bbc6cc5fb6687bee63603e9976dfaa4",
    "uuid":"3bce2c01-b763-4817-9c6e-84cece3adafc"
  },
  "stager_job_fqns":[
    "job::/apcera/stagers::java"
  ],
  "staging_pipeline_fqn":"stagpipe::/apcera::java",
  "state":"ready",
  "updated_at":"2015-10-16T16:33:37.57342753Z",
  "updated_by":"staging_coordinator@apcera.me",
  "uuid":"f79efd2d-8a83-49d1-8d6d-9bcc61505df9",
  "version_id":4
}

Step 7: Create the Job

Now that the package has been successfully staged, we're ready to create a job from the package. To create a job you use the POST /jobs endpoint and pass it the job object to create in the POST body. The request object's packages property is an array of package resource objects. In this case, our job needs a reference to the package we just created with the UUID of f79efd2d-8a83-49d1-8d6d-9bcc61505df9. This UUID is assigned to the package resource object's uuid property.

Also note the following properties on the request JSON object:

  • The processes object specifies a custom start_command that's used to start the app, and a custom environment variable named DESIGNATION that's attached to the process.
  • The tags object is assigned the tag "app":"sampleapp1". This tags the job as an application.
  • The job names is set to "sampleapp1" and its FQN to "job::/sandbox/demouser::sampleapp1".

Request

POST /jobs HTTP/1.1
Host: api.demo.proveapcera.io
Authorization: Bearer eyJ0eXAiOiI...
Content-Type: application/json

{
  "created_at":"0001-01-01T00:00:00Z",
  "created_by":"",
  "fqn":"job::/sandbox/demouser::sampleapp1",
  "hard_scheduling_tags":{
  },
  "logs":null,
  "name":"sampleapp1",
  "num_instances":1,
  "packages":[
    {
      "source":"",
      "state":"",
      "uuid":"f79efd2d-8a83-49d1-8d6d-9bcc61505df9"
    }
  ],
  "processes":{
    "app":{
      "environment":{
        "DESIGNATION":"sampleapp1-bat"
      },
      "start_command":"/app/start.sh",
      "start_command_raw":null,
      "start_command_timeout":30,
      "stop_command":"",
      "stop_command_raw":null,
      "stop_timeout":5
    }
  },
  "resources":{
    "cpu":0,
    "disk":1073741824,
    "memory":268435456,
    "netmax":0,
    "network":5000000
  },
  "restart":{
    "maximum_attempts":0,
    "restart_mode":"always"
  },
  "rollout":{
    "errored_state_window":0,
    "flapping_minimum_restarts":0,
    "flapping_percent":0,
    "flapping_window":0,
    "force_stop_old_instances_after":0
  },
  "soft_scheduling_tags":{

  },
  "state":"",
  "tags":{
    "app":"sampleapp1"
  },
  "updated_at":"0001-01-01T00:00:00Z",
  "updated_by":"",
  "uuid":"",
  "version_id":0,
  "weight":0
}

Response

A 200 OK response indicates that our job was successfully created, and the payload contains the job's UUID. (Response abbreviated for readability.)

HTTP/1.1 200 OK
Content-Type: application/json
Location: http://api.demo.proveapcera.io/v1/jobs/0a132bcc-5140-40ac-8291-f366ef0b8846

{
  "created_at":"2015-10-16T16:33:39.772776571Z",
  "created_by":"demouser@apcera.com",
  "fqn":"job::/sandbox/demouser::sampleapp1",
  ...,
  "state":"ready",
  "tags":{
    "app":"sampleapp1"
  },
  "updated_at":"2015-10-16T16:33:39.772776571Z",
  "updated_by":"",
  "uuid":"0a132bcc-5140-40ac-8291-f366ef0b8846",
  "version_id":1,
  "weight":0
}

Once the job has been created successfully, we need to update the package using the PUT /packages/{uuid} so it's aware that the job is "using" it. This is done by adding a linked-job tag to the tags property whose value is the newly created job's UUID, which is "0a132bcc-5140-40ac-8291-f366ef0b8846" in this case.

Request

PUT /packages/f79efd2d-8a83-49d1-8d6d-9bcc61505df9 HTTP/1.1
Host: api.demo.proveapcera.io
Authorization: Bearer eyJ0eXAiOiI...
Content-Type: application/json

{
  "created_at":"2015-10-16T16:33:16.725837128Z",
  "created_by":"demouser@apcera.com",
  "dependencies":[
    {
      "name":"build-essential",
      "type":"package"
    },
    {
      "name":"java",
      "type":"runtime"
    }
  ],
  "environment":{
    "START_COMMAND":"java $JAVA_OPTS -jar sampleapp1-0.1-SNAPSHOT.jar $JAVA_FLAGS",
    "START_PATH":"/app"
  },
  "fqn":"package::/sandbox/demouser::sampleapp1",
  "name":"sampleapp1",
  "resource":{
    "length":1890603,
    "sha1":"de13a9d38bbc6cc5fb6687bee63603e9976dfaa4",
    "uuid":"3bce2c01-b763-4817-9c6e-84cece3adafc"
  },
  "stager_job_fqns":[
    "job::/apcera/stagers::java"
  ],
  "staging_pipeline_fqn":"stagpipe::/apcera::java",
  "state":"ready",
  "tags":{
    "linked-job":"0a132bcc-5140-40ac-8291-f366ef0b8846"
  },
  "updated_at":"2015-10-16T16:33:37.57342753Z",
  "updated_by":"staging_coordinator@apcera.me",
  "uuid":"f79efd2d-8a83-49d1-8d6d-9bcc61505df9",
  "version_id":4
}

Response

A successful update returns the updated package.

HTTP/1.1 200 OK
Content-Type: application/json

{
  "created_at":"2015-10-16T16:33:16.725837128Z",
  "created_by":"demouser@apcera.com",
  "dependencies":[
    {
      "name":"build-essential",
      "type":"package"
    },
    {
      "name":"java",
      "type":"runtime"
    }
  ],
  "environment":{
    "START_COMMAND":"java $JAVA_OPTS -jar sampleapp1-0.1-SNAPSHOT.jar $JAVA_FLAGS",
    "START_PATH":"/app"
  },
  "fqn":"package::/sandbox/demouser::sampleapp1",
  "name":"sampleapp1",
  "resource":{
    "length":1890603,
    "sha1":"de13a9d38bbc6cc5fb6687bee63603e9976dfaa4",
    "uuid":"3bce2c01-b763-4817-9c6e-84cece3adafc"
  },
  "stager_job_fqns":[
    "job::/apcera/stagers::java"
  ],
  "staging_pipeline_fqn":"stagpipe::/apcera::java",
  "state":"ready",
  "tags":{
    "linked-job":"0a132bcc-5140-40ac-8291-f366ef0b8846"
  },
  "updated_at":"2015-10-16T16:33:39.933371793Z",
  "updated_by":"demouser@apcera.com",
  "uuid":"f79efd2d-8a83-49d1-8d6d-9bcc61505df9",
  "version_id":5
}

Step 9: Bind the Job to the Network Service

Our Java application needs to have network access to the outside world. To enable this we need to create a binding between the job and the /apcera::outside service. To create a service binding you use the POST /bindings endpoint; the POST body payload includes job_fqn and service_fqn fields that specify the job and service to bind, respectively, and the FQN of the new binding.

Request

POST /bindings HTTP/1.1
Host: api.demo.proveapcera.io
Authorization: Bearer eyJ0eXAiOiI...
Content-Type: application/json

{
  "fqn":"binding::/sandbox/demouser::egress_for_sampleapp1444c2c50",
  "job_fqn":"job::/sandbox/demouser::sampleapp1",
  "name":"egress_for_sampleapp1444c2c50",
  "service_fqn":"service::/apcera::outside"
}

Response

A 200 OK response indicates the binding was created successfully. The newly created binding object is returned in the response.

HTTP/1.1 200 OK
Content-Type: application/json

{
  "env_var":[
    "EGRESS_FOR_SAMPLEAPP1444C2C50_URI",
    "NETWORK_URI"
  ],
  "fqn":"binding::/sandbox/demouser::egress_for_sampleapp1444c2c50",
  "job_fqn":"job::/sandbox/demouser::sampleapp1",
  "name":"egress_for_sampleapp1444c2c50",
  "parameters":{
    "ipnet":"any",
    "portrange":"all",
    "protocol":"all"
  },
  "service_fqn":"service::/apcera::outside",
  "uuid":"d4748d3b-1c3f-4677-9644-1dceae8faf28"
}

Expose a Port on an Application

To allow your application to respond to incoming network connections you need to expose a port on it. The APC command to do this is as follows:

app update sampleapp1 --port-add 8189

Job Lookup by FQN

First, get a current snapshot of the job to be updated by looking up the job with the GET /jobs endpoint. For details see Job Lookup by FQN. You can skip this step if you have a current snapshot of the job.

Job Update

Update the job to expose port 8189 by adding the following array object to the job definition:

"ports":[
  {
    "number":8189,
    "optional":false
  }
]

Request

PUT /jobs/0a132bcc-5140-40ac-8291-f366ef0b8846 HTTP/1.1
Host: api.demo.proveapcera.io
Authorization: Bearer eyJ0eXAiOiI...
Content-Type: application/json

{
  "bindings":{
    "binding::/sandbox/demouser::egress_for_sampleapp1444c2c50":{
      "env_var":[
        "EGRESS_FOR_SAMPLEAPP1444C2C50_URI",
        "EGRESS_FOR_SAMPLEAPP1444C2C50_URI",
        "NETWORK_URI"
      ],
      "fqn":"binding::/sandbox/demouser::egress_for_sampleapp1444c2c50",
      "job_fqn":"job::/sandbox/demouser::sampleapp1",
      "name":"egress_for_sampleapp1444c2c50",
      "parameters":{
        "ipnet":"any",
        "portrange":"all",
        "protocol":"all"
      },
      "service_fqn":"service::/apcera::outside",
      "uuid":"d4748d3b-1c3f-4677-9644-1dceae8faf28"
    }
  },
  "created_at":"2015-10-16T16:33:39.772776571Z",
  "created_by":"demouser@apcera.com",
  "fqn":"job::/sandbox/demouser::sampleapp1",
  "hard_scheduling_tags":{

  },
  "logs":[
    {
      "channel":"job.$JOB_UUID.stdout",
      "file":"/logs/stdout.*.log"
    },
    {
      "channel":"job.$JOB_UUID.stderr",
      "file":"/logs/stderr.*.log"
    }
  ],
  "name":"sampleapp1",
  "num_instances":1,
  "packages":[
    {
      "source":"user",
      "state":"unknown",
      "uuid":"f79efd2d-8a83-49d1-8d6d-9bcc61505df9"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"50df2e99-4c0e-46cf-836d-6b231da23e87"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"4da207db-95f5-48bd-8be3-0800f4a74983"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"1ac63726-be3f-4b58-80ca-1b5d9f3f3b9a"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"f79efd2d-8a83-49d1-8d6d-9bcc61505df9"
    }
  ],
  "ports":[
    {
      "number":8189,
      "optional":false
    }
  ],
  "processes":{
    "app":{
      "environment":{
        "DESIGNATION":"sampleapp1-bat"
      },
      "start_command":"foo",
      "start_command_raw":[

      ],
      "start_command_timeout":30,
      "stop_command":"",
      "stop_command_raw":[

      ],
      "stop_timeout":5
    }
  },
  "resources":{
    "cpu":0,
    "disk":1073741824,
    "memory":268435456,
    "netmax":0,
    "network":5000000
  },
  "restart":{
    "maximum_attempts":0,
    "restart_mode":"always"
  },
  "rollout":{
    "errored_state_window":0,
    "flapping_minimum_restarts":3,
    "flapping_percent":0.5,
    "flapping_window":300,
    "force_stop_old_instances_after":120
  },
  "soft_scheduling_tags":{

  },
  "state":"ready",
  "tags":{
    "app":"sampleapp1"
  },
  "updated_at":"2015-10-16T16:33:40.235639764Z",
  "updated_by":"demouser@apcera.com",
  "uuid":"0a132bcc-5140-40ac-8291-f366ef0b8846",
  "version_id":2,
  "weight":0
}

Response

The 200 OK response indicates that the port update succeeded, and the job object returned reflects the change. (Response JSON abbreviated for readability).

HTTP/1.1 200 OK
Content-Type: application/json

{
   ...,
    "ports":[
      {
        "number":8189,
        "optional":false
      }
    ],
    "state":"ready",
    "tags":{
      "app":"sampleapp1"
    },
    "updated_at":"2015-10-16T16:33:40.838100137Z",
    "updated_by":"demouser@apcera.com",
    "uuid":"0a132bcc-5140-40ac-8291-f366ef0b8846",
    "version_id":3,
    "weight":0
  }

Add an HTTP Route to an Application

To make a web application available to the outside world you need to add a route to it. The APC command to add a route looks like the following:

route add sampleapp-one.sample.demo.proveapcera.io  \
    --http  \
    --port 8189  \
    --app sampleapp1

Job Lookup by FQN

First, get a current snapshot of the job to be updated by looking up the job with the GET /jobs endpoint. For details see Job Lookup by FQN. You can skip this step if you have a current snapshot of the job.

Job Update

Update the job object's port object with a routes array, as shown in the request object below.

Request

PUT /jobs/0a132bcc-5140-40ac-8291-f366ef0b8846 HTTP/1.1
Host: api.demo.proveapcera.io
User-Agent: APC/v0.19.6 (Continuum Client)
Transfer-Encoding: chunked
Authorization: Bearer eyJ0eXAiOiI...
Content-Type: application/json
Hostname: dev.apcera.com-Client-Hostname
Accept-Encoding: gzip
{
  "bindings":{
    "binding::/sandbox/demouser::egress_for_sampleapp1444c2c50":{
      "env_var":[
        "EGRESS_FOR_SAMPLEAPP1444C2C50_URI",
        "EGRESS_FOR_SAMPLEAPP1444C2C50_URI",
        "NETWORK_URI"
      ],
      "fqn":"binding::/sandbox/demouser::egress_for_sampleapp1444c2c50",
      "job_fqn":"job::/sandbox/demouser::sampleapp1",
      "name":"egress_for_sampleapp1444c2c50",
      "parameters":{
        "ipnet":"any",
        "portrange":"all",
        "protocol":"all"
      },
      "service_fqn":"service::/apcera::outside",
      "uuid":"d4748d3b-1c3f-4677-9644-1dceae8faf28"
    }
  },
  "created_at":"2015-10-16T16:33:39.772776571Z",
  "created_by":"demouser@apcera.com",
  "fqn":"job::/sandbox/demouser::sampleapp1",
  "hard_scheduling_tags":{

  },
  "logs":[
    {
      "channel":"job.$JOB_UUID.stdout",
      "file":"/logs/stdout.*.log"
    },
    {
      "channel":"job.$JOB_UUID.stderr",
      "file":"/logs/stderr.*.log"
    }
  ],
  "name":"sampleapp1",
  "num_instances":1,
  "packages":[
    {
      "source":"user",
      "state":"unknown",
      "uuid":"f79efd2d-8a83-49d1-8d6d-9bcc61505df9"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"50df2e99-4c0e-46cf-836d-6b231da23e87"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"4da207db-95f5-48bd-8be3-0800f4a74983"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"1ac63726-be3f-4b58-80ca-1b5d9f3f3b9a"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"f79efd2d-8a83-49d1-8d6d-9bcc61505df9"
    }
  ],
  "ports":[
    {
      "number":8189,
      "optional":false,
      "routes":[
        {
          "endpoint":"sampleapp-one.sample.demo.proveapcera.io",
          "type":"http",
          "weight":0
        }
      ]
    }
  ],
  "processes":{
    "app":{
      "environment":{
        "DESIGNATION":"sampleapp1-bat"
      },
      "start_command":"/app/start.sh",
      "start_command_raw":[

      ],
      "start_command_timeout":30,
      "stop_command":"",
      "stop_command_raw":[

      ],
      "stop_timeout":5
    }
  },
  "resources":{
    "cpu":0,
    "disk":1073741824,
    "memory":268435456,
    "netmax":0,
    "network":5000000
  },
  "restart":{
    "maximum_attempts":0,
    "restart_mode":"always"
  },
  "rollout":{
    "errored_state_window":0,
    "flapping_minimum_restarts":3,
    "flapping_percent":0.5,
    "flapping_window":300,
    "force_stop_old_instances_after":120
  },
  "soft_scheduling_tags":{

  },
  "state":"ready",
  "tags":{
    "app":"sampleapp1"
  },
  "updated_at":"2015-10-16T16:33:41.296054166Z",
  "updated_by":"demouser@apcera.com",
  "uuid":"0a132bcc-5140-40ac-8291-f366ef0b8846",
  "version_id":4,
  "weight":0
}

Response

A successful response indicates the update was applied. The application is accessible at the specified URL (sampleapp-one.sample.demo.proveapcera.io, in this case). Response abbreviated for readability.

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Connection: keep-alive
Content-Type: application/json
Date: Fri, 16 Oct 2015 16:33:41 GMT
Minimum-Apc-Version: 0.19.0
Server: nginx
Vary: Accept-Encoding
    {
      ...,
      "ports":[
        {
          "number":8189,
          "optional":false,
          "routes":[
            {
              "endpoint":"sampleapp-one.sample.demo.proveapcera.io",
              "type":"http",
              "weight":0
            }
          ]
        }
      ],
      "processes":{
        "app":{
          "environment":{
            "DESIGNATION":"sampleapp1-bat"
          },
          "start_command":"/app/start.sh",
          "start_command_raw":[

          ],
          "start_command_timeout":30,
          "stop_command":"",
          "stop_command_raw":[

          ],
          "stop_timeout":5
        }
      },
      "resources":{
        "cpu":0,
        "disk":1073741824,
        "memory":268435456,
        "netmax":0,
        "network":5000000
      },
      "restart":{
        "maximum_attempts":0,
        "restart_mode":"always"
      },
      "rollout":{
        "errored_state_window":0,
        "flapping_minimum_restarts":3,
        "flapping_percent":0.5,
        "flapping_window":300,
        "force_stop_old_instances_after":120
      },
      "soft_scheduling_tags":{

      },
      "state":"ready",
      "tags":{
        "app":"sampleapp1"
      },
      "updated_at":"2015-10-16T16:33:41.668805765Z",
      "updated_by":"demouser@apcera.com",
      "uuid":"0a132bcc-5140-40ac-8291-f366ef0b8846",
      "version_id":5,
      "weight":0
    }

Remove a Route from an Application

Removing a route is similar to adding a route. You send an update to the PUT v1/jobs/:uuid that contains a snapshot of the existing job object with its routes field removed.

The corresponding APC command looks like the following:

route delete sampleapp-one.sample.demo.proveapcera.io --http --app sampleapp1

Job Lookup by FQN

First, get a current snapshot of the job to be updated by looking up the job with GET /jobs. For details see Job Lookup by FQN. You can skip this step if you have a current snapshot of the job.

Job Update

Removing a route is similar to adding a route. After you get a snapshot of the job to update, you remove the routes from the ports JSON element and pass the updated object to PUT v1/jobs/:uuid.

Request

PUT /jobs/0a132bcc-5140-40ac-8291-f366ef0b8846 HTTP/1.1
Host: api.demo.proveapcera.io
Authorization: Bearer eyJ0eXAiOiI...
Content-Type: application/json
{
  "bindings":{
    "binding::/sandbox/demouser::egress_for_sampleapp1444c2c50":{
      "env_var":[
        "EGRESS_FOR_SAMPLEAPP1444C2C50_URI",
        "EGRESS_FOR_SAMPLEAPP1444C2C50_URI",
        "NETWORK_URI"
      ],
      "fqn":"binding::/sandbox/demouser::egress_for_sampleapp1444c2c50",
      "job_fqn":"job::/sandbox/demouser::sampleapp1",
      "name":"egress_for_sampleapp1444c2c50",
      "parameters":{
        "ipnet":"any",
        "portrange":"all",
        "protocol":"all"
      },
      "service_fqn":"service::/apcera::outside",
      "uuid":"d4748d3b-1c3f-4677-9644-1dceae8faf28"
    }
  },
  "created_at":"2015-10-16T16:33:39.772776571Z",
  "created_by":"demouser@apcera.com",
  "fqn":"job::/sandbox/demouser::sampleapp1",
  "hard_scheduling_tags":{

  },
  "logs":[
    {
      "channel":"job.$JOB_UUID.stdout",
      "file":"/logs/stdout.*.log"
    },
    {
      "channel":"job.$JOB_UUID.stderr",
      "file":"/logs/stderr.*.log"
    }
  ],
  "name":"sampleapp1",
  "num_instances":1,
  "packages":[
    {
      "source":"user",
      "state":"unknown",
      "uuid":"f79efd2d-8a83-49d1-8d6d-9bcc61505df9"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"50df2e99-4c0e-46cf-836d-6b231da23e87"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"4da207db-95f5-48bd-8be3-0800f4a74983"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"1ac63726-be3f-4b58-80ca-1b5d9f3f3b9a"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"f79efd2d-8a83-49d1-8d6d-9bcc61505df9"
    }
  ],
  "ports":[
    {
      "number":8189,
      "optional":false
    }
  ],
  "processes":{
    "app":{
      "environment":{
        "DESIGNATION":"sampleapp1-bat"
      },
      "start_command":"/app/start.sh",
      "start_command_raw":[

      ],
      "start_command_timeout":30,
      "stop_command":"",
      "stop_command_raw":[

      ],
      "stop_timeout":5
    }
  },
  "resources":{
    "cpu":0,
    "disk":1073741824,
    "memory":268435456,
    "netmax":0,
    "network":5000000
  },
  "restart":{
    "maximum_attempts":0,
    "restart_mode":"always"
  },
  "rollout":{
    "errored_state_window":0,
    "flapping_minimum_restarts":3,
    "flapping_percent":0.5,
    "flapping_window":300,
    "force_stop_old_instances_after":120
  },
  "soft_scheduling_tags":{

  },
  "state":"ready",
  "tags":{
    "app":"sampleapp1"
  },
  "updated_at":"2015-10-16T16:33:41.668805765Z",
  "updated_by":"demouser@apcera.com",
  "uuid":"0a132bcc-5140-40ac-8291-f366ef0b8846",
  "version_id":5,
  "weight":0
}

Response

A 200 OK response indicates the update was successful. The response JSON object's ports field no longer contains a routes field (response abbreviated for readability):

HTTP/1.1 200 OK
Content-Type: application/json

{
  ...,
  "ports":[
    {
      "number":8189,
      "optional":false
    }
  ],
  "state":"ready",
  "tags":{
    "app":"sampleapp1"
  },
  "updated_at":"2015-10-16T16:33:42.434265761Z",
  "updated_by":"demouser@apcera.com",
  "uuid":"0a132bcc-5140-40ac-8291-f366ef0b8846",
  "version_id":6,
  "weight":0
}

Set an Environment Variable on a Job

You can set environment variables on a job object. The corresponding APC command looks like the following:

app update sampleapp1  \
    --env-set SAMPLEAPP2=http://sampleapp2.sample.demo.proveapcera.io/  \
    --env-set SAMPLEAPP3=http://sampleapp3.sample.demo.proveapcera.io/  \
    --env-set oops=extra

Job Lookup by FQN

First, get a current snapshot of the job to be updated by looking up the job with GET /jobs. For details see Job Lookup by FQN. You can skip this step if you have a current snapshot of the job.

Job Update

Update the job definition with environment variables named SAMPLEAPP2, SAMPLEAPP3, and oops environment variables.

Request

PUT /jobs/0a132bcc-5140-40ac-8291-f366ef0b8846 HTTP/1.1
Host: api.demo.proveapcera.io
Content-Type: application/json

{
  "created_at":"2015-10-16T16:33:39.772776571Z",
  "created_by":"demouser@apcera.com",
  "fqn":"job::/sandbox/demouser::sampleapp1",
  "hard_scheduling_tags":{

  },
  "logs":[
    {
      "channel":"job.$JOB_UUID.stdout",
      "file":"/logs/stdout.*.log"
    },
    {
      "channel":"job.$JOB_UUID.stderr",
      "file":"/logs/stderr.*.log"
    }
  ],
  "name":"sampleapp1",
  "num_instances":1,
  "packages":[
    {
      "source":"user",
      "state":"unknown",
      "uuid":"f79efd2d-8a83-49d1-8d6d-9bcc61505df9"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"50df2e99-4c0e-46cf-836d-6b231da23e87"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"4da207db-95f5-48bd-8be3-0800f4a74983"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"1ac63726-be3f-4b58-80ca-1b5d9f3f3b9a"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"f79efd2d-8a83-49d1-8d6d-9bcc61505df9"
    }
  ],
  "ports":[
    {
      "number":8189,
      "optional":false,
      "routes":[
        {
          "endpoint":"sampleapp1.sample.demo.proveapcera.io",
          "type":"http",
          "weight":0
        }
      ]
    }
  ],
  "processes":{
    "app":{
      "environment":{
        "DESIGNATION":"sampleapp1-bat",
        "SAMPLEAPP2":"http://sampleapp2.sample.demo.proveapcera.io/",
        "SAMPLEAPP3":"http://sampleapp3.sample.demo.proveapcera.io/",
        "oops":"extra"
      },
      "start_command":"/app/start.sh",
      "start_command_raw":[

      ],
      "start_command_timeout":30,
      "stop_command":"",
      "stop_command_raw":[

      ],
      "stop_timeout":5
    }
  },
  "resources":{
    "cpu":0,
    "disk":1073741824,
    "memory":268435456,
    "netmax":0,
    "network":5000000
  },
  "restart":{
    "maximum_attempts":0,
    "restart_mode":"always"
  },
  "rollout":{
    "errored_state_window":0,
    "flapping_minimum_restarts":3,
    "flapping_percent":0.5,
    "flapping_window":300,
    "force_stop_old_instances_after":120
  },
  "soft_scheduling_tags":{

  },
  "state":"ready",
  "tags":{
    "app":"sampleapp1"
  },
  "updated_at":"2015-10-16T16:33:42.964270586Z",
  "updated_by":"demouser@apcera.com",
  "uuid":"0a132bcc-5140-40ac-8291-f366ef0b8846",
  "version_id":7,
  "weight":0
}

Response

A successful response includes the updated job object without the bogus environment variable.

HTTP/1.1 200 OK
Content-Type: application/json

{
...,
  "processes":{
    "app":{
      "environment":{
        "DESIGNATION":"sampleapp1-bat",
        "SAMPLEAPP2":"http://sampleapp2.sample.demo.proveapcera.io/",
        "SAMPLEAPP3":"http://sampleapp3.sample.demo.proveapcera.io/",
        "oops":"extra"
      },
      "start_command":"/app/start.sh",
      "start_command_raw":[

      ],
      "start_command_timeout":30,
      "stop_command":"",
      "stop_command_raw":[

      ],
      "stop_timeout":5
    }
  },
  ...,
}

Delete an Environment Variable

You can delete an environment variable from a job similarly to how you add one. In a previous example, a bogus environment variable named oops was mistakenly added to the job. In this example we'll remove that environment variable.

The corresponding APC command to delete an environment variable looks like the following:

app update sampleapp1 --env-unset oops

Job Lookup by FQN

First, get a current snapshot of the job to be updated by looking up the job with GET /jobs. For details see Job Lookup by FQN. You can skip this step if you have a current snapshot of the job.

Job Update

To remove the bogus environment variable, remove the oops key from the job object's processes.app.environment object, as shown in the request.

Request

PUT /jobs/0a132bcc-5140-40ac-8291-f366ef0b8846 HTTP/1.1
Host: api.demo.proveapcera.io
Authorization: Bearer eyJ0eXAiOiI...
Content-Type: application/json

{
  "bindings":{
    "binding::/sandbox/demouser::egress_for_sampleapp1444c2c50":{
      "env_var":[
        "EGRESS_FOR_SAMPLEAPP1444C2C50_URI",
        "EGRESS_FOR_SAMPLEAPP1444C2C50_URI",
        "NETWORK_URI"
      ],
      "fqn":"binding::/sandbox/demouser::egress_for_sampleapp1444c2c50",
      "job_fqn":"job::/sandbox/demouser::sampleapp1",
      "name":"egress_for_sampleapp1444c2c50",
      "parameters":{
        "ipnet":"any",
        "portrange":"all",
        "protocol":"all"
      },
      "service_fqn":"service::/apcera::outside",
      "uuid":"d4748d3b-1c3f-4677-9644-1dceae8faf28"
    }
  },
  "created_at":"2015-10-16T16:33:39.772776571Z",
  "created_by":"demouser@apcera.com",
  "fqn":"job::/sandbox/demouser::sampleapp1",
  "hard_scheduling_tags":{

  },
  "logs":[
    {
      "channel":"job.$JOB_UUID.stdout",
      "file":"/logs/stdout.*.log"
    },
    {
      "channel":"job.$JOB_UUID.stderr",
      "file":"/logs/stderr.*.log"
    }
  ],
  "name":"sampleapp1",
  "num_instances":1,
  "packages":[
    {
      "source":"user",
      "state":"unknown",
      "uuid":"f79efd2d-8a83-49d1-8d6d-9bcc61505df9"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"50df2e99-4c0e-46cf-836d-6b231da23e87"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"4da207db-95f5-48bd-8be3-0800f4a74983"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"1ac63726-be3f-4b58-80ca-1b5d9f3f3b9a"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"f79efd2d-8a83-49d1-8d6d-9bcc61505df9"
    }
  ],
  "ports":[
    {
      "number":8189,
      "optional":false,
      "routes":[
        {
          "endpoint":"sampleapp1.sample.demo.proveapcera.io",
          "type":"http",
          "weight":0
        }
      ]
    }
  ],
  "processes":{
    "app":{
      "environment":{
        "DESIGNATION":"sampleapp1-bat",
        "SAMPLEAPP2":"http://sampleapp2.sample.demo.proveapcera.io/",
        "SAMPLEAPP3":"http://sampleapp3.sample.demo.proveapcera.io/"
      },
      "start_command":"/app/start.sh",
      "start_command_raw":[

      ],
      "start_command_timeout":30,
      "stop_command":"",
      "stop_command_raw":[

      ],
      "stop_timeout":5
    }
  },
  "resources":{
    "cpu":0,
    "disk":1073741824,
    "memory":268435456,
    "netmax":0,
    "network":5000000
  },
  "restart":{
    "maximum_attempts":0,
    "restart_mode":"always"
  },
  "rollout":{
    "errored_state_window":0,
    "flapping_minimum_restarts":3,
    "flapping_percent":0.5,
    "flapping_window":300,
    "force_stop_old_instances_after":120
  },
  "soft_scheduling_tags":{

  },
  "state":"ready",
  "tags":{
    "app":"sampleapp1"
  },
  "updated_at":"2015-10-16T16:33:43.330925025Z",
  "updated_by":"demouser@apcera.com",
  "uuid":"0a132bcc-5140-40ac-8291-f366ef0b8846",
  "version_id":8,
  "weight":0
}

Response

The response (abbreviated below for readability) shows the updated job object, with the oops field removed from the processes.app.environment object.

HTTP/1.1 200 OK
Content-Type: application/json

{
  ...,
  "processes":{
    "app":{
      "environment":{
        "DESIGNATION":"sampleapp1-bat",
        "SAMPLEAPP2":"http://sampleapp2.sample.demo.proveapcera.io/",
        "SAMPLEAPP3":"http://sampleapp3.sample.demo.proveapcera.io/"
      },
      "start_command":"/app/start.sh",
      "start_command_raw":[
      ],
      "start_command_timeout":30,
      "stop_command":"",
      "stop_command_raw":[
      ],
      "stop_timeout":5
    }
  },
  ...,
}

Job links provide one job (the source) with access to another (the target). A job link also sets an environment variable on the source job that contains connection information to the target job.

The corresponding APC command to link jobs:

app link sampleapp1  \
    --to sampleapp2  \
    --port 8289  \
    --name SAMPLEAPP3

Job Lookup by FQN

First, get a current snapshot of the (source) job to be updated by looking up the job with GET /jobs. For details see Job Lookup by FQN. You can skip this step if you have a current snapshot of the job.

In addition to the FQN for the source job, you also need the FQN of the target job. You can obtain it in the same manner as for the source job.

Now we can create a job link between the two jobs by calling the POST /bindings. You need to provide a simple name for the binding and also generate a UUID for the binding's FQN. In addition, you need the FQNs of the source and target jobs, which are specified as job_fqn and target_job_fqn in the request JSON.

Request

The request object's fqn field specifies the new binding's FQN, which has the form "fqn":"binding::/<namespace>::<uuid>. Your client app must generated the binding's UUID.

POST /bindings HTTP/1.1
Host: api.demo.proveapcera.io
Authorization: Bearer eyJ0eXAiOiI...
Content-Type: application/json

{
  "fqn":"binding::/sandbox/demouser::a9e674a7-1660-460f-8fca-ff373d066c0b",
  "job_fqn":"job::/sandbox/demouser::sampleapp1",
  "name":"SAMPLEAPP3",
  "target_job_fqn":"job::/sandbox/demouser::sampleapp2",
  "target_job_port":8289
}

Response

A successful response indicates that the jobs are now bound and also includes the newly created binding object.

HTTP/1.1 200 OK
Content-Type: application/json

{
  "fqn":"binding::/sandbox/demouser::a9e674a7-1660-460f-8fca-ff373d066c0b",
  "name":"SAMPLEAPP3",
  "target_job_fqn":"job::/sandbox/demouser::sampleapp2",
  "target_job_port":8289
}

Starting an Application

To start an application you update the corresponding job using the PUT /jobs/{uuid} endpoint. The job object in the PUT body must have its state field set to "started". The API server responds immediately to the request and does not wait until the job is actually running. To check if the job instance instance is running, poll the GET /jobs/{uuid}/instances endpoint to get a list of job instances and their current state.

The APC command to start an application is as follows:

app start sampleapp1

Step 1: Job Lookup by FQN

First, get a current snapshot of the job to be updated by looking up the job with GET /jobs. For details see Job Lookup by FQN. You can skip this step if you have a current snapshot of the job.

Step 2: Update Job State

To update the job's state you call the PUT /jobs/{uuid} endpoint, passing it the job object you just retrieved with its state property set to "started".

Request

PUT /jobs/0a132bcc-5140-40ac-8291-f366ef0b8846 HTTP/1.1
Host: api.demo.proveapcera.io
Authorization: Bearer eyJ0eXAiOiI...
Content-Type: application/json
{
  "bindings":{
    "binding::/sandbox/demouser::egress_for_sampleapp1444c2c50":{
      "env_var":[
        "EGRESS_FOR_SAMPLEAPP1444C2C50_URI",
        "EGRESS_FOR_SAMPLEAPP1444C2C50_URI",
        "NETWORK_URI"
      ],
      "fqn":"binding::/sandbox/demouser::egress_for_sampleapp1444c2c50",
      "job_fqn":"job::/sandbox/demouser::sampleapp1",
      "name":"egress_for_sampleapp1444c2c50",
      "parameters":{
        "ipnet":"any",
        "portrange":"all",
        "protocol":"all"
      },
      "service_fqn":"service::/apcera::outside",
      "uuid":"d4748d3b-1c3f-4677-9644-1dceae8faf28"
    }
  },
  "created_at":"2015-10-16T16:33:39.772776571Z",
  "created_by":"demouser@apcera.com",
  "fqn":"job::/sandbox/demouser::sampleapp1",
  "hard_scheduling_tags":{

  },
  "logs":[
    {
      "channel":"job.$JOB_UUID.stdout",
      "file":"/logs/stdout.*.log"
    },
    {
      "channel":"job.$JOB_UUID.stderr",
      "file":"/logs/stderr.*.log"
    }
  ],
  "name":"sampleapp1",
  "num_instances":1,
  "packages":[
    {
      "source":"user",
      "state":"unknown",
      "uuid":"f79efd2d-8a83-49d1-8d6d-9bcc61505df9"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"50df2e99-4c0e-46cf-836d-6b231da23e87"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"4da207db-95f5-48bd-8be3-0800f4a74983"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"1ac63726-be3f-4b58-80ca-1b5d9f3f3b9a"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"f79efd2d-8a83-49d1-8d6d-9bcc61505df9"
    }
  ],
  "ports":[
    {
      "number":8189,
      "optional":false,
      "routes":[
        {
          "endpoint":"sampleapp1.sample.demo.proveapcera.io",
          "type":"http",
          "weight":0
        }
      ]
    }
  ],
  "processes":{
    "app":{
      "environment":{
        "DESIGNATION":"sampleapp1-bat"
      },
      "start_command":"/app/start.sh",
      "start_command_raw":[],
      "start_command_timeout":30,
      "stop_command":"",
      "stop_command_raw":[

      ],
      "stop_timeout":5
    }
  },
  "resources":{
    "cpu":0,
    "disk":1073741824,
    "memory":268435456,
    "netmax":0,
    "network":5000000
  },
  "restart":{
    "maximum_attempts":0,
    "restart_mode":"always"
  },
  "rollout":{
    "errored_state_window":0,
    "flapping_minimum_restarts":3,
    "flapping_percent":0.5,
    "flapping_window":300,
    "force_stop_old_instances_after":120
  },
  "soft_scheduling_tags":{},
  "state":"started",
  "tags":{
    "app":"sampleapp1"
  },
  "updated_at":"2015-10-16T16:34:34.416845766Z",
  "updated_by":"demouser@apcera.com",
  "uuid":"0a132bcc-5140-40ac-8291-f366ef0b8846",
  "version_id":11,
  "weight":0
}

Response

If successful, the state field in the JSON response is set to "started" (JSON response abbreviated for readability).

HTTP/1.1 200 OK
Content-Type: application/json
{
  ...,
  "state":"started",
  "tags":{
    "app":"sampleapp1"
  },
  "updated_at":"2015-10-16T16:34:35.52336243Z",
  "updated_by":"demouser@apcera.com",
  "uuid":"0a132bcc-5140-40ac-8291-f366ef0b8846",
  "version_id":12,
  "weight":0
}

Check if Instances have Started

After you update the job's status to "started", you want to check that the job instance has started. To do this you poll GET /jobs/{uuid}/instances endpoint to get a list of job instances, and check the state field of each instance. Once a instance has started its state field is set to "RUNNING". You specify the UUID of the job (0a132bcc-5140-40ac-8291-f366ef0b8846 in this case) as the value of the {uuid} path parameter.

The corresponding APC command to check the state of a job's instances:

apc app instances app1

In this example, the job is configured to run a single instance. For jobs with multiple instances, you should poll GET /jobs/{uuid}/instances until all instances are running.

Request

GET /jobs/0a132bcc-5140-40ac-8291-f366ef0b8846/instances HTTP/1.1
Host: api.demo.proveapcera.io
Authorization: Bearer eyJ0eXAiOiI...

Response

In this case, we see that there is only one instance, in state "SETUP".

HTTP/1.1 200 OK
Content-Type: application/json
[
  {
    "announced_routes":false,
    "created_time":"2015-10-16T16:34:35.594155725Z",
    "exit_code":0,
    "exited":false,
    "failed":false,
    "host":"hcos-demo-1-e7b3dc80",
    "instance_manager_uuid":"47a9fcba-2152-4db3-804c-bdd8fa3ba375",
    "job_version_id":12,
    "state":"SETUP",
    "uuid":"8c5f2a7a-3cf5-4f67-9738-db807a0df215"
  }
]

After a few additional requests to GET /jobs/0a132bcc-5140-40ac-8291-f366ef0b8846/instances (not shown) the response indicates that the instance is now in state "RUNNING":

HTTP/1.1 200 OK
Content-Type: application/json
[
  {
    "announced_routes":true,
    "created_time":"2015-10-16T16:34:35.594155725Z",
    "exit_code":0,
    "exited":false,
    "failed":false,
    "host":"hcos-demo-1-e7b3dc80",
    "instance_manager_uuid":"47a9fcba-2152-4db3-804c-bdd8fa3ba375",
    "job_version_id":12,
    "state":"RUNNING",
    "uuid":"8c5f2a7a-3cf5-4f67-9738-db807a0df215"
  }
]

Get the Logs for an App

You can retrieve an app's logs with the GET /jobs/{uuid}/logs endpoint. You can specify the number of log items to return by passing a lines query parameter.

The corresponding APC command to get the last 1000 log lines from an app looks like the following:

app logs sampleapp1 --lines 1000 --no-tail

Job Lookup by FQN

To get a job's logs you need its UUID. You can obtain a job's UUID by looking up the job with GET /jobs. For details see Job Lookup by FQN. You can skip this step if you already know the job's UUID.

Request Logs for Job with Specified UUID

The following requests the last 1000 lines from the application's log.

Request

GET /jobs/0a132bcc-5140-40ac-8291-f366ef0b8846/logs?lines=1000 HTTP/1.1
Host: api.demo.proveapcera.io
Authorization: Bearer eyJ0eXAiOiI...

Response

HTTP/1.1 200 OK
Content-Type: application/octet-stream

1445013280721287112,job.0a132bcc-5140-40ac-8291-f366ef0b8846.system,Remote endpoint can not be located. for job "c852e026-8a28-46bc-9814-3113a5c99b75"
1445013280831000613,job.0a132bcc-5140-40ac-8291-f366ef0b8846.system,Remote endpoint can not be located. for job "db5cb0e0-6294-4fe5-9ae6-15767f7c1765"
1445013280905023545,job.0a132bcc-5140-40ac-8291-f366ef0b8846.stdout,using SAMPLEAPP2 http://169.254.0.2:10000/
1445013280905787111,job.0a132bcc-5140-40ac-8291-f366ef0b8846.stdout,using SAMPLEAPP3 http://169.254.0.2:10001/
...

Scale an Application to Five Instances

To change the number of instances created to run a job, you update the job object's num_instances field to the desired number of instances

The corresponding APC command to scale an app to five instances:

app update sampleapp1 --instances 5

Job Lookup by FQN

First, get a current snapshot of the job to be updated by looking up the job with GET /jobs. For details see Job Lookup by FQN. You can skip this step if you have a current snapshot of the job.

Job Update

Call PUT /jobs/{uuid} passing it the existing job object with its num_instances field updated to 5.

Request

PUT /jobs/0a132bcc-5140-40ac-8291-f366ef0b8846 HTTP/1.1
Host: api.demo.proveapcera.io
Authorization: Bearer eyJ0eXAiOiI...
Content-Type: application/json

{
  "num_instances":5,
  "bindings":{
    "binding::/sandbox/demouser::a9e674a7-1660-460f-8fca-ff373d066c0b":{
      "env_var":[
        "A9E674A71660460F8FCAFF373D066C0B_URI",
        "SAMPLEAPP3_URI"
      ],
      "fqn":"binding::/sandbox/demouser::a9e674a7-1660-460f-8fca-ff373d066c0b",
      "job_fqn":"job::/sandbox/demouser::sampleapp1",
      "name":"SAMPLEAPP3",
      "target_job_fqn":"job::/sandbox/demouser::sampleapp2",
      "target_job_port":8289,
      "target_job_uuid":"c852e026-8a28-46bc-9814-3113a5c99b75"
    },
  },
  "created_at":"2015-10-16T16:33:39.772776571Z",
  "created_by":"demouser@apcera.com",
  "fqn":"job::/sandbox/demouser::sampleapp1",
  "hard_scheduling_tags":{},
  "logs":[
    {
      "channel":"job.$JOB_UUID.stdout",
      "file":"/logs/stdout.*.log"
    },
    {
      "channel":"job.$JOB_UUID.stderr",
      "file":"/logs/stderr.*.log"
    }
  ],
  "name":"sampleapp1",
  "packages":[
    {
      "source":"user",
      "state":"unknown",
      "uuid":"f79efd2d-8a83-49d1-8d6d-9bcc61505df9"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"50df2e99-4c0e-46cf-836d-6b231da23e87"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"4da207db-95f5-48bd-8be3-0800f4a74983"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"1ac63726-be3f-4b58-80ca-1b5d9f3f3b9a"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"f79efd2d-8a83-49d1-8d6d-9bcc61505df9"
    }
  ],
  "ports":[
    {
      "number":8189,
      "optional":false,
      "routes":[
        {
          "endpoint":"sampleapp1.sample.demo.proveapcera.io",
          "type":"http",
          "weight":0
        }
      ]
    }
  ],
  "processes":{
    "app":{
      "environment":{
        "DESIGNATION":"sampleapp1-bat",
        "SAMPLEAPP2":"http://sampleapp2.sample.demo.proveapcera.io/",
        "SAMPLEAPP3":"http://sampleapp3.sample.demo.proveapcera.io/"
      },
      "start_command":"/app/start.sh",
      "start_command_raw":[

      ],
      "start_command_timeout":30,
      "stop_command":"",
      "stop_command_raw":[

      ],
      "stop_timeout":5
    }
  },
  "resources":{
    "cpu":0,
    "disk":1073741824,
    "memory":268435456,
    "netmax":0,
    "network":5000000
  },
  "restart":{
    "maximum_attempts":0,
    "restart_mode":"always"
  },
  "rollout":{
    "errored_state_window":0,
    "flapping_minimum_restarts":3,
    "flapping_percent":0.5,
    "flapping_window":300,
    "force_stop_old_instances_after":120
  },
  "soft_scheduling_tags":{

  },
  "state":"started",
  "tags":{
    "app":"sampleapp1"
  },
  "updated_at":"2015-10-16T16:34:35.52336243Z",
  "updated_by":"demouser@apcera.com",
  "uuid":"0a132bcc-5140-40ac-8291-f366ef0b8846",
  "version_id":12,
  "weight":0
}

Response

A successful response confirms that the number of instances was increased. (Response abbreviated for readability.)

HTTP/1.1 200 OK
Content-Type: application/json

{
  ...,
  "name":"sampleapp1",
  "num_instances":5,
  "state":"started",
  "tags":{
    "app":"sampleapp1"
  },
  "updated_at":"2015-10-16T16:34:54.198888502Z",
  "updated_by":"demouser@apcera.com",
  "uuid":"0a132bcc-5140-40ac-8291-f366ef0b8846",
  "version_id":13,
  "weight":0
}

Stop an Application

You stop an application by calling PUT /jobs/{uuid}, passing it a job object with its state field set to *"stopped".

The corresponding APC command to stop an application:

app stop sampleapp1

Job Lookup by FQN

First, get a current snapshot of the job to be updated by looking up the job with GET /jobs. For details see Job Lookup by FQN. You can skip this step if you have a current snapshot of the job. You also need the job's UUID for the update call.

Job Update

The request JSON object's state field is set to "stopped".

Request

PUT /jobs/0a132bcc-5140-40ac-8291-f366ef0b8846 HTTP/1.1
Host: api.demo.proveapcera.io
Authorization: Bearer eyJ0eXAiOiI...
Content-Type: application/json
{
  "state":"stopped",
  "bindings":{
    "binding::/sandbox/demouser::a9e674a7-1660-460f-8fca-ff373d066c0b":{
      "env_var":[
        "A9E674A71660460F8FCAFF373D066C0B_URI",
        "SAMPLEAPP3_URI"
      ],
      "fqn":"binding::/sandbox/demouser::a9e674a7-1660-460f-8fca-ff373d066c0b",
      "job_fqn":"job::/sandbox/demouser::sampleapp1",
      "name":"SAMPLEAPP3",
      "target_job_fqn":"job::/sandbox/demouser::sampleapp2",
      "target_job_port":8289,
      "target_job_uuid":"c852e026-8a28-46bc-9814-3113a5c99b75"
    },
  },
  "created_at":"2015-10-16T16:33:39.772776571Z",
  "created_by":"demouser@apcera.com",
  "fqn":"job::/sandbox/demouser::sampleapp1",
  "hard_scheduling_tags":{

  },
  "logs":[
    {
      "channel":"job.$JOB_UUID.stdout",
      "file":"/logs/stdout.*.log"
    },
    {
      "channel":"job.$JOB_UUID.stderr",
      "file":"/logs/stderr.*.log"
    }
  ],
  "name":"sampleapp1",
  "num_instances":5,
  "packages":[
    {
      "source":"user",
      "state":"unknown",
      "uuid":"f79efd2d-8a83-49d1-8d6d-9bcc61505df9"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"50df2e99-4c0e-46cf-836d-6b231da23e87"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"4da207db-95f5-48bd-8be3-0800f4a74983"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"1ac63726-be3f-4b58-80ca-1b5d9f3f3b9a"
    },
    {
      "source":"system",
      "state":"ready",
      "uuid":"f79efd2d-8a83-49d1-8d6d-9bcc61505df9"
    }
  ],
  "ports":[
    {
      "number":8189,
      "optional":false,
      "routes":[
        {
          "endpoint":"sampleapp1.sample.demo.proveapcera.io",
          "type":"http",
          "weight":0
        }
      ]
    }
  ],
  "processes":{
    "app":{
      "environment":{
        "DESIGNATION":"sampleapp1-bat",
        "SAMPLEAPP2":"http://sampleapp2.sample.demo.proveapcera.io/",
        "SAMPLEAPP3":"http://sampleapp3.sample.demo.proveapcera.io/"
      },
      "start_command":"/app/start.sh",
      "start_command_raw":[

      ],
      "start_command_timeout":30,
      "stop_command":"",
      "stop_command_raw":[

      ],
      "stop_timeout":5
    }
  },
  "resources":{
    "cpu":0,
    "disk":1073741824,
    "memory":268435456,
    "netmax":0,
    "network":5000000
  },
  "restart":{
    "maximum_attempts":0,
    "restart_mode":"always"
  },
  "rollout":{
    "errored_state_window":0,
    "flapping_minimum_restarts":3,
    "flapping_percent":0.5,
    "flapping_window":300,
    "force_stop_old_instances_after":120
  },
  "soft_scheduling_tags":{},
  "tags":{
    "app":"sampleapp1"
  },
  "updated_at":"2015-10-16T16:34:54.198888502Z",
  "updated_by":"demouser@apcera.com",
  "uuid":"0a132bcc-5140-40ac-8291-f366ef0b8846",
  "version_id":13,
  "weight":0
}

Response

A 200 OK response indicates the job status was updated. Response abbreviated for readability.

HTTP/1.1 200 OK
Content-Type: application/json
{
  ...,
  "state":"stopped",
  "tags":{
    "app":"sampleapp1"
  },
  "updated_at":"2015-10-16T16:34:58.279160049Z",
  "updated_by":"demouser@apcera.com",
  "uuid":"0a132bcc-5140-40ac-8291-f366ef0b8846",
  "version_id":14,
  "weight":0
}

Delete an Application

To delete an application you first call DELETE /v1/jobs/{uuid} to delete the job. You also need to delete the application package that was linked to the job when it was created (see Link Package to the Job).

The corresponding APC command to delete an application:

app delete sampleapp1

Step 1: Job Lookup by FQN

To delete a job you need it's UUID. First, get a current snapshot of the job to be deleted by looking up the job with GET /jobs. For details see Job Lookup by FQN. You can skip this step if you have a current snapshot of the job.

Step 2: Delete the Job by UUID

Once you have the job's UUID, you send a request to DELETE /v1/jobs/{uuid}.

Request

DELETE /v1/jobs/0a132bcc-5140-40ac-8291-f366ef0b8846 HTTP/1.1
Host: api.demo.proveapcera.io
Authorization: Bearer eyJ0eXAiOiI...

Response

A successful response indicates that the job has been successfully deleted.

HTTP/1.1 200 OK
Content-Length: 4
Content-Type: application/json

null

Step 3: Package Lookup for Linked Jobs

To determine what packages are linked to the deleted job, call GET /packages API, passing it the following query string parameters:

  • tag=linked-job
  • The UUID of the deleted job.

Request

GET /packages?tag=linked-job,0a132bcc-5140-40ac-8291-f366ef0b8846 HTTP/1.1
Host: api.demo.proveapcera.io
Authorization: Bearer eyJ0eXAiOiI...

Response

The response contains the application package that was linked to the job. Save the value of the response package's uuid field so you can delete the package in the next step.

HTTP/1.1 200 OK
Content-Type: application/json
[
  {
    "created_at":"2015-10-16T16:33:16.725837128Z",
    "created_by":"demouser@apcera.com",
    "dependencies":[
      {
        "name":"build-essential",
        "type":"package"
      },
      {
        "name":"java",
        "type":"runtime"
      }
    ],
    "environment":{
      "START_COMMAND":"java $JAVA_OPTS -jar sampleapp1-0.1-SNAPSHOT.jar $JAVA_FLAGS",
      "START_PATH":"/app"
    },
    "fqn":"package::/sandbox/demouser::sampleapp1",
    "name":"sampleapp1",
    "resource":{
      "length":1890603,
      "sha1":"de13a9d38bbc6cc5fb6687bee63603e9976dfaa4",
      "uuid":"3bce2c01-b763-4817-9c6e-84cece3adafc"
    },
    "stager_job_fqns":[
      "job::/apcera/stagers::java"
    ],
    "staging_pipeline_fqn":"stagpipe::/apcera::java",
    "state":"ready",
    "tags":{
      "linked-job":"0a132bcc-5140-40ac-8291-f366ef0b8846"
    },
    "updated_at":"2015-10-16T16:33:39.933371793Z",
    "updated_by":"demouser@apcera.com",
    "uuid":"f79efd2d-8a83-49d1-8d6d-9bcc61505df9",
    "version_id":5
  }
]

Step 4: Delete the Package by UUID

Now that you've retrieved the linked package and obtained its UUID, you can call DELETE /v1/packages/{uuid} to delete it.

Request

DELETE /v1/packages/f79efd2d-8a83-49d1-8d6d-9bcc61505df9 HTTP/1.1
Host: api.demo.proveapcera.io
Authorization: Bearer eyJ0eXAiOiI...

Response

A successful response indicates that the package was successfully deleted.

HTTP/1.1 200 OK
Content-Length: 4
Content-Type: application/json

null

Update the Number of Job Instances

To update the number of job instances, you call the PUT /jobs/{uuid} passing it a Job object with its num_instances set to the desired number of instances.

To update a job you first need to get the job's current JSON description and UUID. If you know the job's fully-qualified name (namespace and local name), you can perform a job lookup by its FQN (see Job Lookup by FQN). Once you have the job's current description and UUID, you can call the PUT /jobs/{uuid} endpoint with the updated Job definition.

You must include the entire job object in the call to PUT /jobs/{uuid}, not just the fields that you want to change.

Step 1: Job Lookup by FQN

First, get a current snapshot of the job to be updated by looking up the job with the GET /jobs endpoint. For details see Job Lookup by FQN. You can skip this step if you have a current snapshot of the job.

Step 2: Update Job with new number of instances

Once you have a current JSON snapshot of the job object, you update the object's num_instances field to specify the desired number of instances, and pass it in the body of a call to PUT /jobs/{uuid}. For instance, in the following request the number of instances is set to 10.

Request

PUT /jobs/d198cde5-1c8f-46d6-9f72-fb820ddce04e HTTP/1.1
Host: api.try.apcera-platform.io
Authorization: Bearer ...
Content-Type: application/json

{
    "uuid": "d198cde5-1c8f-46d6-9f72-fb820ddce04e",
    "num_instances": 10,
    "updated_by": "",
    "created_by": "stagehand@apcera.me",
    "updated_at": "2015-12-15T22:25:03.86189331Z",
    "created_at": "2015-12-15T22:25:03.86189331Z",
    "ports": [
        ...
    ],
    "logs": [
        ...
    ],
    "name": "continuum-guide",
    "fqn": "job::/apcera::continuum-guide",
    "packages": [
        ...
    ],
    "processes": {
        ...
    },
    "resources": {
        ...
    },
    "rollout": {
        ...
    },
    "restart": {
        ...
    },
    "hard_scheduling_tags": {},
    "soft_scheduling_tags": {},
    "state": "started",
    "tags": {
        ...
    },
    "version_id": 1,
}

Response

A successful response echoes back the updated job object.

{
    "created_at": "2015-12-15T22:25:03.86189331Z",
    "created_by": "stagehand@apcera.me",
    "fqn": "job::/apcera::continuum-guide",
    "hard_scheduling_tags": {},
    "logs": [
        ...
    ],
    "name": "continuum-guide",
    "num_instances": 10,
    "packages": [
        ...
    ],
    "ports": [
        ...
    ],
    "processes": {
        ...
    },
    "resources": {
        ...
    },
    "restart": {
        ...
    },
    "rollout": {
        ...
    },
    "soft_scheduling_tags": {},
    "state": "started",
    "tags": {
        ...
    },
    "updated_at": "2015-12-15T23:58:14.050979715Z",
    "updated_by": "admin@apcera.me",
    "uuid": "d198cde5-1c8f-46d6-9f72-fb820ddce04e",
    "version_id": 3
}

Create an App from a Docker image

The POST /jobs/docker endpoint creates a application from a Docker image. You pass the endpoint a CreateDockerJobRequest object in the POST body that specifies the URL of the Docker image, the FQN of the job to create, resources to allocate, and other attributes. A successful response contains the URI of the Task object used to track the progress and status of the app creation process (GET /tasks/{uuid}). A Task object is composed of one or more TaskEvent objects, each of which describes a step in the task process.

Clients can use this Task URI to track progress in either of the following ways:

  • Use the Task URI to connect a WebSocket client to a WebSocket server running on the Apcera cluster. Events are streamed in real-time to the client. See Stream task event over WebSockets.
  • Repeatedly poll the URI over HTTP until the task is complete. See Poll for task status.

Also see the Node.js api-docker-example on GitHub for a complete example.

Send request to create job from Docker image

The POST body is a CreateDockerJobRequest object that specifies the URL of the Docker image from which the app should be created, the FQN of the job to create, resources to allocate to instances of the job, and other properties.

Request
POST /jobs/docker HTTP/1.1
Host: api.try.apcera.net
Authorization: Bearer eyJ0eXAiOiI...
Content-Type: application/json

{
  "allow_egress":true,
  "exposed_ports":[
    80
  ],
  "image_url":"https://registry-1.docker.io/deis/helloworld:latest",
  "job_fqn":"job::/sandbox/user::hellodocker",
  "resources":{
    "cpu":0,
    "disk":1073741824,
    "memory":268435456,
    "netmax":0,
    "network":5000000
  },
  "restart_config":{
    "maximum_attempts":0,
    "restart_mode":"no"
  },
  "routes":{
    "http://hellodocker.sandbox.user.try.apcera.net":80
  },
  "start":true
}
Response

The response payload is a JSON object a location field that contains the URL of the Task object created on the server to monitor the app creation progress.

HTTP/1.1 200 OK
Content-Type: application/json

{
    "location":"http://api.try.apcera.net/v1/tasks/b68d89a0-5b78-462b-8ec6-84edeb5ac02a"
}

To track the progress of the operation you can either poll this URL over HTTP, or use it to connect a WebSocket client to the cluster and stream events in real-time. See Tracking progress of asynchronous API calls for details and example code.

Tracking progress of asynchronous API calls

Some API operations, such as deploying a multi-resource manifest or creating an app from a Docker image, operate asynchronously. These APIs respond with the URI (GET /tasks/{uuid}) you use to track the progress of the operation. The following is representative of such a response:

HTTP/1.1 200 OK
Content-Type: application/json

{
    "location":"http://api.try.apcera.net/v1/tasks/b68d89a0-5b78-462b-8ec6-84edeb5ac02a"
}

You use the location URL returned in the response to track the operation's progress. There are two ways to do this:

  • Stream task events using WebSockets – You can use the location URL to connect a WebSocket client to the Apcera cluster and have TaskEvent objects streamed to your API client in real-time. This is a good choice if you want to display progress to the user as they occur (as APC and Web Console do, for example).
  • Poll for task events using HTTP – You can also use HTTP to repeatedly poll the location URL over HTTP until the task is complete. With this approach each response is a Task rather than a stream of Task objects, as with the WebSocket approach.

Stream task events using WebSockets

To stream task events over WebSockets you use the location field as the connection endpoint for a WebSocket client. For example, the following JavaScript function (from the api-manifest-example) creates a new WebSocket connection from a taskLocation variable that contains the value of the location field returned by a previous API call (not shown).

function streamTaskEvents(taskLocation) {
    // Create new WebSocket from taskLocation URL
    var ws = new WebSocket(taskLocation);

    // Each websocket message is a TaskEvent object (http://docs.apcera.com/api/apcera-api-endpoints/#definitions-TaskEvent)
    ws.on('message', function(data, flags) {
        var taskEvent = JSON.parse(data);
        var eventType = taskEvent.task_event_type;
        if (eventType == "error") {
            console.log("An error occurred deploying the manifest: " + taskEvent.payload.error);
            ws.close();
            return;
        }
        if (eventType == "eos") {
            ws.close();
            return;
        }
        var thread = taskEvent.thread;
        var stage = taskEvent.stage;
        var subtask = taskEvent.subtask;
        console.log(thread, "-", stage, "-", subtask.name);
    });
}

Note: There is a chance that the Apcera cluster will publish the same WebSocket message twice to your API client. It's recommended that you check each message for uniqueness by storing a hash of each message string and ignoring it if it's a duplicate.

Once a connection has been established the WebSocket client's on('message') event handler is invoked for each incoming WebSocket message. Each message is a JSON-encoded TaskEvent that contains information about the task, including the event type (taskEvent.task_event_type), current task stage (taskEvent.stage) and subtask (taskEvent.subtask).

If the event type is "errored" then the error description (taskEvent.payload.error) is displayed to the user and the method returns. If no error is detected, it then checks if the event type is "eos" (end-of-stream) indicating the last message has been received. In this case, since we already checked for an error condition, it can inferred that the task has completed successfully. If the event type is neither "errored" or "eos" then the task event's stage and subtask are displayed to the user.

See the Node.js api-manifest-example on GitHub for the complete source code for this example.

Poll for task events over HTTP

To track progress of an asynchronous operation your API client can poll the /v1/tasks/{uuid} endpoint repeatedly until the operation is complete. Each HTTP response to this URL is a Task object that contains one or more TaskEvent objects.

The state field of each Task object indicates the task's current state and can be one of the following values: "running" (task is still running), "stopped" (task has stopped), or "complete" (task is complete). Each Task object also has an errored field that indicates if an error has occurred ("errored" or "unerrored"). The Task's errored field is independent of its state, so it's important to check both the state and errrored field of the Task to determine the current status.

For example, the following JavaScript code (from the Node.js api-manifest-example) defines a recursive function that polls the taskLocation URL returned by a previous API call (not shown).

var Client = require('node-rest-client').Client;
var client = new Client();

// Poll for task status over HTTP recursively
function pollTaskStatus(taskLocation) {
    client.get(taskLocation, function(data, response) {
        switch (response.statusCode) {
            case 200:
                // Response is a Task object (http://docs.apcera.com/api/apcera-api-endpoints/#definitions-Task)
                var task = data;
                var state = task.state;
                var errored = task.errored;
                if (state == "running" && errored == "unerrored") {
                    console.log("Task still running, polling again...")
                    pollTaskStatus(taskLocation);
                }
                if (state == "complete" && errored == "unerrored") {
                    console.log("Manifest deployed successfully.")
                    return;
                }
                if (state == "stopped" && errored == "errored") {
                    // Extract value from "error" JSON object (taskEvent.payload.error)
                    var errorMessage = getValues(task,'error')[0];
                    console.log("An error occurred deploying the manifest: ", errorMessage);
                    return;
                }
                break;
            case 403:
                // "Forbidden" error, usually a policy permissions error.
                console.log("403 error:", data.message);
                break;
        }

    }).on('error', function (err) {
        console.log('Error contacting API endpoint: ', err);
    });
}

If the task is still running and unerrored then the function calls itself again:

if (state == "running" && errored == "unerrored") {
    console.log("Task still running, polling again...")
    pollTaskStatus(taskLocation);
}

If the task is complete and unerrored then a success message is displayed to the user and the function returns. Lastly, if the task is stopped and errored, the error description is extracted from the response's taskEvent.payload.error field:

if (state == "stopped" && errored == "errored") {
    // Extract value from "error" JSON object (taskEvent.payload.error)
    var errorMessage = getValues(task,'error')[0];
    console.log("An error occurred deploying the manifest: ", errorMessage);
    return;
}

See the Node.js api-manifest-example on GitHub for the complete source code for this example.

Job Lookup by FQN

A common API task is looking up a job by its FQN (fully-qualified name) using the GET /jobs endpoint. This API returns a JSON representation of the job that you can modify, for example, and send in a request to PUT /jobs. A common use of this endpoint is to see if a job exists with the specified FQN before trying to create a new job with the same FQN.

The endpoint returns an array of jobs that matched the specified FQN. An empty array indicates that no jobs matched the FQN.

Request

The following requests a job that doesn't exist.

GET /jobs?fqn=job::/sandbox/demouser::somejob HTTP/1.1
Host: api.demo.proveapcera.io
Authorization: Bearer eyJ0eXAiOiI...

Response

The response is an empty array, so you can surmise that the job doesn't exist.

HTTP/1.1 200 OK
Content-Length: 2
Content-Type: application/json

[]

Request

In this case, the job exists so the response array contains a single item.

GET /jobs?fqn=job::/sandbox/demouser::sampleapp1 HTTP/1.1
Host: api.demo.proveapcera.io
Authorization: Bearer eyJ0eXAiOiI...

Response

Response JSON abbreviated for readability.

HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    ...
      "state":"ready",
      "tags":{
        "app":"sampleapp1"
      },
      "updated_at":"2015-10-16T16:33:40.235639764Z",
      "updated_by":"demouser@apcera.com",
      "uuid":"0a132bcc-5140-40ac-8291-f366ef0b8846",
      "version_id":2,
      "weight":0
    }
]

Creating and updating policy documents

To create or update policy documents on your cluster you use the PUT /policy endpoint. You pass this endpoint an array of PolicyDocumentRequest objects, each of which represents a policy document to update or create.

To update an existing policy document, you must first obtain the document's current version number by calling GET /policy/{name}, and extracting the version field from the PolicyDocument response object. You use this value in a subsequent call to PUT /policy to update the policy document. See Updating existing policy documents.

To create a new policy document you must either omit the for_version field, or set its value to 0. See Creating new policy documents.

Note: To make it easier for others to read and edit policy you create using the API, it's recommended that you include newline (\n) characters at logical places in the policy text.

Updating existing policy documents

To update an existing policy document, first obtain the current version number of the target document by calling GET /policy/{name}, where {name} is the name of the policy document to update. This endpoint returns a PolicyDocument object that contains the policy document's text and current version number, for example:

GET /policy/quotas HTTP/1.1

{
  "name": "quotas",
  "text": "on quota::/ {\n  { max.package.size 4GB }\n}\n\n// on quota::/apcera::continuum-guide {\n//   { max.instances 3 }\n// }\n\non quota::/ {\n  if (fqnMatch == \"job::/apcera::continuum-guide\") {\n  \tmax.instances 3\n  }\n}",
  "version": 9,
  "updated_at": "0001-01-01T00:00:00Z"
}

The version field contains the policy document's current version (in this case "9"). To update this policy document, create a PolicyDocumentRequest and set its text property to the modified policy text, and set its for_version property to the version obtained from the previous API call. Add the PolicyDocumentRequest to the docs array in the PUT /policy request body:

PUT /policy HTTP/1.1

{
    "docs": [
        {
          "name": "quotas",
          "text": "// THIS IS A NEW COMMENT\n\non quota::/ {\n  { max.package.size 4GB }\n}\n\n// on quota::/apcera::continuum-guide {\n//   { max.instances 3 }\n// }\n\non quota::/ {\n  if (fqnMatch == \"job::/apcera::continuum-guide\") {\n  \tmax.instances 3\n  }\n}",
          "for_version": 9
        }
    ]
}

If the value specified by for_version doesn't match the current document version then the request will fail with a HTTP 409 Conflict error and the message:

Error updating policy: policy version mismatch while updating '<document>': update is for version <X>, current version is <Y>

Creating new policy documents

To create a new policy document, omit the for_version field from the corresponding PolicyDocumentRequest object (or set its value to 0). You can create and update new policy documents in the same request. For example, the following creates a new policy document named newdoc and updates an existing document named olddoc that's currently at version 6.

PUT /policy HTTP/1.1

{
    "docs": [
        {
          "name": "newdoc",
          "text": "<continuum-policy description=\"New policy doc\">\n\njob::/sandbox/user {\n\t{ role admin } \n}"
        },
        {
          "name": "olddoc",
          "text": "<continuum-policy description=\"Existing policy doc\">\n\njob::/sandbox/user {\n\t{ role dev } \n}"
          "for_version": 6
        }

    ]
}