Concepts and Interface
Engine
The image that contains the desired operating system and all the software required to accomplish a data transformation. This resource is a Docker Image that contains the function(s) that are to be exposed in the Processing Framework API as Operation objects.
Engines may contain an executable, a script, or pure logic that accomplishes the business need. Engines should also be written with Executions in mind. Consider what information you would like to know given either success or failure scenarios as this information should be logged and then can be retrieved after an Execution is processed.
Dockerfile Requirements
Patching Labels
With security measures from CIS (updated February 2024), all Docker containers must contain a label from this list.
The “software-patching” and “os-patching” labels must be specified and both must contain valid values that are relevant to your organization from the list provided by the CIS team.
Please note that the label value that you must specify is found after the cyberorg:: prefix. If you include the
prefix, the image will fail validation.
LABEL software-patching="known-value"LABEL os-patching="known-value"Product Label
The “product” label must be specified and contain a valid value that is relevant to the product owner. These values are discoverable through the Products API or from this list
LABEL product="known-value"User
It is crucial that the Engine Container runs as a non-root user. If a root user is specified, the Engine will be
rejected during the ingestion. More specifically, a user cannot be specified as 0 nor can a user specified by the user
name even if that user name is a non-root user. This means that a user must be specified by the integer ID
representation of the non-root user.
Architecture
A linux Engine image must be built for the amd64 CPU architecture. When triggering a build, use the
--platform linux/amd64 flag to specify the target platform for the build output.
Operation
The unique business logic that performs the data transformation. This is typically written in an Engine as a function. An Operation function is where input ports, output ports, parameters, output parameters, logs, etc. are interacted with.
If you are leveraging the Python package tdsplitengine, you may utilize a decorator such as this to denote the
Operation function:
@operation_dispatcher.operation(operation_name="example_operation")def example_operation(task_input: dict) -> dict:...It is critical to note that an Operation Identifier must be related to the business logic coded into an Engine by some
means. More explicitly, the name of a function in an Engine should have a corresponding Operation Identifier on the API.
If you are using the Python library tdsplitengine, you can utilize the decorator described above as a way to
explicitly define an Operation Identifier.
Task JSON
The lightweight interface that contains instructions for file and parameter handling. There are two required types of Task JSON — Request and Result. The former is provided by the Processing Framework, and the latter is created by the Engine container.
Request Schema
The contents of the task request object are written to the PF_TASK_JSON_PATH file that is accessible as an environment
variable. Important information such as absolute paths for where to read files from (Input Ports), paths for where to
write files to (Output Ports), parameters and their values, and the location to write log messages to is all contained
within the Request JSON. Detailed information can be found here.
Results Schema
The Engine must write the contents of the Task Results JSON to the absolute path specified by the results_json
property from the Task Request JSON. The contents of the Results object provided the communication channel to indicate
success or failure content along with any Output Parameters. Detailed information can be found
here.
Engine Container Interface
Engine containers are required to implement the Engine interface. It is the responsibility of the Engine developer to provide a container image that implements the interface. The Processing Framework interacts with an Engine container through a defined interface. This interface is implemented using environment variables and JSON files written to a shared volume.
The Processing Framework controls the lifecycle of the Engine container through this interface.
Each Engine container is used to execute a single Operation. After completing the requested Operation, the container is expected to terminate.
The Processing Framework sets the PF_TASK_JSON_PATH environment variable in the container. The value of the
variable is the path to a Task JSON file that specifies details of the Operation the container should perform. The JSON
file specified by PF_TASK_JSON_PATH will not exist when the Engine container first starts. The Engine container’s
entry executable must watch or poll for the existence of the PF_TASK_JSON_PATH specified file. The contents of the
Task JSON are described below.
The Task Request JSON specifies a results_json property. The value of this property is an absolute path where the
Engine should write a results JSON file to communicate the final status of the Operation.
Lifecycle
-
The Processing Framework runs a container using the integrator-provided Engine Docker image.
- The Framework sets the
PF_TASK_JSON_PATHenvironment variable in the container. - The Framework doesn’t specify the command to run in the container. The dockerfile used to build the image must
specify the command to run with a
CMDorENTRYPOINTstatement.
- The Framework sets the
-
The container waits for a JSON file to be written to the absolute path specified by the
PF_TASK_JSON_PATHenvironment variable. This file is created after the Engine container is running. A Task JSON file will be written in approximately one minute of the Engine container’s start time. It is important to note that the Engine container must watch for the existence of thePF_TASK_JSON_PATHspecified file. A pseudocode example of this might look like:WHILE True:IF IsEnvVarDefined("PF_TASK_JSON_PATH") AND FileExists(GetEnvVar("PF_TASK_JSON_PATH")) THENBREAK;ENDDONE -
The Engine container parses the contents of the file specified by
PF_TASK_JSON_PATHto obtain atask_jsonobject. -
If
task_jsonobject has a top levelterminateproperty with the valueTrue, the Engine container should immediately exit. The container does not need to write aresults_jsonfile before exiting. -
Execute the Operation specified in the
task_resultsJSON file or exit if instructed.-
Write a Results JSON file to the path specified by the
results_jsonproperty of Task JSON. This file communicates whether the requested Operation succeeded or failed. When this file is written, the Processing Framework assumes the Operation has finished and does not wait for the Engine container to exit. -
Exit the Engine container. If the Engine container exits without writing a JSON file to the path specified by
results_json, the Processing Framework assumes the Engine crashed and will report the Operation as failed.
-
Custom Environment Variables
Custom environment variables (or secrets) are useful for adding information to a container on launch. This prevents sensitive information from being included within the docker image. Some use cases include:
- API Key/Secret
- Executable license key
Secrets can be mounted to an engine using Deployment Secret resources. See the API documentation for a practical example of attaching a Secret to an Engine.
Delegated Authorization
Delegated Authorization allows Processing Framework Operations to securely access a user’s private resources during a Procedure Execution. It enables services to call other Trimble APIs on a user’s behalf without embedding credentials in the Engine code.
This is a critical feature and involves thoughtful access. It is restricted to teams and Engines that have passed a Well-Architected Review by the Cloud Platform Architecture team. More details can be found in the comprehensive guide outlining all aspects of the functionality.