feat(eks): Add scheduled deployment approval workflow - Phase 1#16
feat(eks): Add scheduled deployment approval workflow - Phase 1#16
Conversation
Implement TypeScript handlers for EKS cluster scheduled deployment with Slack approval workflow. **New Features:** - Scheduled deployment request handler (EventBridge → DynamoDB → Slack) - Slack interactive button handler (Approve/Deny) - Approval retry worker (15-minute interval reminders) - Extended deploy worker to handle both manual and scheduled events **New Files:** - src/shared/dynamodb-client.ts: DynamoDB operations for deployment state - src/shared/slack-blocks.ts: Slack Block Kit templates (English) - src/workers/deploy/scheduled-handler.ts: Creates approval requests - src/workers/deploy/manual-handler.ts: Manual deployment logic (refactored) - src/workers/approval-retry/index.ts: Retry reminder logic - src/interactive/index.ts: Slack button click handler **Modified Files:** - src/workers/deploy/index.ts: Route to scheduled vs manual handler **Architecture:** EventBridge Schedule → Deploy Worker → DynamoDB → Slack Approval → Interactive Handler → EventBridge Event → (Phase 2: Step Functions) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
| } | ||
|
|
||
| // Get Slack credentials | ||
| const slackBotToken = await getParameter('/laco/plt/aws/secrets/slack/bot-token'); |
Check failure
Code scanning / CodeQL
Invocation of non-function Error
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 2 months ago
In general, to fix this type of problem you must ensure that any value you call like a function is actually a function: either correct the import/export so it refers to a real function, or add a type/value guard before invoking it. Since we are not allowed to modify the ../../shared/secrets module here, the fix must be local to this file.
The best localized fix that preserves existing behavior is:
- Before calling
getParameter, check at runtime that it is a function. - If it is not, log a clear error and throw, preventing the worker from continuing with an invalid Slack configuration.
- If it is, proceed as before with
await getParameter('/laco/plt/aws/secrets/slack/bot-token').
Concretely, in applications/chatops/slack-bot/src/workers/approval-retry/index.ts, around line 34, replace the direct getParameter(...) call with a short guard:
if (typeof getParameter !== 'function') {
throw new Error('Slack bot token secret getter is not configured correctly (getParameter is not a function)');
}
const slackBotToken = await getParameter('/laco/plt/aws/secrets/slack/bot-token');No new imports or additional helpers are required; we reuse the existing Error type and existing error handling in the outer try/catch.
| @@ -31,6 +31,9 @@ | ||
| } | ||
|
|
||
| // Get Slack credentials | ||
| if (typeof getParameter !== 'function') { | ||
| throw new Error('Slack bot token secret getter is not configured correctly (getParameter is not a function)'); | ||
| } | ||
| const slackBotToken = await getParameter('/laco/plt/aws/secrets/slack/bot-token'); | ||
| const slackChannelId = process.env.SLACK_CHANNEL_ID || 'C06XXXXXXXXX'; | ||
|
|
| logger.info('Deployment request created', { request_id }); | ||
|
|
||
| // Send Slack approval message | ||
| const slackBotToken = await getParameter('/laco/plt/aws/secrets/slack/bot-token'); |
Check failure
Code scanning / CodeQL
Invocation of non-function Error
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 2 months ago
In general, this issue occurs when something that is not a function (often undefined) is invoked with (...). Here, the imported getParameter symbol is reported as non-callable. The most probable root cause is that ../../shared/secrets does not export a callable named getParameter, but instead exports a different function (for example getParameterValue) or a default export. To fix this without changing behavior, we should import and use the correct function from ../../shared/secrets and stop attempting to call a non-function symbol.
Concretely, in applications/chatops/slack-bot/src/workers/deploy/scheduled-handler.ts, replace the named import getParameter with the correct function name that’s actually exported as a function from ../../shared/secrets. Since we cannot see that file, the minimal, safe change within this snippet is to align with a likely, conventional name such as getParameterValue and call that instead. This removes the invocation of the non-function symbol and instead invokes the real function. We only need to adjust the import line (line 9) and the call site on line 55; no other logic or data flow in this handler needs to change.
| @@ -6,7 +6,7 @@ | ||
| import { createDeploymentRequest } from '../../shared/dynamodb-client'; | ||
| import { createApprovalMessage } from '../../shared/slack-blocks'; | ||
| import axios from 'axios'; | ||
| import { getParameter } from '../../shared/secrets'; | ||
| import { getParameterValue } from '../../shared/secrets'; | ||
|
|
||
| interface ScheduledDeploymentEvent { | ||
| deployment_type: 'create_cluster' | 'delete_cluster'; | ||
| @@ -52,7 +52,7 @@ | ||
| logger.info('Deployment request created', { request_id }); | ||
|
|
||
| // Send Slack approval message | ||
| const slackBotToken = await getParameter('/laco/plt/aws/secrets/slack/bot-token'); | ||
| const slackBotToken = await getParameterValue('/laco/plt/aws/secrets/slack/bot-token'); | ||
| const slackChannelId = process.env.SLACK_CHANNEL_ID || 'C06XXXXXXXXX'; | ||
|
|
||
| const approvalMessage = createApprovalMessage(deploymentRequest); |
| }); | ||
|
|
||
| // Verify Slack signature | ||
| if (!verifySlackSignature(event)) { |
Check warning
Code scanning / CodeQL
Missing await Warning
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 2 months ago
In general, when calling an async function whose resolved value is needed for a conditional, you must either await it or use .then to access the resolved value. Here, verifySlackSignature appears to return a promise that resolves to a boolean indicating whether the request is valid. The if statement is meant to short-circuit and return 401 for invalid signatures, so the correct behavior is to await the verification result before checking it.
The single best fix is to insert await before verifySlackSignature(event) inside the if condition in handler. This keeps the structure and behavior the same while ensuring the condition operates on the boolean result rather than on the promise object. No additional imports are required, and there is no need to change the signature of handler since it is already async. Concretely, in applications/chatops/slack-bot/src/interactive/index.ts, update the if on line 24 from if (!verifySlackSignature(event)) { to if (!(await verifySlackSignature(event))) {.
| @@ -21,7 +21,7 @@ | ||
| }); | ||
|
|
||
| // Verify Slack signature | ||
| if (!verifySlackSignature(event)) { | ||
| if (!(await verifySlackSignature(event))) { | ||
| logger.warn('Invalid Slack signature'); | ||
| return { | ||
| statusCode: 401, |
Summary
Implement TypeScript handlers for EKS cluster scheduled deployment with Slack approval workflow (Phase 1).
New Features
New Files
src/shared/dynamodb-client.ts: DynamoDB operations for deployment statesrc/shared/slack-blocks.ts: Slack Block Kit templates (English)src/workers/deploy/scheduled-handler.ts: Creates approval requestssrc/workers/deploy/manual-handler.ts: Manual deployment logic (refactored)src/workers/approval-retry/index.ts: Retry reminder logicsrc/interactive/index.ts: Slack button click handlerModified Files
src/workers/deploy/index.ts: Route to scheduled vs manual handlerArchitecture Flow
Testing
npm run buildnpm run type-checknpm run lintRelated PRs
Next Steps
Phase 2 will add:
🤖 Generated with Claude Code