Skip to content

Commit

Permalink
feat: Multiple security groups, no more default security SG, and imag…
Browse files Browse the repository at this point in the history
…e builder connections support (#171)

Relates to #163
Closes #165

BREAKING CHANGE: The default VPC security group will no longer be used, and instead a new security group will be created
BREAKING CHANGE: IRunnerProvider no longer exposes vpc and securityGroup
BREAKING CHANGE: IRunnerProviderStatus changed securityGroup to securityGroups
  • Loading branch information
kichik committed Dec 5, 2022
1 parent 38b74ca commit c3aebb1
Show file tree
Hide file tree
Showing 15 changed files with 1,007 additions and 276 deletions.
239 changes: 114 additions & 125 deletions API.md

Large diffs are not rendered by default.

40 changes: 26 additions & 14 deletions src/providers/codebuild.ts
Expand Up @@ -62,12 +62,21 @@ export interface CodeBuildRunnerProps extends RunnerProviderProps {
readonly vpc?: ec2.IVpc;

/**
* Security Group to assign to this instance.
* Security group to assign to this instance.
*
* @default public project with no security group
*
* @deprecated use {@link securityGroups}
*/
readonly securityGroup?: ec2.ISecurityGroup;

/**
* Security groups to assign to this instance.
*
* @default a new security group, if {@link vpc} is used
*/
readonly securityGroups?: ec2.ISecurityGroup[];

/**
* Where to place the network interfaces within the VPC.
*
Expand Down Expand Up @@ -137,16 +146,6 @@ export class CodeBuildRunner extends BaseProvider implements IRunnerProvider {
*/
readonly labels: string[];

/**
* VPC used for hosting the project.
*/
readonly vpc?: ec2.IVpc;

/**
* Security group attached to the task.
*/
readonly securityGroup?: ec2.ISecurityGroup;

/**
* Grant principal used to add permissions to the runner role.
*/
Expand All @@ -157,12 +156,25 @@ export class CodeBuildRunner extends BaseProvider implements IRunnerProvider {
*/
readonly image: RunnerImage;

private readonly vpc?: ec2.IVpc;
private readonly securityGroups?: ec2.ISecurityGroup[];

constructor(scope: Construct, id: string, props: CodeBuildRunnerProps) {
super(scope, id);

this.labels = this.labelsFromProperties('codebuild', props.label, props.labels);
this.vpc = props.vpc;
this.securityGroup = props.securityGroup;
if (props.securityGroup) {
this.securityGroups = [props.securityGroup];
} else {
if (props.securityGroups) {
this.securityGroups = props.securityGroups;
} else {
if (this.vpc) {
this.securityGroups = [new ec2.SecurityGroup(this, 'SG', { vpc: this.vpc })];
}
}
}

let buildSpec = {
version: '0.2',
Expand Down Expand Up @@ -237,7 +249,7 @@ export class CodeBuildRunner extends BaseProvider implements IRunnerProvider {
description: `GitHub Actions self-hosted runner for labels ${this.labels}`,
buildSpec: codebuild.BuildSpec.fromObject(buildSpec),
vpc: this.vpc,
securityGroups: this.securityGroup ? [this.securityGroup] : undefined,
securityGroups: this.securityGroups,
subnetSelection: props.subnetSelection,
timeout: props.timeout ?? Duration.hours(1),
environment: {
Expand Down Expand Up @@ -317,7 +329,7 @@ export class CodeBuildRunner extends BaseProvider implements IRunnerProvider {
type: this.constructor.name,
labels: this.labels,
vpcArn: this.vpc?.vpcArn,
securityGroup: this.securityGroup?.securityGroupId,
securityGroups: this.securityGroups?.map(sg => sg.securityGroupId),
roleArn: this.project.role?.roleArn,
image: {
imageRepository: this.image.imageRepository.repositoryUri,
Expand Down
14 changes: 2 additions & 12 deletions src/providers/common.ts
Expand Up @@ -349,9 +349,9 @@ export interface IRunnerProviderStatus {
readonly vpcArn?: string;

/**
* Security group attached to runners.
* Security groups attached to runners.
*/
readonly securityGroup?: string;
readonly securityGroups?: string[];

/**
* Role attached to runners.
Expand Down Expand Up @@ -382,16 +382,6 @@ export interface IRunnerProvider extends ec2.IConnectable, iam.IGrantable {
*/
readonly labels: string[];

/**
* VPC network in which runners will be placed.
*/
readonly vpc?: ec2.IVpc;

/**
* Security group associated with runners.
*/
readonly securityGroup?: ec2.ISecurityGroup;

/**
* Generate step function tasks that execute the runner.
*
Expand Down
28 changes: 18 additions & 10 deletions src/providers/ec2.ts
Expand Up @@ -159,10 +159,19 @@ export interface Ec2RunnerProps extends RunnerProviderProps {
/**
* Security Group to assign to launched runner instances.
*
* @default account's default security group
* @default a new security group
*
* @deprecated use {@link securityGroups}
*/
readonly securityGroup?: ec2.ISecurityGroup;

/**
* Security groups to assign to launched runner instances.
*
* @default a new security group
*/
readonly securityGroups?: ec2.ISecurityGroup[];

/**
* Subnet where the runner instances will be launched.
*
Expand Down Expand Up @@ -212,11 +221,6 @@ export class Ec2Runner extends BaseProvider implements IRunnerProvider {
*/
readonly labels: string[];

/**
* Security group attached to launched instances.
*/
readonly securityGroup?: ec2.ISecurityGroup;

/**
* Grant principal used to add permissions to the runner role.
*/
Expand All @@ -229,13 +233,16 @@ export class Ec2Runner extends BaseProvider implements IRunnerProvider {
private readonly storageSize: cdk.Size;
private readonly spot: boolean;
private readonly spotMaxPrice: string | undefined;
private readonly vpc: ec2.IVpc;
private readonly subnet?: ec2.ISubnet;
private readonly securityGroups: ec2.ISecurityGroup[];

constructor(scope: Construct, id: string, props: Ec2RunnerProps) {
super(scope, id);

this.labels = props.labels ?? ['ec2'];
this.securityGroup = props.securityGroup;
this.vpc = props.vpc ?? ec2.Vpc.fromLookup(this, 'Default VPC', { isDefault: true });
this.securityGroups = props.securityGroup ? [props.securityGroup] : (props.securityGroups ?? [new ec2.SecurityGroup(this, 'SG', { vpc: this.vpc })]);
this.subnet = props.subnet ?? props.vpc?.selectSubnets(props.subnetSelection).subnets[0];
this.instanceType = props.instanceType ?? ec2.InstanceType.of(ec2.InstanceClass.M5, ec2.InstanceSize.LARGE);
this.storageSize = props.storageSize ?? cdk.Size.gibibytes(30); // 30 is the minimum for Windows
Expand All @@ -245,6 +252,7 @@ export class Ec2Runner extends BaseProvider implements IRunnerProvider {
const amiBuilder = props.amiBuilder ?? new AmiBuilder(this, 'Image Builder', {
vpc: props.vpc,
subnetSelection: props.subnetSelection,
securityGroups: this.securityGroups,
});
this.ami = amiBuilder.bind();

Expand Down Expand Up @@ -334,7 +342,7 @@ export class Ec2Runner extends BaseProvider implements IRunnerProvider {
MetadataOptions: {
HttpTokens: 'required',
},
SecurityGroupIds: this.securityGroup ? [this.securityGroup.securityGroupId] : undefined,
SecurityGroupIds: this.securityGroups.map(sg => sg.securityGroupId),
SubnetId: this.subnet?.subnetId,
BlockDeviceMappings: [{
DeviceName: '/dev/sda1',
Expand Down Expand Up @@ -386,7 +394,7 @@ export class Ec2Runner extends BaseProvider implements IRunnerProvider {
return {
type: this.constructor.name,
labels: this.labels,
securityGroup: this.securityGroup?.securityGroupId,
securityGroups: this.securityGroups.map(sg => sg.securityGroupId),
roleArn: this.role.roleArn,
ami: {
launchTemplate: this.ami.launchTemplate.launchTemplateId || 'unknown',
Expand All @@ -399,6 +407,6 @@ export class Ec2Runner extends BaseProvider implements IRunnerProvider {
* The network connections associated with this resource.
*/
public get connections(): ec2.Connections {
return this.securityGroup?.connections ?? new ec2.Connections();
return new ec2.Connections({ securityGroups: this.securityGroups });
}
}
32 changes: 20 additions & 12 deletions src/providers/fargate.ts
Expand Up @@ -20,7 +20,8 @@ import {
Os,
RunnerImage,
RunnerProviderProps,
RunnerRuntimeParameters, RunnerVersion,
RunnerRuntimeParameters,
RunnerVersion,
} from './common';
import { CodeBuildImageBuilder } from './image-builders/codebuild';

Expand Down Expand Up @@ -69,12 +70,21 @@ export interface FargateRunnerProps extends RunnerProviderProps {
readonly subnetSelection?: ec2.SubnetSelection;

/**
* Security Group to assign to the task.
* Security group to assign to the task.
*
* @default a new security group
*
* @deprecated use {@link securityGroupss}
*/
readonly securityGroup?: ec2.ISecurityGroup;

/**
* Security groups to assign to the task.
*
* @default a new security group
*/
readonly securityGroups?: ec2.ISecurityGroup[];

/**
* Existing Fargate cluster to use.
*
Expand Down Expand Up @@ -160,7 +170,8 @@ interface EcsFargateLaunchTargetProps {
* Our special launch target that can use spot instances and set EnableExecuteCommand.
*/
class EcsFargateLaunchTarget implements stepfunctions_tasks.IEcsLaunchTarget {
constructor(readonly props: EcsFargateLaunchTargetProps) {}
constructor(readonly props: EcsFargateLaunchTargetProps) {
}

/**
* Called when the Fargate launch type configured on RunTask
Expand Down Expand Up @@ -240,11 +251,6 @@ export class FargateRunner extends BaseProvider implements IRunnerProvider {
*/
readonly subnetSelection?: ec2.SubnetSelection;

/**
* Security group attached to the task.
*/
readonly securityGroup?: ec2.ISecurityGroup;

/**
* Whether runner task will have a public IP.
*/
Expand All @@ -270,14 +276,16 @@ export class FargateRunner extends BaseProvider implements IRunnerProvider {
*/
readonly image: RunnerImage;

private readonly securityGroups: ec2.ISecurityGroup[];

constructor(scope: Construct, id: string, props: FargateRunnerProps) {
super(scope, id);

this.labels = this.labelsFromProperties('fargate', props.label, props.labels);
this.vpc = props.vpc ?? ec2.Vpc.fromLookup(this, 'default vpc', { isDefault: true });
this.subnetSelection = props.subnetSelection;
this.securityGroup = props.securityGroup ?? new ec2.SecurityGroup(this, 'security group', { vpc: this.vpc });
this.connections = this.securityGroup.connections;
this.securityGroups = props.securityGroup ? [props.securityGroup] : (props.securityGroups ?? [new ec2.SecurityGroup(this, 'security group', { vpc: this.vpc })]);
this.connections = new ec2.Connections({ securityGroups: this.securityGroups });
this.assignPublicIp = props.assignPublicIp ?? true;
this.cluster = props.cluster ? props.cluster : new ecs.Cluster(
this,
Expand Down Expand Up @@ -367,7 +375,7 @@ export class FargateRunner extends BaseProvider implements IRunnerProvider {
}),
subnets: this.subnetSelection,
assignPublicIp: this.assignPublicIp,
securityGroups: this.securityGroup ? [this.securityGroup] : undefined,
securityGroups: this.securityGroups,
containerOverrides: [
{
containerDefinition: this.container,
Expand Down Expand Up @@ -413,7 +421,7 @@ export class FargateRunner extends BaseProvider implements IRunnerProvider {
type: this.constructor.name,
labels: this.labels,
vpcArn: this.vpc?.vpcArn,
securityGroup: this.securityGroup?.securityGroupId,
securityGroups: this.securityGroups.map(sg => sg.securityGroupId),
roleArn: this.task.taskRole.roleArn,
image: {
imageRepository: this.image.imageRepository.repositoryUri,
Expand Down
15 changes: 12 additions & 3 deletions src/providers/image-builders/ami.ts
Expand Up @@ -60,12 +60,21 @@ export interface AmiBuilderProps {
readonly vpc?: ec2.IVpc;

/**
* Security Group to assign to launched builder instances.
* Security group to assign to launched builder instances.
*
* @default default account security group
* @default new security group
*
* @deprecated use {@link securityGroups}
*/
readonly securityGroup?: ec2.ISecurityGroup;

/**
* Security groups to assign to launched builder instances.
*
* @default new security group
*/
readonly securityGroups?: ec2.ISecurityGroup[];

/**
* Where to place the network interfaces within the VPC. Only the first matched subnet will be used.
*
Expand Down Expand Up @@ -219,7 +228,7 @@ export class AmiBuilder extends ImageBuilderBase implements IAmiBuilder {
supportedArchitectures: [Architecture.X86_64, Architecture.ARM64],
instanceType: props?.instanceType,
vpc: props?.vpc,
securityGroup: props?.securityGroup,
securityGroups: props?.securityGroup ? [props.securityGroup] : props?.securityGroups,
subnetSelection: props?.subnetSelection,
logRemovalPolicy: props?.logRemovalPolicy,
logRetention: props?.logRetention,
Expand Down
29 changes: 21 additions & 8 deletions src/providers/image-builders/common.ts
Expand Up @@ -292,11 +292,11 @@ export interface ImageBuilderBaseProps {
readonly vpc?: ec2.IVpc;

/**
* Security Group to assign to launched builder instances.
* Security groups to assign to launched builder instances.
*
* @default default account security group
* @default new security group
*/
readonly securityGroup?: ec2.ISecurityGroup;
readonly securityGroups?: ec2.ISecurityGroup[];

/**
* Where to place the network interfaces within the VPC.
Expand Down Expand Up @@ -339,7 +339,7 @@ export interface ImageBuilderBaseProps {
/**
* @internal
*/
export abstract class ImageBuilderBase extends Construct {
export abstract class ImageBuilderBase extends Construct implements ec2.IConnectable {
protected readonly architecture: Architecture;
protected readonly os: Os;
protected readonly platform: 'Windows' | 'Linux';
Expand All @@ -350,8 +350,9 @@ export abstract class ImageBuilderBase extends Construct {

protected components: ImageBuilderComponent[] = [];

private readonly vpc: ec2.IVpc;
private readonly subnetId: string | undefined;
private readonly securityGroupIds: string[] | undefined;
private readonly securityGroups: ec2.ISecurityGroup[];
private readonly instanceType: ec2.InstanceType;

private readonly rebuildInterval: Duration;
Expand Down Expand Up @@ -387,11 +388,16 @@ export abstract class ImageBuilderBase extends Construct {

// vpc settings
if (props?.vpc) {
this.vpc = props.vpc;
this.subnetId = props.vpc.selectSubnets(props.subnetSelection).subnetIds[0];
} else {
this.vpc = ec2.Vpc.fromLookup(this, 'Default VPC', { isDefault: true });
}

if (props?.securityGroup) {
this.securityGroupIds = [props.securityGroup.securityGroupId];
if (props?.securityGroups) {
this.securityGroups = props.securityGroups;
} else {
this.securityGroups = [new ec2.SecurityGroup(this, 'SG', { vpc: this.vpc })];
}

// instance type
Expand Down Expand Up @@ -433,7 +439,7 @@ export abstract class ImageBuilderBase extends Construct {
name: uniqueImageBuilderName(this),
description: this.description,
subnetId: this.subnetId,
securityGroupIds: this.securityGroupIds,
securityGroupIds: this.securityGroups.map(sg => sg.securityGroupId),
instanceTypes: [this.instanceType.toString()],
instanceProfileName: new iam.CfnInstanceProfile(this, 'Instance Profile', {
roles: [
Expand Down Expand Up @@ -486,4 +492,11 @@ export abstract class ImageBuilderBase extends Construct {

return pipeline;
}

/**
* The network connections associated with this resource.
*/
public get connections(): ec2.Connections {
return new ec2.Connections({ securityGroups: this.securityGroups });
}
}

0 comments on commit c3aebb1

Please sign in to comment.