Integrating Other Tools into Your CI/CD Pipeline

  • 27 September 2021
  • 0 replies
  • 25 views

This article explains how to integrate and use a CI/CD that is not supported out-of-the-box by Torque. When integrating CI/CD tools with Torque for continuous testing, a basic requirement is to develop the Start Sandbox and End Sandbox stages.

NOTE:  Depending on your CI tool, it might be called differently like task, action, or step. For consistency, we will reference to these building blocks as “stages” in this article.

High-level overview of a CI pipeline

A high-level view of a CI pipeline might look like this:

  1. The Build stage compiles the code and publishes the artifact to an Artifact Repository accessible by Torque. Popular artifact repositories that are commonly used with Torque are S3 buckets, Azure Storage Accounts or JFrog Artifactory.
  2. The Start Sandbox stage creates a sandbox environment from a Torque blueprint and waits until it’s active. As a best practice when building a CI pipeline, we recommend testing the same artifacts throughout the entire pipeline. Typically, the Start Sandbox stage gets a reference to the artifacts so they can be pulled into the sandbox during the sandbox’s provisioning and used to install the application. When the sandbox’s provisioning completes, the Start Sandbox stage returns a sandbox details object containing the information for the endpoints that we want to test.
  3. Next, the pipeline runs the tests against the sandbox’s endpoints.
  4. When the test run completes, regardless of the completion status of the tests, we want to end the sandbox to clean up the environment and remove any provisioned cloud resources. The End Sandbox stage should get as an input the Sandbox ID that was returned by the Start Sandbox stage.

Implementation code samples

Here are some code examples that implement the Start Sandbox and End Sandbox stages. We will be using node.js for the code examples.

Authentication

To use Torque via REST API, you need to generate a Bearer token. Torque uses Bearer tokens for authentication. Our code needs to send this token in the Authorization header when making requests to protected resources:

Authorization: Bearer <token>

There are several ways to generate the token:

Via Web portal:

  1. Go to app.qtorque.io and login to your account.
  2. Open the Settings page and select Integrations.
  3. Select Connect under any of the “CI CD Pipeline Tools” icons.
  4. Click the New Token button to get an auto-generated token value.

Via REST API:
The following endpoint generates a long-lived token using rest API:
https://qtorque.io/swagger/index.html#/Access%20Tokens/post_api_token_longtoken

Here’s an example bash script to generate such a token:

TORQUE_EMAIL=<email address>
TORQUE_PASSWORD=<password>
TORQUE_ACCOUNT=<torque account name>

SHORT_TOKEN=$(curl -s -X "POST" "https://qtorque.io/api/accounts/$TORQUE_ACCOUNT/login" -H "accept: text/plain" -H "Content-Type: application/json-patch+json" -d "{\"email\": \"$TORQUE_EMAIL\", \"password\": \"$TORQUE_PASSWORD\"}" | jq -r .access_token)

curl -s -X "POST" "https://qtorque.io/api/token/longtoken" -H "accept: text/plain" -H "Authorization: Bearer $SHORT_TOKEN"-d "" | jq -r .access_token

NOTE: This method is not supported with Torque accounts that have SSO enabled.

Start Sandbox Stage

We typically need to use two API methods to implement this stage. First, we call a POST request to create a sandbox as this will give us the sandbox ID. And then, we need a GET request to get the sandbox details using the sandbox ID in a loop until the sandbox status changes to indicate that the sandbox is ready.

The Start Sandbox stage usually asks for the following inputs from the DevOps engineer creating the pipeline:

  • Torque space name: The space where the blueprint is located
  • Blueprint name: The name of the blueprint to be used for creating this sandbox
  • Sandbox name: This input usually contains some runtime variables like Build ID for example
  • Inputs: If the blueprint has inputs, then all inputs must be passed in the API Post request as key-value pairs.
  • Artifacts: If the blueprint has applications with artifacts, then all the artifacts must be passed in the API Post request as key-value pairs in the format:
    ApplicationName = artifact path
  • Timeout: Gives the user the option to control the timeout for the Start Sandbox stage to wait until the sandbox is ready

  • Duration: This is the duration for the sandbox. When the specified duration ends, the sandbox will automatically end. The duration value should give the tests that use this sandbox enough time to be executed.

The Start Sandbox method might look something like this with a simple implementation:

async function StartSandbox (space, sandbox_name, blueprint_name, inputs, artifacts) {
try {
// set authorization header
const config = {
headers: { 'Authorization': `Bearer ${torqueToken}` }
};
// create data
const data = {
sandbox_name,
blueprint_name,
inputs,
artifacts,
automation: true,
duration: 'PT2H' // ISO 8601 date format - 2 hours
};
// create new sandbox
const url = `${torquApiBaseUrl}/spaces/${space}/sandbox`;
const response = await axios.post(url, data, config);
// return sandbox id
const sandboxId = response.data['id'];
console.log(`New sandbox created. Sandbox Id: ${sandboxId}`);
return sandboxId;
} catch (error) {
console.error(JSON.stringify(error));
}
}

