a9cd7750f4
CI / unit-test (push) Has been cancelled
CI / detect-changes (push) Has been cancelled
CI / build (push) Has been cancelled
Publish docs via GitHub Pages / Deploy docs (push) Has been cancelled
CI / test-harness (push) Has been cancelled
CI / generate-e2e-matrix (push) Has been cancelled
CI / e2e (push) Has been cancelled
CI / build-ui (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
UI v2 Integration CI / E2E (Integration) (push) Has been cancelled
UI v2 CI / Lint, Format & Test (push) Has been cancelled
UI v2 CI / E2E (Mocked) (push) Has been cancelled
273 lines
10 KiB
Markdown
273 lines
10 KiB
Markdown
---
|
||
description: "Build Conductor workers in Go with type-safe task definitions and workflow management."
|
||
---
|
||
|
||
# Go SDK
|
||
|
||
!!! info "Source"
|
||
GitHub: [conductor-oss/go-sdk](https://github.com/conductor-oss/go-sdk) | Report issues and contribute on GitHub.
|
||
|
||
## Installation
|
||
|
||
1. Initialize your module. e.g.:
|
||
|
||
```shell
|
||
mkdir hello_world
|
||
cd hello_world
|
||
go mod init hello_world
|
||
```
|
||
|
||
2. Get the SDK:
|
||
|
||
```shell
|
||
go get github.com/conductor-sdk/conductor-go
|
||
```
|
||
|
||
## Hello World
|
||
|
||
In this repo you will find a basic "Hello World" under [examples/hello_world](https://github.com/conductor-oss/go-sdk/blob/main/examples/hello_world/).
|
||
|
||
Let's analyze the app in 3 steps.
|
||
|
||
|
||
> [!note]
|
||
> You will need an up & running Conductor Server.
|
||
>
|
||
> For details on how to run Conductor take a look at [our guide](https://conductor-oss.github.io/conductor/devguide/running/deploy.html).
|
||
>
|
||
> The examples expect the server to be listening on http://localhost:8080.
|
||
|
||
|
||
### Step 1: Creating the workflow by code
|
||
|
||
The "greetings" workflow is going to be created by code and registered in Conductor.
|
||
|
||
Check the `CreateWorkflow` function in [examples/hello_world/src/workflow.go](https://github.com/conductor-oss/go-sdk/blob/main/examples/hello_world/src/workflow.go).
|
||
|
||
```go
|
||
func CreateWorkflow(executor *executor.WorkflowExecutor) *workflow.ConductorWorkflow {
|
||
wf := workflow.NewConductorWorkflow(executor).
|
||
Name("greetings").
|
||
Version(1).
|
||
Description("Greetings workflow - Greets a user by their name").
|
||
TimeoutPolicy(workflow.TimeOutWorkflow, 600)
|
||
|
||
greet := workflow.NewSimpleTask("greet", "greet_ref").
|
||
Input("person_to_be_greated", "${workflow.input.name}")
|
||
|
||
wf.Add(greet)
|
||
|
||
wf.OutputParameters(map[string]interface{}{
|
||
"greetings": greet.OutputRef("hello"),
|
||
})
|
||
|
||
return wf
|
||
}
|
||
```
|
||
|
||
In the above code first we create a workflow by calling `workflow.NewConductorWorkflow(..)` and set its properties `Name`, `Version`, `Description` and `TimeoutPolicy`.
|
||
|
||
Then we create a [Simple Task](https://orkes.io/content/reference-docs/worker-task) of type `"greet"` with reference name `"greet_ref"` and add it to the workflow. That task gets the workflow input `"name"` as an input with key `"person_to_be_greated"`.
|
||
|
||
> [!note]
|
||
>`"person_to_be_greated"` is too verbose! Why would you name it like that?
|
||
>
|
||
> It's just to make it clear that the workflow input is not passed automatically.
|
||
>
|
||
> The worker will get the actual value of the workflow input because of this mapping `Input("person_to_be_greated", "${workflow.input.name}")` in the workflow definition.
|
||
>
|
||
>Expressions like `"${workflow.input.name}"` will be replaced by their value during execution.
|
||
|
||
Last but not least, the output of the workflow is set by calling `wf.OutputParameters(..)`.
|
||
|
||
The value of `"greetings"` is going to be whatever `"hello"` is in the output of the executed `"greet"` task, e.g.: if the task output is:
|
||
```
|
||
{
|
||
"hello" : "Hello, John"
|
||
}
|
||
```
|
||
|
||
The expected workflow output will be:
|
||
```
|
||
{
|
||
"greetings": "Hello, John"
|
||
}
|
||
```
|
||
|
||
The Go code translates to this JSON defininition. You can view this in your Conductor server after registering the workflow.
|
||
|
||
```json
|
||
{
|
||
"schemaVersion": 2,
|
||
"name": "greetings",
|
||
"description": "Greetings workflow - Greets a user by their name",
|
||
"version": 1,
|
||
"tasks": [
|
||
{
|
||
"name": "greet",
|
||
"taskReferenceName": "greet_ref",
|
||
"type": "SIMPLE",
|
||
"inputParameters": {
|
||
"name": "${workflow.input.name}"
|
||
}
|
||
}
|
||
],
|
||
"outputParameters": {
|
||
"Greetings": "${greet_ref.output.greetings}"
|
||
},
|
||
"timeoutPolicy": "TIME_OUT_WF",
|
||
"timeoutSeconds": 600
|
||
}
|
||
```
|
||
|
||
> [!note]
|
||
> Workflows can also be registered using the API. Using the JSON you can make the following request:
|
||
> ```shell
|
||
> curl -X POST -H "Content-Type:application/json" \
|
||
> http://localhost:8080/api/metadata/workflow -d @greetings_workflow.json
|
||
> ```
|
||
|
||
In [Step 3](#step-3-running-the-application) you will see how to create an instance of `executor.WorkflowExecutor`.
|
||
|
||
|
||
### Step 2: Creating the worker
|
||
|
||
A worker is a function with a specific task to perform.
|
||
|
||
In this example the worker just uses the input `person_to_be_greated` to say hello, as you can see in [examples/hello_world/src/worker.go](https://github.com/conductor-oss/go-sdk/blob/main/examples/hello_world/src/worker.go).
|
||
|
||
```go
|
||
func Greet(task *model.Task) (interface{}, error) {
|
||
return map[string]interface{}{
|
||
"hello": "Hello, " + fmt.Sprintf("%v", task.InputData["person_to_be_greated"]),
|
||
}, nil
|
||
}
|
||
```
|
||
|
||
To learn more about workers take a look at [Writing Workers with the Go SDK](https://github.com/conductor-oss/go-sdk/blob/main/docs/workers_sdk.md).
|
||
|
||
> [!note]
|
||
> A single workflow can have task workers written in different languages and deployed anywhere, making your workflow polyglot and distributed!
|
||
|
||
### Step 3: Running the application
|
||
|
||
The application is going to start the Greet worker (to execute tasks of type "greet") and it will register the workflow created in [step 1](#step-1-creating-the-workflow-by-code).
|
||
|
||
To begin with, let's take a look at the variable declaration in [examples/hello_world/main.go](https://github.com/conductor-oss/go-sdk/blob/main/examples/hello_world/main.go).
|
||
|
||
```go
|
||
|
||
var (
|
||
apiClient = client.NewAPIClientFromEnv()
|
||
taskRunner = worker.NewTaskRunnerWithApiClient(apiClient)
|
||
workflowExecutor = executor.NewWorkflowExecutor(apiClient)
|
||
)
|
||
|
||
```
|
||
|
||
First we create an `APIClient` instance. This is a REST client.
|
||
|
||
We need to provide the correct settings to our client. In this example, `client.NewAPIClientFromEnv()` is used, which initializes a new client by reading the settings from the following environment variables: `CONDUCTOR_SERVER_URL`, `CONDUCTOR_AUTH_KEY`, and `CONDUCTOR_AUTH_SECRET`.
|
||
`CONDUCTOR_CLIENT_HTTP_TIMEOUT` lets you configure the HTTP timeout for our client, in seconds. If not set, defaults to 30 seconds.
|
||
|
||
> [!tip]
|
||
> For advanced configuration options and detailed examples see the [API Client Configuration Guide](https://github.com/conductor-oss/go-sdk/blob/main/docs/api_client/README.md).
|
||
|
||
Now let's take a look at the `main` function:
|
||
|
||
```go
|
||
func main() {
|
||
// Start the Greet Worker. This worker will process "greet" tasks.
|
||
taskRunner.StartWorker("greet", hello_world.Greet, 1, time.Millisecond*100)
|
||
|
||
// This is used to register the Workflow, it's a one-time process. You can comment from here
|
||
wf := hello_world.CreateWorkflow(workflowExecutor)
|
||
err := wf.Register(true)
|
||
if err != nil {
|
||
log.Error(err.Error())
|
||
return
|
||
}
|
||
// Till Here after registering the workflow
|
||
|
||
// Start the greetings workflow
|
||
id, err := workflowExecutor.StartWorkflow(
|
||
&model.StartWorkflowRequest{
|
||
Name: "greetings",
|
||
Version: 1,
|
||
Input: map[string]string{
|
||
"name": "Gopher",
|
||
},
|
||
},
|
||
)
|
||
|
||
if err != nil {
|
||
log.Error(err.Error())
|
||
return
|
||
}
|
||
|
||
log.Info("Started workflow with Id: ", id)
|
||
|
||
// Get a channel to monitor the workflow execution -
|
||
// Note: This is useful in case of short duration workflows that completes in few seconds.
|
||
channel, _ := workflowExecutor.MonitorExecution(id)
|
||
run := <-channel
|
||
log.Info("Output of the workflow: ", run.Output)
|
||
}
|
||
```
|
||
|
||
The `taskRunner` uses the `apiClient` to poll for work and complete tasks. It also starts the worker and handles concurrency and polling intervals for us based on the configuration provided.
|
||
|
||
That simple line `taskRunner.StartWorker("greet", hello_world.Greet, 1, time.Millisecond*100)` is all that's needed to get our Greet worker up & running and processing tasks of type `"greet"`.
|
||
|
||
The `workflowExecutor` gives us an abstraction on top of the `apiClient` to manage workflows. It is used under the hood by `ConductorWorkflow` to register the workflow and it's also used to start and monitor the execution.
|
||
|
||
#### Running the example with a local Conductor OSS server:
|
||
```shell
|
||
export CONDUCTOR_SERVER_URL="http://localhost:8080/api"
|
||
cd examples
|
||
go run hello_world/main.go
|
||
```
|
||
|
||
#### Running the example with an [Orkes developer account](https://developer.orkescloud.com).
|
||
```shell
|
||
export CONDUCTOR_SERVER_URL="https://developer.orkescloud.com/api"
|
||
export CONDUCTOR_AUTH_KEY="..."
|
||
export CONDUCTOR_AUTH_SECRET="..."
|
||
cd examples
|
||
go run hello_world/main.go
|
||
```
|
||
|
||
> [!note]
|
||
> Orkes Conductor requires authentication. [Get a key and secret from the server](https://orkes.io/content/how-to-videos/access-key-and-secret) to set those variables.
|
||
|
||
The above commands should give an output similar to
|
||
```shell
|
||
INFO[0000] Updated poll interval for task: greet, to: 100ms
|
||
INFO[0000] Started 1 worker(s) for taskName greet, polling in interval of 100 ms
|
||
INFO[0000] Started workflow with Id:14a9fcc5-3d74-11ef-83dc-acde48001122
|
||
INFO[0000] Output of the workflow:map[Greetings:Hello, Gopher]
|
||
```
|
||
|
||
## Deprecated Methods
|
||
Some methods in the SDK client interfaces are now deprecated. They’ve been replaced with newer methods that follow more consistent naming. Please refer to our [Migration Guide](https://github.com/conductor-oss/go-sdk/blob/main/docs/migration_guide.md) for detailed information on how to update your code.
|
||
# Further Reading
|
||
|
||
- [Writing Workers with the Go SDK](https://github.com/conductor-oss/go-sdk/blob/main/docs/workers_sdk.md)
|
||
- [Authoring Workflows with the Go SDK](https://github.com/conductor-oss/go-sdk/blob/main/docs/workflow_sdk.md)
|
||
- [Logging Configuration](https://github.com/conductor-oss/go-sdk/blob/main/docs/logger_sdk.md)
|
||
- [Migration Guide: Deprecated Methods](https://github.com/conductor-oss/go-sdk/blob/main/docs/migration_guide.md)
|
||
- [API Client Configuration](https://github.com/conductor-oss/go-sdk/blob/main/docs/api_client/README.md) - Complete guide to API client setup, authentication, and proxy configuration
|
||
- [TLS Configuration Guide](https://github.com/conductor-oss/go-sdk/blob/main/docs/api_client/tls_configuration.md) - TLS/SSL configuration for self-signed certificates and mTLS
|
||
|
||
|
||
## Examples
|
||
|
||
Browse all examples on GitHub: [conductor-oss/go-sdk/examples](https://github.com/conductor-oss/go-sdk/tree/main/examples)
|
||
|
||
| Example | Type |
|
||
|---|---|
|
||
| [Readme](https://github.com/conductor-oss/go-sdk/blob/main/examples/README.md) | file |
|
||
| [Api Gateway](https://github.com/conductor-oss/go-sdk/tree/main/examples/api_gateway) | directory |
|
||
| [Hello World](https://github.com/conductor-oss/go-sdk/tree/main/examples/hello_world) | directory |
|
||
| [Workflow](https://github.com/conductor-oss/go-sdk/tree/main/examples/workflow) | directory |
|