Skip to content

Commit

Permalink
feat: ecs dev deployments (#192)
Browse files Browse the repository at this point in the history
  • Loading branch information
jbrunton authored Sep 7, 2024
1 parent 006e9eb commit 50b8a7a
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 16 deletions.
54 changes: 53 additions & 1 deletion .github/workflows/deploy-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,28 @@ jobs:
needs: [build-api]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: |
echo "CURRENT_TASK_DEFINITION=${{ runner.temp }}/currentTaskDefinition.json" >> $GITHUB_ENV
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1

- name: Build Client
env:
VITE_API_URL: https://chat-demo-api-${{ github.head_ref || github.ref_name }}.dev.jbrunton-aws.com
run: |
pnpm --filter client install
pnpm --filter client run build
- run: pnpm install --ignore-workspace
working-directory: pulumi

- uses: pulumi/actions@v3
with:
command: up
Expand All @@ -54,3 +66,43 @@ jobs:
AWS_REGION: us-east-1
API_TAG: ${{ needs.build-api.outputs.tag }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Get pulumi outputs
id: pulumi-outputs
run: |
pulumi stack select ${{ github.head_ref || github.ref_name }}
echo "cluster=$(pulumi stack output cluster)" > $GITHUB_OUTPUT
echo "service=$(pulumi stack output apiService)" > $GITHUB_OUTPUT
echo "taskDefinitionArn=$(pulumi stack output apiTaskDefinitionArn)" > $GITHUB_OUTPUT
working-directory: pulumi
env:
PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}

- name: Get current task definition
id: current-task-def
run: |
export TASK_DEFINITION_FILE=${{ runner.temp }}/taskDefinition.json
aws ecs describe-task-definition --task-definition $TASK_DEFINITION_ARN \
--query taskDefinition --output json \
| jq 'del(.revision, .compatibilities, .taskDefinitionArn, .requiresAttributes, .status, .registeredAt, .registeredBy)' \
> $TASK_DEFINITION_FILE
echo "taskDefinitionFile=$TASK_DEFINITION_FILE" > $GITHUB_OUTPUT
env:
TASK_DEFINITION_ARN: ${{ steps.pulumi-outputs.outputs.taskDefinitionArn }}
working-directory: pulumi

- name: Render new task definition
id: new-task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: ${{ steps.current-task-def.outputs.taskDefinitionFile }}
container-name: chat-demo-api
image: ${{ needs.build-api.outputs.tag }}

- name: Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
with:
task-definition: ${{ steps.new-task-def.outputs.task-definition }}
service: ${{ steps.pulumi-outputs.outputs.service }}
cluster: ${{ steps.pulumi-outputs.outputs.cluster }}
wait-for-service-stability: true
17 changes: 14 additions & 3 deletions pulumi/app/aws/apply-service-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@ import * as pulumi from "@pulumi/pulumi";
import { getTaskDefinitionSpec } from "./get-app-spec";
import { SharedResources } from "./usecases/stack/get-shared-resources";
import { Cluster } from "@pulumi/aws/ecs";
import { Output } from "@pulumi/pulumi";

export const applyServiceConfig = (
stackConfig: StackConfig,
serviceConfig: StackConfig["services"][0],
shared: SharedResources,
cluster: Cluster,
provider: aws.Provider
) => {
): {
serviceName: Output<string>;
taskDefinitionArn: Output<string>;
} => {
const resourceName = `${stackConfig.appName}-${serviceConfig.name}`;

// Pulumi sometimes adds `-` + 7 random chars for unique names.
Expand Down Expand Up @@ -150,7 +154,7 @@ export const applyServiceConfig = (
writeCapacity: 1,
});

pulumi
const outputs = pulumi
.all([webLogGroup.name, taskExecutionRole.arn, taskRole.arn, table.name])
.apply(([logGroupName, executionRoleArn, taskRoleArn, tableName]) => {
const taskDefinitionSpec = getTaskDefinitionSpec({
Expand All @@ -166,7 +170,7 @@ export const applyServiceConfig = (
taskDefinitionSpec
);

new aws.ecs.Service(stackConfig.appName, {
const service = new aws.ecs.Service(stackConfig.appName, {
cluster: cluster.arn,
desiredCount: 1,
launchType: "FARGATE",
Expand All @@ -184,6 +188,11 @@ export const applyServiceConfig = (
},
],
});

return {
taskDefinitionArn: taskDefinition.arn,
serviceName: service.name,
};
});

const lb = aws.lb.getLoadBalancerOutput({ arn: shared.loadBalancer.arn });
Expand All @@ -208,4 +217,6 @@ export const applyServiceConfig = (
},
{ provider }
);

return outputs;
};
49 changes: 42 additions & 7 deletions pulumi/app/aws/apply-stack-config.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,62 @@
import { ApplyStackConfig, StackConfig } from "@entities";
import { ApplyStackConfig, ApplyStackResult, StackConfig } from "@entities";
import * as aws from "@pulumi/aws";
import {
getSharedResources,
SharedResources,
} from "./usecases/stack/get-shared-resources";
import { applyClientConfig } from "./apply-client-config";
import { applyServiceConfig } from "./apply-service-config";
import { Output } from "@pulumi/pulumi";

const provider = new aws.Provider("aws", { region: "us-east-1" });

export const applyStackConfig: ApplyStackConfig = (config: StackConfig) => {
getSharedResources().apply((shared) => createResources(config, shared));
type Result = ApplyStackResult<Output<unknown>>;

export const applyStackConfig: ApplyStackConfig<Output<unknown>> = (
config: StackConfig
): Result => {
const result: Result = getSharedResources().apply((shared) =>
createResources(config, shared)
);
return result;
};

function createResources(stackConfig: StackConfig, shared: SharedResources) {
function createResources(
stackConfig: StackConfig,
shared: SharedResources
): Result {
// Pulumi adds `-` + 7 random chars for unique names.
const shortName = stackConfig.appName.slice(0, 24);

const cluster = new aws.ecs.Cluster(shortName, undefined, { provider });

applyClientConfig(stackConfig, shared);

stackConfig.services.forEach((serviceConfig) => {
applyServiceConfig(stackConfig, serviceConfig, shared, cluster, provider);
});
const outputs: Record<string, Output<unknown>> = stackConfig.services
.map((serviceConfig) => {
return [
serviceConfig.name,
applyServiceConfig(
stackConfig,
serviceConfig,
shared,
cluster,
provider
),
] as [string, ReturnType<typeof applyServiceConfig>];
})
.reduce((prev, [name, outputs]) => {
return {
...prev,
[`${name}TaskDefinitionArn`]: outputs.taskDefinitionArn,
[`${name}Service`]: outputs.serviceName,
};
}, {});

return {
outputs: {
...outputs,
cluster: cluster.name,
},
};
}
8 changes: 6 additions & 2 deletions pulumi/domain/entities/stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ export interface GetStackConfig {
(inputs: ApplicationInputs): StackConfig;
}

export interface ApplyStackConfig {
(config: StackConfig): void;
export interface ApplyStackResult<T> {
outputs: Record<string, T>;
}

export interface ApplyStackConfig<T> {
(config: StackConfig): ApplyStackResult<T>;
}
9 changes: 6 additions & 3 deletions pulumi/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ new pulumiservice.StackTag("stack-tags", {
value: appConfig.environment,
});

applyStackConfig(stackConfig);
const result = applyStackConfig(stackConfig);

export const publicUrl = stackConfig.client.publicUrl;
export const publicApiUrl = stackConfig.services[0].publicUrl;
export const webUrl = stackConfig.client.publicUrl;
export const apiUrl = stackConfig.services[0].publicUrl;
export const apiTaskDefinitionArn = result.outputs["apiTaskDefinitionArn"];
export const apiService = result.outputs["apiService"];
export const cluster = result.outputs["cluster"];

0 comments on commit 50b8a7a

Please sign in to comment.