Tork Documentation

repository·main·Indexed 21 days ago

https://github.com/runabol/tork

A highly-scalable, general-purpose workflow engine that executes tasks within isolated containers. Tork supports standalone and distributed modes using a Coordinator/Worker architecture and provides runtimes for Docker, Podman, and Shell. Key features include cron scheduling, parallel task execution, conditional execution via the expr language, and a REST API for job management.

Tokens
13.8K
Snippets
64
Records
74
Agent score
73%

What's inside tork

  1. Define Job Inputs and Secrets

    main

    Tork jobs support dynamic values through inputs and sensitive data through secrets.

    Inputs

    Use the inputs block to define variables that can be referenced in tasks using the {{ inputs.key_name }} syntax.

    Secrets

    Use the secrets block for sensitive information. These values are automatically redacted in API responses. Reference them in tasks using the {{ secrets.key_name }} syntax.

    Note: When passing secrets to a task's environment, use the expression language to map them to environment variables.

    name: my job
    inputs:
      source: https://example.com/path/to/video.mov
    secrets:
      api_key: 1111-1111-1111-1111
    tasks:
      - name: my task
        image: alpine:latest
        env:
          SOURCE_URL: '{{ inputs.source }}'
          API_KEY: '{{ secrets.api_key }}'
        run: curl -X POST -H "API_KEY: $API_KEY" http://example.com
  2. How Tork's distributed architecture works

    main

    Tork can operate in two modes: Standalone (all-in-one) and Distributed (Coordinator + Workers).

    In Distributed mode, components interact as follows:

    • Coordinator: Tracks jobs, dispatches work to workers, and handles retries/failures. It is stateless and leaderless. It does not run tasks.
    • Worker: Executes tasks using a runtime (typically Docker).
    • Broker: A message broker (like RabbitMQ) that routes messages between the Coordinator and Workers.
    • Datastore: Persists the state of jobs and tasks.
    • Runtime: The execution environment for tasks (Docker, Podman, or Shell).

    To run in distributed mode, you must start a broker (e.g., RabbitMQ), then start the coordinator, and finally one or more workers.

    # 1. Start RabbitMQ
    docker run -d -p 5672:5672 -p 15672:15672 --name=tork-rabbitmq rabbitmq:3-management
    
    # 2. Start Coordinator
    TORK_DATASTORE_TYPE=postgres TORK_BROKER_TYPE=rabbitmq ./tork run coordinator
    
    # 3. Start Worker(s)
    TORK_BROKER_TYPE=rabbitmq ./tork run worker
  3. Define and execute tasks in Tork

    main

    A task is the fundamental unit of execution in Tork. When using the Docker runtime, each task runs in its own container. A task requires an image (the Docker image to use) and a run property (the script or command to execute).

    To capture output from a task for use in subsequent tasks, write to the $TORK_OUTPUT environment variable. You can also assign a var name to the task to store its result in the job context.

    name: hello job
    tasks:
      - name: say hello
        var: task1
        image: ubuntu:mantic
        run: |
          echo -n hello world > $TORK_OUTPUT
  4. Use Webhooks for Job/Task state changes

    main

    You can configure Tork to notify external services when a job or task changes state using the webhooks block. You can use the if conditional to trigger webhooks only on specific states.

    Supported events:

    • job.StateChange
    • task.StateChange
    name: my job
    webhooks:
      - url: http://example.com/my/webhook
        event: job.StateChange
        headers:
          my-header: somevalue
        if: "{{ job.State == 'COMPLETED' }}"
    tasks:
      - name: my task
        image: alpine:latest
        run: echo hello world
  5. Configure task mounts

    main

    Tork supports several mount types for providing storage to tasks:

    • volume: A Docker volume (ephemeral, removed when the task ends).
    • bind: A host path mounted into the container.
    • tmpfs: An in-memory mount (Linux only).

    pre and post tasks run on the same worker as the main task and share its mounts and networks.

    name: mounts job
    tasks:
      - name: convert the first 5 seconds of a video
        image: jrottenberg/ffmpeg:3.4-alpine
        run: ffmpeg -i /tmp/my_video.mov -t 5 /tmp/output.mp4
        mounts:
          - type: volume
            target: /tmp
        pre:
          - name: download the remote file
            image: alpine:3.18.3
            run: wget http://example.com/my_video.mov -O /tmp/my_video.mov
  6. Use Tork as a library

    main

    You can embed Tork into your own Go applications by importing the github.com/runabol/tork/cli and github.com/runabol/tork/conf packages. To start the engine, you must first load the configuration using conf.LoadConfig() and then execute the CLI instance using cli.New().Run().

    package main
    
    import (
    	"fmt"
    	"os"
    	"github.com/runabol/tork/cli"
    	"github.com/runabol/tork/conf"
    )
    
    func main() {
    	if err := conf.LoadConfig(); err != nil {
    		fmt.Println(err)
    		os.Exit(1)
    	}
    	if err := cli.New().Run(); err != nil {
    		fmt.Println(err)
    		os.Exit(1)
    	}
    }
  7. Use expressions and conditional execution

    main

    Tork uses the expr language for expressions. You can access several context namespaces: inputs, secrets, tasks, and job.

    Use the if property on a task to perform conditional execution based on an expression.

    name: conditional job
    inputs:
      run: 'true'
    tasks:
      - name: say something
        if: "{{ inputs.run == 'true' }}"
        image: ubuntu:mantic
        run: echo "this runs only when inputs.run is 'true'"
  8. Schedule jobs with Cron

    main

    Tork supports scheduled jobs using standard cron syntax. To use this, submit your job definition to the /scheduled-jobs endpoint instead of the /jobs endpoint.

    name: scheduled job test
    schedule:
      cron: "0/5 * * * *"   # every 5 minutes
    tasks:
      - name: my first task
        image: alpine:3.18.3
        run: echo -n hello world

    Submit via API:

    curl -s -X POST --data-binary @job.yaml \
      -H "Content-type: text/yaml" \
      http://localhost:8000/scheduled-jobs | jq .
  9. Use variables and outputs between tasks

    main

    You can pass data between tasks using the var property and the tasks context namespace. A task writes its result to $TORK_OUTPUT, and the next task can access it using the expression {{ tasks.<VAR_NAME> }}.

    name: output and variables job
    tasks:
      - name: populate a variable
        var: task1
        image: ubuntu:mantic
        run: echo -n "world" > "$TORK_OUTPUT"
      - name: say hello
        image: ubuntu:mantic
        env:
          NAME: '{{ tasks.task1 }}'
        run: echo -n hello $NAME
  10. Quick Start with Tork

    main

    Tork is a scalable workflow engine where jobs consist of multiple tasks running in containers. To get started, you need a recent version of Docker and the Tork binary.

    1. Set up PostgreSQL

    Run a PostgreSQL container for the datastore:

    docker run -d \
      --name tork-postgres \
      -p 5432:5432 \
      -e POSTGRES_PASSWORD=tork \
      -e POSTGRES_USER=tork \
      -e PGDATA=/var/lib/postgresql/data/pgdata \
      -e POSTGRES_DB=tork postgres:15.3

    Run the migration to initialize the schema:

    TORK_DATASTORE_TYPE=postgres ./tork migration

    2. Run in Standalone Mode

    Start Tork on a single machine:

    ./tork run standalone

    3. Submit a Hello World Job

    Create a hello.yaml file:

    ---
    name: hello job
    tasks:
      - name: say hello
        image: ubuntu:mantic
        run: |
          echo -n hello world
      - name: say goodbye
        image: alpine:latest
        run: |
          echo -n bye world

    Submit the job via REST API:

    JOB_ID=$(curl -s -X POST --data-binary @hello.yaml \
      -H "Content-type: text/yaml" http://localhost:8000/jobs | jq -r .id)

    Check the job status:

    curl -s http://localhost:8000/jobs/$JOB_ID
    # Setup DB
    TORK_DATASTORE_TYPE=postgres ./tork migration
    
    # Run
    ./tork run standalone
    
    # Submit
    curl -s -X POST --data-binary @hello.yaml -H "Content-type: text/yaml" http://localhost:8000/jobs
  11. Configure Tork via config.toml or environment variables

    main

    Tork can be configured using a config.toml file or environment variables.

    Config File Locations:

    1. Current directory
    2. ~/tork/config.toml
    3. /etc/tork/config.toml

    You can override the config file location using the TORK_CONFIG environment variable.

    Environment Variables: Use the prefix TORK_ followed by the property path with dots replaced by underscores (e.g., TORK_LOGGING_LEVEL=warn).

    TORK_CONFIG=myconfig.toml ./tork run standalone
  12. Define and manage Task states

    main

    A Task in Tork exists in one of several states. You can use the TaskState type to track or filter tasks. The TaskStateActive slice identifies states where a task is still in progress or waiting to be processed.

    Available States:

    • CREATED
    • PENDING
    • SCHEDULED
    • RUNNING
    • CANCELLED
    • STOPPED
    • COMPLETED
    • FAILED
    • SKIPPED

    Use the IsActive() method on a Task instance to check if it is currently in an active state (CREATED, PENDING, SCHEDULED, or RUNNING).

    // Example checking if a task is active
    if task.IsActive() {
        fmt.Println("Task is currently in progress or queued")
    }