diff --git a/codeblocks/codeblocks.json b/codeblocks/codeblocks.json index ce730777..f9726e52 100644 --- a/codeblocks/codeblocks.json +++ b/codeblocks/codeblocks.json @@ -1 +1 @@ -{"https://github.com/orkes-io/workflow-cicd/blob/main/src/deploy_workflows.sh---1":"{`export response=\\`curl -s -X POST $CONDUCTOR_SERVER_URL/token -H 'Content-Type:application/json' -d '{\n\t\"keyId\": \"'\"$KEY\"'\",\n\t\"keySecret\": \"'\"$SECRET\"'\"\n}'\\`\n\nif [[ \"$response\" != *'token'* ]]; then\n echo \"Unable to generate the auth header. Please check KEY, SECRET and CONDUCTOR_SERVER_URL variables\"\n echo \"Server response:\"\n echo $response\n exit 1\nfi\n\nexport token=\\`echo $response | cut -d '\"' -f4\\`\n\nfor FILE in main/resources/workflows/*;\n do\n echo \"Deploying @$FILE\";\n\n curl -X POST $CONDUCTOR_SERVER_URL/metadata/workflow?overwrite=true \\\n -H \"X-Authorization: $token\" \\\n -H \"accept: */*\" \\\n -H \"Content-Type: application/json\" \\\n -d @$FILE\n done\n`}","https://github.com/orkes-io/workflow-cicd/blob/main/src/deploy_workflows.sh---1-lines":"#L8-L32","https://github.com/conductor-sdk/orkes-java-springboot2-example/blob/main/src/main/java/io/orkes/example/banking/workers/ConductorWorkers.java---1":"{`\n /**\n * Note: Using this setting, up to 5 tasks will run in parallel, with tasks being polled every 200ms\n */\n @WorkerTask(value = \"fraud-check\", threadCount = 5, pollingInterval = 200)\n public FraudCheckResult checkForFraudTask(DepositDetail depositDetail) {\n return fraudCheckService.checkForFraud(depositDetail);\n }\n`}","https://github.com/conductor-sdk/orkes-java-springboot2-example/blob/main/src/main/java/io/orkes/example/banking/workers/ConductorWorkers.java---1-lines":"#L27-L35","https://github.com/conductor-sdk/csharp-sdk-examples/blob/main/Examples/Worker/Workers.cs---1":"{` // Note: Using this setting, up to 5 tasks will run in parallel, with tasks being polled every 200ms\n [WorkerTask(taskType: \"fraud-check\", batchSize: 5, domain: null, pollIntervalMs: 200, workerId: \"workerId\")]\n public TaskResult FraudWorker(Task task)\n {\n var depositDetail = (DepositDetail)task.InputData[\"depositDetail\"];\n var fraudCheckResult = _fraudCheckService.CheckForFraud(depositDetail);\n var result = task.Completed();\n result.OutputData = Examples.Util.TypeUtil.GetDictionaryFromObject(fraudCheckResult);\n return result;\n }`}","https://github.com/conductor-sdk/csharp-sdk-examples/blob/main/Examples/Worker/Workers.cs---1-lines":"#L25-L34","https://github.com/conductor-sdk/javascript-sdk-examples/blob/main/src/banking/workers/workers.js---1":"{`const fraudCheckWorker = {\n taskDefName: \"fraud-check\",\n execute: async ({ inputData }) => {\n const { amount, accountId } = inputData;\n const fraudResult = fraudService.isFraudulentTxn(accountId, amount);\n return {\n outputData: fraudResult,\n status: \"COMPLETED\",\n };\n },\n};`}","https://github.com/conductor-sdk/javascript-sdk-examples/blob/main/src/banking/workers/workers.js---1-lines":"#L4-L14","https://github.com/conductor-sdk/typescript-examples/blob/main/src/banking/workers/workers.ts---1":"{`export const fraudCheckWorker: ConductorWorker = {\n taskDefName: \"fraud-check\",\n execute: async ({ inputData }) => {\n const amount = inputData?.amount;\n const accountId = inputData?.accountId;\n const fraudResult = fraudService.isFraudulentTxn(accountId, amount);\n return {\n outputData: fraudResult,\n status: \"COMPLETED\",\n };\n },\n};`}","https://github.com/conductor-sdk/typescript-examples/blob/main/src/banking/workers/workers.ts---1-lines":"#L5-L16","https://github.com/conductor-sdk/orkes-java-springboot2-example/blob/main/src/main/java/io/orkes/example/banking/workers/ConductorWorkers.java---2":"{`\n @WorkerTask(value = \"retrieve-deposit-batch\", threadCount = 5, pollingInterval = 200)\n public List retrieveDepositBatch(@InputParam(\"batchCount\") Integer batchCount) {\n if (batchCount == null) {\n batchCount = random.nextInt(5, 11);\n }\n batchCount = Math.min(100, batchCount); // Limit to 100 in playground\n List depositDetails = IntStream.range(0, batchCount)\n .mapToObj(i -> DepositDetail.builder()\n .accountId(\"acc-id-\" + i)\n .amount(BigDecimal.valueOf(i * 1500L)) // Create random amounts\n .build())\n .toList();\n log.info(\"Returning {} transactions\", depositDetails.size());\n return depositDetails;\n }\n`}","https://github.com/conductor-sdk/orkes-java-springboot2-example/blob/main/src/main/java/io/orkes/example/banking/workers/ConductorWorkers.java---2-lines":"#L40-L56","https://github.com/conductor-sdk/orkes-java-springboot2-example/blob/main/src/main/java/io/orkes/example/banking/service/WorkflowService.java---1":"{`\n StartWorkflowRequest request = new StartWorkflowRequest();\n request.setName(\"deposit_payment\");\n Map inputData = new HashMap<>();\n inputData.put(\"amount\", depositDetail.getAmount());\n inputData.put(\"accountId\", depositDetail.getAccountId());\n request.setInput(inputData);\n\n String workflowId = workflowClient.startWorkflow(request);\n log.info(\"Workflow id: {}\", workflowId);\n`}","https://github.com/conductor-sdk/orkes-java-springboot2-example/blob/main/src/main/java/io/orkes/example/banking/service/WorkflowService.java---1-lines":"#L22-L32","https://github.com/conductor-sdk/csharp-sdk-examples/blob/main/Examples/Service/WorkflowService.cs---1":"{` var request = new StartWorkflowRequest\n {\n Name = WORKFLOW_NAME,\n Version = WORKFLOW_VERSION,\n Input = Examples.Util.TypeUtil.GetDictionaryFromObject(depositDetail)\n };\n var workflowId = _workflowClient.StartWorkflow(request);\n Console.WriteLine($\"Started deposit workflow id: {workflowId}\");\n return workflowId;`}","https://github.com/conductor-sdk/csharp-sdk-examples/blob/main/Examples/Service/WorkflowService.cs---1-lines":"#L23-L31","https://github.com/conductor-sdk/orkes-java-springboot2-example/blob/main/src/main/java/io/orkes/example/banking/controller/BankingApiController.java---1":"{` @PostMapping(value = \"/triggerDepositFlow\", produces = \"application/json\")\n public ResponseEntity> triggerDepositFlow(@RequestBody DepositDetail depositDetail) {\n log.info(\"Starting deposit flow for: {}\", depositDetail);\n return ResponseEntity.ok(workflowService.startDepositWorkflow(depositDetail));\n }\n`}","https://github.com/conductor-sdk/orkes-java-springboot2-example/blob/main/src/main/java/io/orkes/example/banking/controller/BankingApiController.java---1-lines":"#L32-L37","https://github.com/conductor-sdk/orkes-java-springboot2-example/blob/main/src/main/java/io/orkes/example/banking/workers/PollUntilConditionMeetsWorker.java---1":"{` @Override\n public TaskResult execute(Task task) {\n TaskResult taskResult = new TaskResult(task);\n if (!task.getInputData().containsKey(POLL_COUNTER)) {\n taskResult.addOutputData(\"message\", \"pollCounter param not found in input, will use default of \" + defaultPollCount + \" polls\");\n }\n\n int pollCounter = Math.min(10, castToInt(task.getInputData().getOrDefault(POLL_COUNTER, defaultPollCount)));\n int pollIntervalSeconds = Math.min(10, castToInt(task.getInputData().getOrDefault(POLL_INTERVAL_SECONDS, 5)));\n\n // Add these to the output for context\n taskResult.addOutputData(POLL_INTERVAL_SECONDS, pollIntervalSeconds + \" (this test task has a max limit of 10 seconds)\");\n taskResult.addOutputData(POLL_COUNTER, pollCounter + \" (this test task has a max limit of 10 iterations)\");\n\n // We can read current iteration from the task output as the data will be retained on the worker when polled\n int currentIteration = castToInt(taskResult.getOutputData().getOrDefault(CURRENT_ITERATION, 0));\n\n // Increment the current iteration and set to the task output\n taskResult.addOutputData(CURRENT_ITERATION, ++currentIteration);\n taskResult.addOutputData(\"updatedTime\", new Date().toString());\n\n // While condition is not met, keep task in progress\n if (currentIteration < pollCounter) {\n taskResult.setStatus(TaskResult.Status.IN_PROGRESS);\n // Set to configured seconds to callback, and you can set this to any value as per the requirements\n taskResult.setCallbackAfterSeconds(pollIntervalSeconds);\n return taskResult;\n }\n\n // Set task as completed now that the poll count condition is met\n taskResult.setStatus(TaskResult.Status.COMPLETED);\n return taskResult;\n }`}","https://github.com/conductor-sdk/orkes-java-springboot2-example/blob/main/src/main/java/io/orkes/example/banking/workers/PollUntilConditionMeetsWorker.java---1-lines":"#L24-L56"} \ No newline at end of file +{"https://github.com/orkes-io/workflow-cicd/blob/main/src/deploy_workflows.sh---1":"{`export response=\\`curl -s -X POST $CONDUCTOR_SERVER_URL/token -H 'Content-Type:application/json' -d '{\n\t\"keyId\": \"'\"$KEY\"'\",\n\t\"keySecret\": \"'\"$SECRET\"'\"\n}'\\`\n\nif [[ \"$response\" != *'token'* ]]; then\n echo \"Unable to generate the auth header. Please check KEY, SECRET and CONDUCTOR_SERVER_URL variables\"\n echo \"Server response:\"\n echo $response\n exit 1\nfi\n\nexport token=\\`echo $response | cut -d '\"' -f4\\`\n\nfor FILE in main/resources/workflows/*;\n do\n echo \"Deploying @$FILE\";\n\n curl -X POST $CONDUCTOR_SERVER_URL/metadata/workflow?overwrite=true \\\n -H \"X-Authorization: $token\" \\\n -H \"accept: */*\" \\\n -H \"Content-Type: application/json\" \\\n -d @$FILE\n done\n`}","https://github.com/orkes-io/workflow-cicd/blob/main/src/deploy_workflows.sh---1-lines":"#L8-L32","https://github.com/conductor-sdk/orkes-java-springboot2-example/blob/main/src/main/java/io/orkes/example/banking/workers/ConductorWorkers.java---1":"{`\n /**\n * Note: Using this setting, up to 5 tasks will run in parallel, with tasks being polled every 200ms\n */\n @WorkerTask(value = \"fraud-check\", threadCount = 5, pollingInterval = 200)\n public FraudCheckResult checkForFraudTask(DepositDetail depositDetail) {\n return fraudCheckService.checkForFraud(depositDetail);\n }\n`}","https://github.com/conductor-sdk/orkes-java-springboot2-example/blob/main/src/main/java/io/orkes/example/banking/workers/ConductorWorkers.java---1-lines":"#L27-L35","https://github.com/conductor-sdk/csharp-sdk-examples/blob/main/Examples/Worker/Workers.cs---1":"{` // Note: Using this setting, up to 5 tasks will run in parallel, with tasks being polled every 200ms\n [WorkerTask(taskType: \"fraud-check\", batchSize: 5, domain: null, pollIntervalMs: 200, workerId: \"workerId\")]\n public TaskResult FraudWorker(Task task)\n {\n var depositDetail = (DepositDetail)task.InputData[\"depositDetail\"];\n var fraudCheckResult = _fraudCheckService.CheckForFraud(depositDetail);\n var result = task.Completed();\n result.OutputData = Examples.Util.TypeUtil.GetDictionaryFromObject(fraudCheckResult);\n return result;\n }`}","https://github.com/conductor-sdk/csharp-sdk-examples/blob/main/Examples/Worker/Workers.cs---1-lines":"#L25-L34","https://github.com/conductor-sdk/javascript-sdk-examples/blob/main/src/banking/workers/workers.js---1":"{`const fraudCheckWorker = {\n taskDefName: \"fraud-check\",\n execute: async ({ inputData }) => {\n const { amount, accountId } = inputData;\n const fraudResult = fraudService.isFraudulentTxn(accountId, amount);\n return {\n outputData: fraudResult,\n status: \"COMPLETED\",\n };\n },\n domain: \"fraud\", // Optional\n pollInterval: 100, // Optional can be specified in the TaskManager\n concurrency: 1, // Optional can be specified in the TaskManager\n};`}","https://github.com/conductor-sdk/javascript-sdk-examples/blob/main/src/banking/workers/workers.js---1-lines":"#L4-L17","https://github.com/conductor-sdk/typescript-examples/blob/main/src/banking/workers/workers.ts---1":"{`export const fraudCheckWorker: ConductorWorker = {\n taskDefName: \"fraud-check\",\n execute: async ({ inputData }) => {\n const amount = inputData?.amount;\n const accountId = inputData?.accountId;\n const fraudResult = fraudService.isFraudulentTxn(accountId, amount);\n return {\n outputData: fraudResult,\n status: \"COMPLETED\",\n };\n },\n domain: \"fraud\", // Optional\n pollInterval: 100, // Optional can be specified in the TaskManager\n concurrency: 2, // Optional can be specified in the TaskManager\n};`}","https://github.com/conductor-sdk/typescript-examples/blob/main/src/banking/workers/workers.ts---1-lines":"#L5-L19","https://github.com/conductor-sdk/orkes-java-springboot2-example/blob/main/src/main/java/io/orkes/example/banking/workers/ConductorWorkers.java---2":"{`\n @WorkerTask(value = \"retrieve-deposit-batch\", threadCount = 5, pollingInterval = 200)\n public List retrieveDepositBatch(@InputParam(\"batchCount\") Integer batchCount) {\n if (batchCount == null) {\n batchCount = random.nextInt(5, 11);\n }\n batchCount = Math.min(100, batchCount); // Limit to 100 in playground\n List depositDetails = IntStream.range(0, batchCount)\n .mapToObj(i -> DepositDetail.builder()\n .accountId(\"acc-id-\" + i)\n .amount(BigDecimal.valueOf(i * 1500L)) // Create random amounts\n .build())\n .toList();\n log.info(\"Returning {} transactions\", depositDetails.size());\n return depositDetails;\n }\n`}","https://github.com/conductor-sdk/orkes-java-springboot2-example/blob/main/src/main/java/io/orkes/example/banking/workers/ConductorWorkers.java---2-lines":"#L40-L56","https://github.com/conductor-sdk/orkes-java-springboot2-example/blob/main/src/main/java/io/orkes/example/banking/service/WorkflowService.java---1":"{`\n StartWorkflowRequest request = new StartWorkflowRequest();\n request.setName(\"deposit_payment\");\n Map inputData = new HashMap<>();\n inputData.put(\"amount\", depositDetail.getAmount());\n inputData.put(\"accountId\", depositDetail.getAccountId());\n request.setInput(inputData);\n\n String workflowId = workflowClient.startWorkflow(request);\n log.info(\"Workflow id: {}\", workflowId);\n`}","https://github.com/conductor-sdk/orkes-java-springboot2-example/blob/main/src/main/java/io/orkes/example/banking/service/WorkflowService.java---1-lines":"#L22-L32","https://github.com/conductor-sdk/csharp-sdk-examples/blob/main/Examples/Service/WorkflowService.cs---1":"{` var request = new StartWorkflowRequest\n {\n Name = WORKFLOW_NAME,\n Version = WORKFLOW_VERSION,\n Input = Examples.Util.TypeUtil.GetDictionaryFromObject(depositDetail)\n };\n var workflowId = _workflowClient.StartWorkflow(request);\n Console.WriteLine($\"Started deposit workflow id: {workflowId}\");\n return workflowId;`}","https://github.com/conductor-sdk/csharp-sdk-examples/blob/main/Examples/Service/WorkflowService.cs---1-lines":"#L23-L31","https://github.com/conductor-sdk/orkes-java-springboot2-example/blob/main/src/main/java/io/orkes/example/banking/controller/BankingApiController.java---1":"{` @PostMapping(value = \"/triggerDepositFlow\", produces = \"application/json\")\n public ResponseEntity> triggerDepositFlow(@RequestBody DepositDetail depositDetail) {\n log.info(\"Starting deposit flow for: {}\", depositDetail);\n return ResponseEntity.ok(workflowService.startDepositWorkflow(depositDetail));\n }\n`}","https://github.com/conductor-sdk/orkes-java-springboot2-example/blob/main/src/main/java/io/orkes/example/banking/controller/BankingApiController.java---1-lines":"#L32-L37","https://github.com/conductor-sdk/orkes-java-springboot2-example/blob/main/src/main/java/io/orkes/example/banking/workers/PollUntilConditionMeetsWorker.java---1":"{` @Override\n public TaskResult execute(Task task) {\n TaskResult taskResult = new TaskResult(task);\n if (!task.getInputData().containsKey(POLL_COUNTER)) {\n taskResult.addOutputData(\"message\", \"pollCounter param not found in input, will use default of \" + defaultPollCount + \" polls\");\n }\n\n int pollCounter = Math.min(10, castToInt(task.getInputData().getOrDefault(POLL_COUNTER, defaultPollCount)));\n int pollIntervalSeconds = Math.min(10, castToInt(task.getInputData().getOrDefault(POLL_INTERVAL_SECONDS, 5)));\n\n // Add these to the output for context\n taskResult.addOutputData(POLL_INTERVAL_SECONDS, pollIntervalSeconds + \" (this test task has a max limit of 10 seconds)\");\n taskResult.addOutputData(POLL_COUNTER, pollCounter + \" (this test task has a max limit of 10 iterations)\");\n\n // We can read current iteration from the task output as the data will be retained on the worker when polled\n int currentIteration = castToInt(taskResult.getOutputData().getOrDefault(CURRENT_ITERATION, 0));\n\n // Increment the current iteration and set to the task output\n taskResult.addOutputData(CURRENT_ITERATION, ++currentIteration);\n taskResult.addOutputData(\"updatedTime\", new Date().toString());\n\n // While condition is not met, keep task in progress\n if (currentIteration < pollCounter) {\n taskResult.setStatus(TaskResult.Status.IN_PROGRESS);\n // Set to configured seconds to callback, and you can set this to any value as per the requirements\n taskResult.setCallbackAfterSeconds(pollIntervalSeconds);\n return taskResult;\n }\n\n // Set task as completed now that the poll count condition is met\n taskResult.setStatus(TaskResult.Status.COMPLETED);\n return taskResult;\n }`}","https://github.com/conductor-sdk/orkes-java-springboot2-example/blob/main/src/main/java/io/orkes/example/banking/workers/PollUntilConditionMeetsWorker.java---1-lines":"#L24-L56"} \ No newline at end of file diff --git a/docs/templates/examples/scanning-an-endpoint-and-triggering-pagerduty-alert.md b/docs/templates/examples/scanning-an-endpoint-and-triggering-pagerduty-alert.md index f5000429..9b4d5bcf 100644 --- a/docs/templates/examples/scanning-an-endpoint-and-triggering-pagerduty-alert.md +++ b/docs/templates/examples/scanning-an-endpoint-and-triggering-pagerduty-alert.md @@ -5,20 +5,31 @@ import CodeBlock from '@theme/CodeBlock'; # Alerting using PagerDuty with Conductor Workflows -As we know, Netflix Conductor (a.k.a Orkes Conductor) is a power orchestration engine. In this example, we can look at how to scan an endpoint and trigger an alert if required. This needs to be done periodically. Orkes Conductor has the features to support this, and we can explore how to achieve this in this article. +As we know, Netflix Conductor (a.k.a Orkes Conductor) is a powerful orchestration engine. -## Requirements +In this article we are looking at implementing a use case: + +1. Scan or poll and endpoint periodically +2. Depending on the endpoint response, fire an alert on system like PagerDuty or OpsGenie -Let's say we have an endpoint that lets us search for data. This endpoint supports a timestamp-based search by specifying the from-and-to window. Using this API, we can search for specific scenarios, such as failures that may have happened during a time window, and then whenever the API returns any records, we have to call our support engineers via an alerting tool like PagerDuty. PagerDuty is a 3rd party system that can help with alerting users and supports features such as on-call schedules, escalations, etc. +## Requirements -In this example, we are using PagerDuty, but in reality, we can connect to any system that offers an endpoint-based integration, often called Webhooks. Similar alerting systems are OpsGenie, Squadcast, Datadog, etc. +Let's say we have an endpoint that lets us search for data. And assume this endpoint supports a timestamp-based search +by specifying a time window. As part of monitoring the application, we need to poll this endpoint periodically and +whenever the endpoint call returns any records, we have to call our support engineers via +an alerting tool like PagerDuty. +PagerDuty is a 3rd party system that can help with alerting users and supports features +such as on-call schedules, escalations, etc. -## Alert Design +In this example, we are using PagerDuty, but we can connect to any system that offers an API based +integration, often called Webhooks. Similar alerting systems are OpsGenie, Squadcast, Datadog, etc. -To scan for failures, we need to poll this endpoint every minute and look for failure instances that may have occurred during a window. This window is a sliding window - so we can index it for the scheduled kick-off time. Say, for example - ( now - 10 minutes) or (now - 5 minutes). This is effectively the last 10 mins or the last 5 mins. +## Alert Design -Once we detect records, we need to be able to make the integration webhook calls into the 3rd party systems to trigger the required alerts. +To scan for failures, we need to poll this endpoint every minute and look for failure instances that may have occurred +during a window. This window is a sliding window - so we can index it for the scheduled kick-off time. Say, for +example - ( now - 10 minutes) or (now - 5 minutes). Here is the design diagram for this alert: @@ -26,39 +37,170 @@ Here is the design diagram for this alert: ## Workflows for Alerts -As you know, we can replicate whiteboard-based flows in Conductor almost as is. The following definition shows how the above logic is implemented. +As you know, we can replicate whiteboard-based flows in Conductor almost as is. The following workflow definition shows +how the above logic is implemented.