Let’s dive into the code.

  1. First, we create the 'Authorization' header with the token.
  2. Next, we create the post data using the provided parameters. For the full list of supported options for the create sandbox API method,  see https://qtorque.io/swagger/index.html#/Sandbox%20Environments/post_api_spaces__space_name__sandbox
    In this example, we used axios as the http client. After we prepare the headers and post data, we send the request to Torque to create a new sandbox. If the sandbox is created successfully, we will get a response with the sandbox ID.
  3. Once the sandbox is created, the API that created the sandbox returns the sandbox id, without waiting for the sandbox to fully deploy.
  4. However, we will need our Start Sandbox stage to block and wait until the sandbox is fully active. To achieve this, we need to add some logic that gets the sandbox status in a loop until the sandbox is active. Here’s an example implementation of a function that gets the sandbox id as an input and waits until it’s either Active or has an error:
async function WaitUntilActive (space, sandboxId, timeoutInMinutes) {
try {
// set authorization header
const config = {
headers: { 'Authorization': `Bearer ${process.env.TORQUE_TOKEN}` }
};
// build url for get sandbox details
const url = `${constants.TORQUE_API_BASE_URL}/spaces/${space}/sandbox/${sandboxId}`;

let deployingSandbox = true;
const startTime = Date.now();
const timeoutInMs = timeoutInMinutes * 60000; // convert minutes to milliseconds
const sleepInterval = 10000; // 10 seconds

while (deployingSandbox && (Date.now() - startTime) < timeoutInMs) {
// get sandbox details
const response = await axios.get(url, config);
// get sandbox status
const sandboxStatus = response.data['sandbox_status'];
// check if sandbox status is Active has an Error or Ended
if (constants.FINAL_SB_STATUSES.includes(sandboxStatus)) {
console.log(`Sandbox deployment finished with status ${sandboxStatus}`);
deployingSandbox = false;
break;
}
// sleep for 10 seconds between sandbox status pulls
console.log(`Waiting for sandbox to be Active. Sandbox status is ${sandboxStatus}. Sleeping for 10 seconds...`);
await utils.sleep(sleepInterval);
}
if (deployingSandbox) {
console.error(`ERROR: reached ${timeoutInMinutes} minutes timeout but sandbox is still deploying :(`)
}
} catch (error) {
console.error(JSON.stringify(error));
}
}

Let’s break down the code.

  1. First, we set up the Bearer token for authentication and build the URL to get the sandbox details. We‘ll use the following API to get the sandbox detailshttps://qtorque.io/swagger/index.html#/Sandbox%20Environments/get_api_spaces__space_name__sandbox
    Instead of having an endless loop that pulls the sandbox status, a best practice is to add timer logic to the loop to ensure that we don’t end up with an infinite loop. And the function accepts the timeout value in minutes as an input.
  2. Inside the loop, we get the sandbox details and retrieve the sandbox status. We will exit the loop if the sandbox status is any of the following:
    • Active: Sandbox deployment finished successfully
    • ActiveWithError: Sandbox deployment finished but with an error
    • Ended: The sandbox ended successfully
    • EndedWithError: The sandbox ended but there is an error
    • Ending: The sandbox is in the ending process (teardown)
    • NotFound: This status might appear if someone deleted the cloud infrastructure that the sandbox created directly from the cloud provider console and not from Torque.
  3. And finally, the process sleeps for 10 seconds between status pulls.

A possible enhancement for the Start Sandbox stage is to extract the sandbox endpoint from the sandbox details object (once the sandbox is Active) and publish it as a stage output to be used for testing in future stages of the CI. If this is not implemented as part of the Start Sandbox stage, it will usually need to be implemented in a later stage when a test needs to run against the sandbox.

Here’s an example for extracting the 1st sandbox endpoint (there might be multiple endpoints exposed) from the sandbox details:

const sandboxEndpoint = response.data.applications[0].shortcuts[0];

End Sandbox Stage

Once the sandbox deployment completes and the sandbox is Active, we need to publish the sandbox id as an output of the Start Sandbox stage. The sandbox id is required for the End Sandbox stage. Furthermore, to pass it dynamically for each run of the pipeline, the sandbox id input for the End Sandbox stage is usually referenced to the sandbox id output of the Start Sandbox stage.

Here’s an example of a simple implementation for the End Sandbox stage:

async function EndSandbox (space, sandboxId) {
try {
// set authorization header
const config = {
headers: { 'Authorization': `Bearer ${process.env.TORQUE_TOKEN}` }
};
// build url for get sandbox details
const url = `${constants.TORQUE_API_BASE_URL}/spaces/${space}/sandbox/${sandboxId}`;
// end sandbox
await axios.delete(url, config);
console.log(`End request sent for sandbox ${sandboxId}`);
} catch (error) {
console.error(JSON.stringify(error));
}
}

Let’s review what the above code does:

The end sandbox only needs two inputs: the space name and the sandbox id. We first set the Bearer token authorization, then build the URL of the API endpoint and make an http DELETE request. If the delete request passes successfully, we know that the ending process has started.

The above example uses an optimistic End Sandbox approach. The delete request to end the sandbox initiates the ending process. The sandbox status will be “Ending”, and the function will exit without making sure that the sandbox has fully ended.

Depending on the complexity of the blueprint, some sandboxes might take several minutes to end. A possible enhancement for a more robust implementation might include a wait loop until the sandbox is in status “Ended”.

The sandbox might also get an error during ending and be in a “EndedWithError” status, which means that Torque didn’t manage to clean up all the sandbox’s cloud infrastructure from the cloud and it should be manually cleaned-up. You might also want this to be reflected in the CI pipeline.

Summary

In this article, we learned how to use Torque REST API to start and end sandboxes and we covered how an integration between Torque and CI/CD tools usually looks.

The full source code of the examples above is available here: TBD


This topic has been closed for comments