Skip to content

Commit e01d877

Browse files
authored
Merge pull request #23 from sonikro/optional-subnet-ids
feat(subnetIds): made subnet-ids optional
2 parents 38744ab + bc395ef commit e01d877

File tree

8 files changed

+148
-25
lines changed

8 files changed

+148
-25
lines changed

.github/workflows/test.yml

+21
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,24 @@ jobs:
6262
terraform -v
6363
echo "Hello World. I am ${{github.repository}}"
6464
echo "Testing $GITHUB_REPOSITORY variable"
65+
test-auto-subnet-ids:
66+
runs-on: ubuntu-latest
67+
if: github.repository_owner == 'sonikro'
68+
permissions:
69+
contents: read
70+
id-token: write
71+
steps:
72+
- uses: actions/checkout@v3
73+
74+
- uses: ./
75+
with:
76+
role_arn: "${{secrets.TEST_ROLE_ARN}}"
77+
image: hashicorp/terraform:latest
78+
region: us-east-1
79+
vpc_id: "${{secrets.TEST_VPC_ID}}"
80+
shell: sh
81+
run: |
82+
ls -la
83+
terraform -v
84+
echo "Hello World. I am ${{github.repository}}"
85+
echo "Testing $GITHUB_REPOSITORY variable"

README.md

+35-9
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,12 @@ The policy attattched to this role must have at least these permissions:
9191
"logs:GetLogEvents",
9292
"s3:*",
9393
"ec2:DescribeSecurityGroups",
94-
"ec2:DescribeSecurityGroupRules",
95-
"ec2:AuthorizeSecurityGroupEgress",
96-
"ec2:CreateSecurityGroup",
97-
"ec2:AuthorizeSecurityGroupIngress",
98-
"ec2:DeleteSecurityGroup"
94+
"ec2:DescribeSecurityGroupRules",
95+
"ec2:AuthorizeSecurityGroupEgress",
96+
"ec2:CreateSecurityGroup",
97+
"ec2:AuthorizeSecurityGroupIngress",
98+
"ec2:DeleteSecurityGroup",
99+
"ec2:DescribeSubnets"
99100
],
100101
"Resource": "*"
101102
},
@@ -119,7 +120,6 @@ The policy attattched to this role must have at least these permissions:
119120

120121
#### Easiest way to get started
121122

122-
123123
```yaml
124124
jobs:
125125
terraform:
@@ -136,8 +136,6 @@ jobs:
136136
image: hashicorp/terraform:latest
137137
region: us-east-1
138138
vpc_id: "${{secrets.VPC_ID}}"
139-
subnet_ids: |
140-
${{secrets.SUBNBET_ID}}
141139
shell: sh
142140
run: |
143141
terraform apply
@@ -171,6 +169,34 @@ jobs:
171169
terraform apply
172170
```
173171
172+
#### Using specific subnet ids
173+
174+
If you want your task to run on specific subnets, use the **subnet_ids** argument
175+
176+
```yaml
177+
jobs:
178+
terraform:
179+
runs-on: ubuntu-latest
180+
permissions:
181+
contents: read
182+
id-token: write
183+
steps:
184+
- uses: actions/checkout@v3
185+
186+
- uses: sonikro/aws-run@v1
187+
with:
188+
role_arn: "${{secrets.ROLE_ARN}}"
189+
image: hashicorp/terraform:latest
190+
region: us-east-1
191+
vpc_id: "${{secrets.VPC_ID}}"
192+
security_group_id: "<SECURITY_GROUP_ID>"
193+
subnet_ids: |
194+
${{secrets.SUBNBET_ID}}
195+
shell: sh
196+
run: |
197+
terraform apply
198+
```
199+
174200
> id-token: write is required in order to authenticate to AWS using OIDC
175201
176202
This example would run a container with image **hashicorp/terraform:latest** in your AWS Account, connected to the specified VPC_ID, SUBNET_ID nd SECURITY_GROUP_ID. All contents of the GitHub Runner Workspace are copied to the remote container, meaning that the remote shell has access to all of the files of the host runner.
@@ -214,7 +240,7 @@ In the execution phase, the action will:
214240
- [X] Allow multiple Subnet IDs
215241
- [X] Stream the Cloudwatch logs as they happen, and not just at the end of the execution
216242
- [X] Automatically create temporary security group if one is not provided
217-
- [ ] Automatically grab list of Subnets for VPC_ID, if Subnet_IDS are not provided
243+
- [X] Automatically grab list of Subnets for VPC_ID, if Subnet_IDS are not provided
218244
- [ ] Mask secrets inside the Cloudwatch Logs
219245
- [X] Map all GitHub Contexts/ENVS into the ECS Container
220246
- [ ] Ability to upload artifacts back to GitHub (if your remote execution generates artifacts)

action.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ inputs:
4040
required: true
4141
description: Name of the shell to be used in the container to execute the run script
4242
subnet_ids:
43-
required: true
44-
description: Subnet ID of where the Task will be executed.
43+
required: false
44+
description: Subnet ID of where the Task will be executed. If no subnet_ids is specified, the task will find one automatically within the VPC
4545
vpc_id:
4646
required: true
4747
description: VPC ID of where the Task will be executed

