Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Multiple security groups, no more default security SG, and image builder connections support #171

Merged
merged 4 commits into from
Dec 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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 });
}
}