Alert Workflow

| [View in Playground](https://play.orkes.io/workflowDef/track_workflow_failures) | -|--------------------------------------------------------------------------------------------------| +|---------------------------------------------------------------------------------| + + +```json lines +{ + "name": "track_workflow_failures", + "description": "Track Workflow Failures", + "version": 1, + "tasks": [ + { + "name": "compute_time_windows", + "taskReferenceName": "compute_time_windows", + "inputParameters": { + "expression": "(function() { \n let now = new Date();\n // Earlier start time for testing\n let fromTime = new Date(now.getTime() - ($.nowMinusStartMinutes * 60 * 1000)).getTime();\n let toTime = new Date(now.getTime() - ($.nowMinusEndMinutes * 60 * 1000)).getTime();\n return {\n fromTime: fromTime,\n toTime: toTime\n };\n})();\n", + "evaluatorType": "graaljs", + "nowMinusStartMinutes": "${workflow.input.nowMinusStartMinutes}", + "nowMinusEndMinutes": "${workflow.input.nowMinusEndMinutes}" + }, + "type": "INLINE" + }, + { + "name": "data_query", + "taskReferenceName": "data_query", + "inputParameters": { + "uri": "http://conductor-app.uidev-owkpy-ns.svc.cluster.local:8080/api/workflow/search?start=0&size=20&sort=startTime:DESC&freeText=*&query=workflowType IN (workflowDotMap) AND status IN (FAILED) AND startTime>${compute_time_windows.output.result.fromTime} AND startTime<${compute_time_windows.output.result.toTime}", + "method": "GET", + "accept": "application/json", + "contentType": "application/json", + "headers": { + "X-Authorization": "${workflow.secrets.api_token_failure_tracker}" + }, + "outputFilter": { + "resultCount": "$${data_query.output.response.body.results.length()}" + } + }, + "type": "HTTP" + }, + { + "name": "has_failure_workflows", + "taskReferenceName": "has_failure_workflows", + "inputParameters": { + "resultCount": "${data_query.output.resultCount}" + }, + "type": "SWITCH", + "defaultCase": [], + "decisionCases": { + "true": [ + { + "name": "inform_on_slack", + "taskReferenceName": "inform_on_slack", + "inputParameters": { + "uri": "https://orkes-api-tester.orkesconductor.com/api", + "method": "GET", + "accept": "application/json", + "contentType": "application/json", + "body": "Mock slack API" + }, + "type": "HTTP" + }, + { + "name": "call_ops_genie", + "taskReferenceName": "call_ops_genie", + "inputParameters": { + "uri": "https://orkes-api-tester.orkesconductor.com/api", + "method": "GET", + "accept": "application/json", + "contentType": "application/json", + "body": "Mock OpsGenie API" + }, + "type": "HTTP" + } + ] + }, + "evaluatorType": "graaljs", + "expression": "(function () { return $.resultCount === null || $.resultCount > 0; })();" + } + ], + "inputParameters": [ + "nowMinusEndMinutes", + "nowMinusStartMinutes" + ], + "schemaVersion": 2 +} +``` -We can try to run this against Conductor itself. When we run workflows using Conductor, one of the requirements is that we need to track failures. So, in this example, that is what we are doing. We are looking to find failure instances of the workflow **sample_tracker_workflow** on the playground environment of Orkes Conductor in a 10-minute window. +In this example, we are looking to find failure instances of the workflow **sample_tracker_workflow** on the playground +environment of Orkes Conductor in a 10-minute window. + +An example payload for pagerduty integration could look like: + +Endpoint: `POST https://events.pagerduty.com/v2/enqueue` + +```json +{ + "payload": { + "summary": "My issue summary", + "severity": "critical", + "source": "Orkes Conductor", + "component": "my-component", + "group": "my-group", + "class": "my-class", + "custom_details": { + "additional-context-1": "arbitrary string 1", + "additional-context-2": "arbitrary string 2" + } + }, + "routing_key": "${workflow.secrets.orkes_pagerduty_integration_key}", + "event_action": "trigger", + "client": "my client", + "client_url": "https://my-url", + "links": [ + { + "href": "https://my-cluster.orkesconductor.io/execution/${workflow.workflowId}", + "text": "Workflow List" + } + ], + "images": [] +} +``` :::tip -We are using a JavaScript task called [INLINE](https://orkes.io/content/reference-docs/system-tasks/inline) to compute the start and end times for making the API query to Conductor. We can have any custom logic in here to customize for our alerting requirements. +We are using a JavaScript task called [INLINE](https://orkes.io/content/reference-docs/system-tasks/inline) to compute +the start and end times for making the API query to Conductor. We can have any custom logic in here to customize for our +alerting requirements. ::: -A switch task with Javascript condition checks if the situation warrants an alert, and if yes, makes a webhook call to alert our users using Pagerduty. Voila! We have our alert configured! +A switch task with Javascript condition checks if the situation warrants an alert, and if yes, makes a webhook call to +alert our users using Pagerduty. Voilà! We have our alert configured! ## Configuring and Running Alerts Of course, we are not done yet, as we also need to figure out a bunch of pending things: -1. Managing the API token to make the API calls into Conductor. -2. Scheduling the alert workflow to run at the per-minute interval. +1. Managing the API token to make the API calls into Conductor +2. Scheduling the alert workflow to run at the per-minute interval +3. Managing API secrets for PagerDuty or OpsGenie ### Managing API tokens -Invoking an endpoint to scan for failures will often involve some sort of authorization. And this authorization will typically be using some token that may expire. Here, we look at a similar scenario and configure a secret that will hold the API token to query Conductor. This [article](/content/templates/examples/rotating-secrets-that-expire) describes how we can maintain that token by periodically refreshing it. +Invoking an endpoint to scan for failures will often involve some sort of authorization. And this authorization will +typically be using some token that may expire. Here, we look at a similar scenario and configure a secret that will hold +the API token to query Conductor. This [article](/content/templates/examples/rotating-secrets-that-expire) describes how +we can maintain that token by periodically refreshing it. ### Scheduling Alerts -For scheduling the alerts, you can leverage the Workflow Scheduler feature in Orkes Conductor. This lets the workflow at the chosen cadence thus automating the alerting process. +For scheduling the alerts, you can leverage the Workflow Scheduler feature in Orkes Conductor. This lets the workflow at +the chosen cadence thus automating the alerting process. 1. Navigate to **Definitions > Scheduler** on your Orkes Conductor console. 2. Click **Define schedule**. -3. [Create a scheduler](/content/developer-guides/scheduling-workflows#creating-schedule) using the following cron expression and choose the alert workflow you’ve created: +3. [Create a scheduler](/content/developer-guides/scheduling-workflows#creating-schedule) using the following cron + expression and choose the alert workflow you’ve created: + +### Managing API secrets + +Similar to how we maintain API tokens, we can maintain the API keys / secrets for 3rd party systems as a secret, and it +can be referred in your workflows. ``` 0 * * ? * *