dist/index.js

+25-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/main.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ async function run(): Promise<void> {
2020
const runScript: string = core.getInput('run')
2121
const vpcId: string = core.getInput('vpc_id')
2222
const subnetIds: string[] = core
23-
.getInput('subnet_ids')
23+
.getInput('subnet_ids', {required: false})
2424
.split('\n')
2525
.map(s => s.trim())
2626
.filter(x => x !== '')

src/providers/remoteEnvironments/AWSECSRemoteEnvironment.test.ts

+31-1
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,16 @@ describe('AWSECSRemoteEnvironment', () => {
233233
return callback(null, {})
234234
})
235235
AWSMock.mock(`EC2`, `deleteSecurityGroup`, deleteSecurityGroup)
236+
237+
const mockedDescribeSubnetResult = mock<
238+
PromiseResult<EC2.DescribeSubnetsResult, AWSError>
239+
>({
240+
Subnets: [{SubnetId: 'subnet1'}, {SubnetId: 'subnet2'}]
241+
})
242+
const describeSubnets = jest.fn().mockImplementation((input, callback) => {
243+
callback(null, mockedDescribeSubnetResult)
244+
})
245+
AWSMock.mock('EC2', 'describeSubnets', describeSubnets)
236246
return {
237247
Sut: AWSECSRemoteEnvironment,
238248
region,
@@ -248,11 +258,13 @@ describe('AWSECSRemoteEnvironment', () => {
248258
mockedCreateClusterResult,
249259
mockedDescribeClusterResult,
250260
mockedEcsCluster,
261+
mockedDescribeSubnetResult,
251262
deregisterTaskDefinition,
252263
deleteLogStream,
253264
deleteBucket,
254265
deleteObject,
255-
deleteSecurityGroup
266+
deleteSecurityGroup,
267+
describeSubnets
256268
}
257269
}
258270

@@ -317,6 +329,24 @@ describe('AWSECSRemoteEnvironment', () => {
317329
// Then
318330
expect(receivedResult.exitCode).toBe(0)
319331
}, 10000)
332+
333+
it('correctly finds all subnetIds, if no subnetId is provided', async () => {
334+
// Given
335+
const {Sut, region, roleArn, webIdentityToken, ecsExecutionSettings} =
336+
makeSut({subnetIds: []})
337+
// When
338+
const awsEcsRemoteEnvironment = await Sut.fromGithubOidc({
339+
region,
340+
roleArn,
341+
webIdentityToken
342+
})
343+
344+
const receivedResult = await awsEcsRemoteEnvironment.execute({
345+
settings: ecsExecutionSettings
346+
})
347+
// Then
348+
expect(receivedResult.exitCode).toBe(0)
349+
}, 10000)
320350
})
321351

322352
describe('tearDown', () => {

src/providers/remoteEnvironments/AWSECSRemoteEnvironment.ts

+32-6
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,13 @@ export class AWSECSRemoteEnvironment
130130
})
131131
console.log(`Starting ECS Task`)
132132
// Start Remote Execution
133+
const subnetIds = await this.findSubnetIds({settings})
134+
133135
const executionTask = await this.startTask({
134136
ecsCluster,
135-
settings,
136137
taskDefinition,
137-
securityGroupId
138+
securityGroupId,
139+
subnetIds
138140
})
139141
console.log(`Waiting until ECS Task is running`)
140142
await ecs
@@ -169,6 +171,30 @@ export class AWSECSRemoteEnvironment
169171
s3WorkspaceBucket: s3Workspace
170172
}
171173
}
174+
async findSubnetIds({
175+
settings
176+
}: {
177+
settings: ECSExecutionSettings
178+
}): Promise<string[]> {
179+
const {ec2} = this.dependencies
180+
if (settings.subnetIds.length > 0) {
181+
return settings.subnetIds
182+
}
183+
184+
const allSubnets = await ec2
185+
.describeSubnets({
186+
Filters: [
187+
{
188+
Name: 'vpc-id',
189+
Values: [settings.vpcId]
190+
}
191+
]
192+
})
193+
.promise()
194+
195+
return allSubnets.Subnets!.map(it => it.SubnetId!)
196+
}
197+
172198
async setupSecurityGroup({
173199
settings
174200
}: {
@@ -328,13 +354,13 @@ export class AWSECSRemoteEnvironment
328354
protected async startTask({
329355
taskDefinition,
330356
ecsCluster,
331-
settings,
332-
securityGroupId
357+
securityGroupId,
358+
subnetIds
333359
}: {
334360
taskDefinition: ECS.TaskDefinition
335361
ecsCluster: ECS.Cluster
336-
settings: ECSExecutionSettings
337362
securityGroupId: string
363+
subnetIds: string[]
338364
}): Promise<ECS.Task> {
339365
const {ecs} = this.dependencies
340366
const task = await ecs
@@ -345,7 +371,7 @@ export class AWSECSRemoteEnvironment
345371
networkConfiguration: {
346372
awsvpcConfiguration: {
347373
assignPublicIp: 'ENABLED',
348-
subnets: settings.subnetIds,
374+
subnets: subnetIds,
349375
securityGroups: [securityGroupId]
350376
}
351377
},

0 commit comments

Comments
 (0)