Skip to content

Commit

Permalink
feat: add windows runner instances
Browse files Browse the repository at this point in the history
* change RunnerType to include platform
* refactor asg-runner-stack to build for both platform types macOS and
windows
* add windows-runner-user-data.yaml with PowerShell script to set up
  runner
* update runner-config.json to include Windows runners for beta, prod,
  and release
* add licenses for Windows

Signed-off-by: Gavin Inglis <[email protected]>
  • Loading branch information
ginglis13 committed Sep 20, 2023
1 parent 3c7f98c commit d8f5ae2
Show file tree
Hide file tree
Showing 5 changed files with 251 additions and 56 deletions.
117 changes: 94 additions & 23 deletions config/runner-config.json
Original file line number Diff line number Diff line change
@@ -1,152 +1,223 @@
{
"runnerBeta": {
"licenseArn": "arn:aws:license-manager:us-west-2:820462304213:license-configuration:lic-7ac3e249bd13f71b44ac34963a8c3353",
"macLicenseArn": "arn:aws:license-manager:us-west-2:820462304213:license-configuration:lic-7ac3e249bd13f71b44ac34963a8c3353",
"windowsLicenseArn": "arn:aws:license-manager:us-west-2:820462304213:license-configuration:lic-56c55a611976d66a1357e58077b19e9",
"runnerTypes": [
{
"macOSVersion": "13.2",
"platform": "mac",
"version": "13.2",
"arch": "arm",
"repo": "finch",
"desiredInstances": 1,
"availabilityZones": ["us-west-2a", "us-west-2b", "us-west-2c", "us-west-2d"]
},
{
"macOSVersion": "13.2",
"platform": "mac",
"version": "13.2",
"arch": "x86",
"repo": "finch-core",
"desiredInstances": 1,
"availabilityZones": ["us-west-2a", "us-west-2b", "us-west-2c", "us-west-2d"]
},
{
"platform": "windows",
"version": "2022",
"arch": "x86",
"repo": "finch",
"desiredInstances": 1,
"availabilityZones": ["us-west-2a", "us-west-2b", "us-west-2c", "us-west-2d"]
},
{
"platform": "windows",
"version": "2022",
"arch": "x86",
"repo": "finch-core",
"desiredInstances": 1,
"availabilityZones": ["us-west-2a", "us-west-2b", "us-west-2c", "us-west-2d"]
}
]
},
"runnerProd": {
"licenseArn": "arn:aws:license-manager:us-west-2:090529234398:license-configuration:lic-c0c3e2458f6d2a45b0bcc8d66fbc072e",
"macLicenseArn": "arn:aws:license-manager:us-west-2:090529234398:license-configuration:lic-c0c3e2458f6d2a45b0bcc8d66fbc072e",
"windowsLicenseArn": "arn:aws:license-manager:us-west-2:090529234398:license-configuration:lic-62c55a61d2d9ce6ef100c6ea267216e6",
"runnerTypes": [
{
"macOSVersion": "13.2",
"platform": "mac",
"version": "13.2",
"arch": "arm",
"repo": "finch",
"desiredInstances": 2,
"availabilityZones": ["us-west-2a", "us-west-2b", "us-west-2c", "us-west-2d"]
},
{
"macOSVersion": "13.2",
"platform": "mac",
"version": "13.2",
"arch": "x86",
"repo": "finch",
"desiredInstances": 2,
"availabilityZones": ["us-west-2a", "us-west-2b", "us-west-2c", "us-west-2d"]
},
{
"macOSVersion": "12.6",
"platform": "mac",
"version": "12.6",
"arch": "arm",
"repo": "finch",
"desiredInstances": 2,
"availabilityZones": ["us-west-2a", "us-west-2b", "us-west-2c", "us-west-2d"]
},
{
"macOSVersion": "12.6",
"platform": "mac",
"version": "12.6",
"arch": "x86",
"repo": "finch",
"desiredInstances": 2,
"availabilityZones": ["us-west-2a", "us-west-2b", "us-west-2c", "us-west-2d"]
},
{
"macOSVersion": "13.2",
"platform": "mac",
"version": "13.2",
"arch": "arm",
"repo": "finch-core",
"desiredInstances": 1,
"availabilityZones": ["us-west-2a", "us-west-2b", "us-west-2c", "us-west-2d"]
},
{
"macOSVersion": "13.2",
"platform": "mac",
"version": "13.2",
"arch": "x86",
"repo": "finch-core",
"desiredInstances": 1,
"availabilityZones": ["us-west-2a", "us-west-2b", "us-west-2c", "us-west-2d"]
},
{
"macOSVersion": "12.6",
"platform": "mac",
"version": "12.6",
"arch": "arm",
"repo": "finch-core",
"desiredInstances": 1,
"availabilityZones": ["us-west-2a", "us-west-2b", "us-west-2c", "us-west-2d"]
},
{
"macOSVersion": "12.6",
"platform": "mac",
"version": "12.6",
"arch": "x86",
"repo": "finch-core",
"desiredInstances": 1,
"availabilityZones": ["us-west-2a", "us-west-2b", "us-west-2c", "us-west-2d"]
},
{
"platform": "windows",
"version": "2022",
"arch": "x86",
"repo": "finch",
"desiredInstances": 1,
"availabilityZones": ["us-west-2a", "us-west-2b", "us-west-2c", "us-west-2d"]
},
{
"platform": "windows",
"version": "2022",
"arch": "x86",
"repo": "finch-core",
"desiredInstances": 1,
"availabilityZones": ["us-west-2a", "us-west-2b", "us-west-2c", "us-west-2d"]
}
]
},
"runnerRelease": {
"licenseArn": "arn:aws:license-manager:us-east-2:019528120233:license-configuration:lic-94c3e244cb74a70c90c82f432e9e0d91",
"macLicenseArn": "arn:aws:license-manager:us-east-2:019528120233:license-configuration:lic-94c3e244cb74a70c90c82f432e9e0d91",
"windowsLicenseArn": "arn:aws:license-manager:us-east-2:019528120233:license-configuration:lic-2ec55a6280292474f1ea0d7ad6f64987",
"runnerTypes": [
{
"macOSVersion": "13.2",
"platform": "mac",
"version": "13.2",
"arch": "arm",
"repo": "finch",
"desiredInstances": 1,
"availabilityZones": ["us-east-2a", "us-east-2b", "us-east-2c"]
},
{
"macOSVersion": "13.2",
"platform": "mac",
"version": "13.2",
"arch": "x86",
"repo": "finch",
"desiredInstances": 1,
"availabilityZones": ["us-east-2a", "us-east-2b", "us-east-2c"]
},
{
"macOSVersion": "12.6",
"platform": "mac",
"version": "12.6",
"arch": "arm",
"repo": "finch",
"desiredInstances": 1,
"availabilityZones": ["us-east-2a", "us-east-2b", "us-east-2c"]
},
{
"macOSVersion": "12.6",
"platform": "mac",
"version": "12.6",
"arch": "x86",
"repo": "finch",
"desiredInstances": 1,
"availabilityZones": ["us-east-2a", "us-east-2b", "us-east-2c"]
},
{
"macOSVersion": "11.7",
"platform": "mac",
"version": "11.7",
"arch": "arm",
"repo": "finch",
"desiredInstances": 1,
"availabilityZones": ["us-east-2a", "us-east-2b", "us-east-2c"]
},
{
"macOSVersion": "11.7",
"platform": "mac",
"version": "11.7",
"arch": "x86",
"repo": "finch",
"desiredInstances": 1,
"availabilityZones": ["us-east-2a", "us-east-2b", "us-east-2c"]
},
{
"macOSVersion": "13.2",
"platform": "mac",
"version": "13.2",
"arch": "arm",
"repo": "finch-core",
"desiredInstances": 1,
"availabilityZones": ["us-east-2a", "us-east-2b", "us-east-2c"]
},
{
"macOSVersion": "13.2",
"platform": "mac",
"version": "13.2",
"arch": "x86",
"repo": "finch-core",
"desiredInstances": 1,
"availabilityZones": ["us-east-2a", "us-east-2b", "us-east-2c"]
},
{
"macOSVersion": "11.7",
"platform": "mac",
"version": "11.7",
"arch": "arm",
"repo": "finch-core",
"desiredInstances": 1,
"availabilityZones": ["us-east-2a", "us-east-2b", "us-east-2c"]
},
{
"macOSVersion": "11.7",
"platform": "mac",
"version": "11.7",
"arch": "x86",
"repo": "finch-core",
"desiredInstances": 1,
"availabilityZones": ["us-east-2a", "us-east-2b", "us-east-2c"]
},
{
"platform": "windows",
"version": "2022",
"arch": "x86",
"repo": "finch",
"desiredInstances": 1,
"availabilityZones": ["us-east-2a", "us-east-2b", "us-east-2c"]
},
{
"platform": "windows",
"version": "2022",
"arch": "x86",
"repo": "finch-core",
"desiredInstances": 1,
Expand Down
11 changes: 9 additions & 2 deletions config/runner-config.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import config from './runner-config.json';

export interface RunnerProps {
licenseArn: string;
macLicenseArn: string;
windowsLicenseArn: string;
runnerTypes: Array<RunnerType>;
}

export interface RunnerType {
macOSVersion: string;
platform: PlatformType;
version: string; // e.g. 13.2 if platform == macOS, 2022 if platform == windows
arch: string;
repo: string;
desiredInstances: number;
availabilityZones: Array<string>;
}

export enum PlatformType {
WINDOWS = 'windows',
MAC = 'mac'
}

/**
* Class for runner configurations. Outlines self hosted license arn and an
* array of runner types to create using an auto scaling group.
Expand Down
72 changes: 44 additions & 28 deletions lib/asg-runner-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as iam from 'aws-cdk-lib/aws-iam';
import * as resourcegroups from 'aws-cdk-lib/aws-resourcegroups';
import { aws_autoscaling as autoscaling } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { RunnerType } from '../config/runner-config';
import { PlatformType, RunnerType } from '../config/runner-config';
import { readFileSync } from 'fs';
import { ENVIRONMENT_STAGE } from './finch-pipeline-app-stage';
import { UpdatePolicy } from 'aws-cdk-lib/aws-autoscaling';
Expand All @@ -26,22 +26,26 @@ interface ASGRunnerStackProps extends cdk.StackProps {
export class ASGRunnerStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: ASGRunnerStackProps) {
super(scope, id, props);
const arch = props.type.arch === 'arm' ? 'arm64_mac' : 'x86_64_mac';
const instanceType = props.type.arch === 'arm' ? 'mac2.metal' : 'mac1.metal';

const platform = props.type.platform;
const arch = props.type.arch === 'arm' ? `arm64_${platform}` : `x86_64_${platform}`;
const instanceType =
platform === PlatformType.WINDOWS ? 'm5zn.metal' : props.type.arch === 'arm' ? 'mac2.metal' : 'mac1.metal';
const version = props.type.version;

if (props.env == undefined) {
throw new Error('Runner environment is undefined!');
}

const vpc = cdk.aws_ec2.Vpc.fromLookup(this, 'VPC', { isDefault: true });

const securityGroup = new ec2.SecurityGroup(this, 'MacEC2SecurityGroup', {
const securityGroup = new ec2.SecurityGroup(this, 'EC2SecurityGroup', {
vpc,
description: 'Allow only outbound traffic',
allowAllOutbound: true
});

const role = new iam.Role(this, 'MacEC2Role', {
const role = new iam.Role(this, 'EC2Role', {
assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com')
});

Expand All @@ -65,8 +69,7 @@ export class ASGRunnerStack extends cdk.Stack {
);

// Create a custom name for this as names for resource groups cannot be repeated
const resourceGroupName =
props.type.repo + '-' + 'Mac' + '-' + props.type.macOSVersion.split('.')[0] + '-' + props.type.arch + 'HostGroup';
const resourceGroupName = `${props.type.repo}-${platform}-${version.split('.')[0]}-${props.type.arch}HostGroup`;

const resourceGroup = new resourcegroups.CfnGroup(this, resourceGroupName, {
name: resourceGroupName,
Expand Down Expand Up @@ -105,30 +108,42 @@ export class ASGRunnerStack extends cdk.Stack {
]
});

const macOSVersion = props.type.macOSVersion;
const amiSearchString = `amzn-ec2-macos-${macOSVersion}*`;
const macImage = new ec2.LookupMachineImage({
name: amiSearchString,
filters: {
'virtualization-type': ['hvm'],
'root-device-type': ['ebs'],
architecture: [arch],
'owner-alias': ['amazon']
}
});

const userData =
`#!/bin/bash
LABEL_STAGE=${props.stage === ENVIRONMENT_STAGE.Release ? 'release' : 'test'}
REPO=${props.type.repo}
REGION=${props.env?.region}
` + readFileSync('./scripts/setup-runner.sh', 'utf8');
const amiSearchString = `amzn-ec2-macos-${version}*`;
const machineImage =
platform === PlatformType.WINDOWS
? ec2.MachineImage.latestWindows(ec2.WindowsVersion.WINDOWS_SERVER_2022_ENGLISH_FULL_BASE)
: new ec2.LookupMachineImage({
name: amiSearchString,
filters: {
'virtualization-type': ['hvm'],
'root-device-type': ['ebs'],
architecture: [arch],
'owner-alias': ['amazon']
}
});

var userData = '';
if (platform === PlatformType.WINDOWS) {
// We need to provide user data as a yaml file to specify runAs: admin
// Maintain that file as yaml and source here to ensure formatting.
userData = readFileSync('./scripts/windows-runner-user-data.yaml', 'utf8')
.replace('<STAGE>', props.stage === ENVIRONMENT_STAGE.Release ? 'release' : 'test')
.replace('<REPO>', props.type.repo)
.replace('<REGION>', props.env?.region || '');
} else if (platform === PlatformType.MAC) {
userData =
`#!/bin/bash
LABEL_STAGE=${props.stage === ENVIRONMENT_STAGE.Release ? 'release' : 'test'}
REPO=${props.type.repo}
REGION=${props.env?.region}
` + readFileSync('./scripts/setup-runner.sh', 'utf8');
}

const lt = new ec2.LaunchTemplate(this, 'MacASGLaunchTemplate', {
const lt = new ec2.LaunchTemplate(this, `${platform}ASGLaunchTemplate`, {
requireImdsv2: true,
instanceType: new ec2.InstanceType(instanceType),
keyName: 'runner-key',
machineImage: macImage,
machineImage: machineImage,
role: role,
securityGroup: securityGroup,
userData: ec2.UserData.custom(userData)
Expand Down Expand Up @@ -161,7 +176,8 @@ export class ASGRunnerStack extends cdk.Stack {
]
};

const asg = new autoscaling.AutoScalingGroup(this, 'MacASG', {
const asgName = platform === PlatformType.WINDOWS ? 'WindowsASG' : 'MacASG';
const asg = new autoscaling.AutoScalingGroup(this, asgName, {
vpc,
desiredCapacity: props.type.desiredInstances,
maxCapacity: props.type.desiredInstances + 2,
Expand Down
Loading

0 comments on commit d8f5ae2

Please sign in to comment.