mirror of
https://github.com/game-ci/unity-builder.git
synced 2025-07-04 12:25:19 -04:00
Cloud runner develop v0.1 (#395)
* Correct aws logs link * Correct aws logs link * better aws cli commands and better cleanup for aws * better aws cli commands and better cleanup for aws * improved garbage collection cli options * Only allow ephemeral runners when using cloud runner integration tests flag to avoid unexpected hangup * Only allow ephemeral runners when using cloud runner integration tests flag to avoid unexpected hangup * fix issue #393 * Extract follow log stream service * consolidate into one pipeline file * consolidate into one pipeline file
This commit is contained in:
parent
4556fc4ff1
commit
f77696efae
4
.github/workflows/cleanup.yml
vendored
4
.github/workflows/cleanup.yml
vendored
@ -23,13 +23,13 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: 12.x
|
node-version: 12.x
|
||||||
- run: yarn
|
- run: yarn
|
||||||
- run: yarn run cli -m aws-list-tasks
|
- run: yarn run cli --help
|
||||||
env:
|
env:
|
||||||
AWS_REGION: eu-west-2
|
AWS_REGION: eu-west-2
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_DEFAULT_REGION: eu-west-2
|
AWS_DEFAULT_REGION: eu-west-2
|
||||||
- run: yarn run cli -m aws-list-stacks
|
- run: yarn run cli -m aws-list-all
|
||||||
env:
|
env:
|
||||||
AWS_REGION: eu-west-2
|
AWS_REGION: eu-west-2
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
|
114
.github/workflows/cloud-runner-aws-pipeline.yml
vendored
114
.github/workflows/cloud-runner-aws-pipeline.yml
vendored
@ -1,114 +0,0 @@
|
|||||||
name: Cloud Runner - AWS Tests
|
|
||||||
|
|
||||||
on:
|
|
||||||
push: { branches: [main, cloud-runner-develop] }
|
|
||||||
|
|
||||||
env:
|
|
||||||
GKE_ZONE: 'us-central1'
|
|
||||||
GKE_REGION: 'us-central1'
|
|
||||||
GKE_PROJECT: 'unitykubernetesbuilder'
|
|
||||||
GKE_CLUSTER: 'unity-builder-cluster'
|
|
||||||
GCP_LOGGING: true
|
|
||||||
GCP_PROJECT: unitykubernetesbuilder
|
|
||||||
AWS_REGION: eu-west-2
|
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
||||||
AWS_DEFAULT_REGION: eu-west-2
|
|
||||||
AWS_BASE_STACK_NAME: game-ci-github-pipelines
|
|
||||||
CLOUD_RUNNER_BRANCH: ${{ github.ref }}
|
|
||||||
CLOUD_RUNNER_TESTS: true
|
|
||||||
DEBUG: true
|
|
||||||
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
buildForAllPlatforms:
|
|
||||||
name: AWS Fargate Build
|
|
||||||
if: github.event.pull_request.draft == false
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
projectPath:
|
|
||||||
- test-project
|
|
||||||
unityVersion:
|
|
||||||
# - 2019.2.11f1
|
|
||||||
- 2019.3.15f1
|
|
||||||
targetPlatform:
|
|
||||||
#- StandaloneOSX # Build a macOS standalone (Intel 64-bit).
|
|
||||||
- StandaloneWindows64 # Build a Windows 64-bit standalone.
|
|
||||||
- StandaloneLinux64 # Build a Linux 64-bit standalone.
|
|
||||||
- WebGL # WebGL.
|
|
||||||
#- iOS # Build an iOS player.
|
|
||||||
#- Android # Build an Android .apk.
|
|
||||||
# - StandaloneWindows # Build a Windows standalone.
|
|
||||||
# - WSAPlayer # Build an Windows Store Apps player.
|
|
||||||
# - PS4 # Build a PS4 Standalone.
|
|
||||||
# - XboxOne # Build a Xbox One Standalone.
|
|
||||||
# - tvOS # Build to Apple's tvOS platform.
|
|
||||||
# - Switch # Build a Nintendo Switch player
|
|
||||||
# steps
|
|
||||||
steps:
|
|
||||||
- name: Checkout (default)
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
if: github.event.event_type != 'pull_request_target'
|
|
||||||
with:
|
|
||||||
lfs: true
|
|
||||||
- name: Configure AWS Credentials
|
|
||||||
uses: aws-actions/configure-aws-credentials@v1
|
|
||||||
with:
|
|
||||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
||||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
||||||
aws-region: eu-west-2
|
|
||||||
- run: yarn
|
|
||||||
- run: yarn run cli --help
|
|
||||||
- run: yarn run test "caching"
|
|
||||||
- run: yarn run test-i-aws
|
|
||||||
env:
|
|
||||||
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
|
||||||
PROJECT_PATH: ${{ matrix.projectPath }}
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
TARGET_PLATFORM: ${{ matrix.targetPlatform }}
|
|
||||||
cloudRunnerTests: true
|
|
||||||
versioning: None
|
|
||||||
- uses: ./
|
|
||||||
id: aws-fargate-unity-build
|
|
||||||
timeout-minutes: 25
|
|
||||||
with:
|
|
||||||
cloudRunnerCluster: aws
|
|
||||||
versioning: None
|
|
||||||
projectPath: ${{ matrix.projectPath }}
|
|
||||||
unityVersion: ${{ matrix.unityVersion }}
|
|
||||||
targetPlatform: ${{ matrix.targetPlatform }}
|
|
||||||
githubToken: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
postBuildSteps: |
|
|
||||||
- name: upload
|
|
||||||
image: amazon/aws-cli
|
|
||||||
commands: |
|
|
||||||
aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID --profile default
|
|
||||||
aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY --profile default
|
|
||||||
aws configure set region $AWS_DEFAULT_REGION --profile default
|
|
||||||
aws s3 ls
|
|
||||||
aws s3 ls game-ci-test-storage
|
|
||||||
ls /data/cache/$CACHE_KEY
|
|
||||||
ls /data/cache/$CACHE_KEY/build
|
|
||||||
aws s3 cp /data/cache/$CACHE_KEY/build/build-$BUILD_GUID.tar s3://game-ci-test-storage/$CACHE_KEY/build-$BUILD_GUID.tar
|
|
||||||
secrets:
|
|
||||||
- name: awsAccessKeyId
|
|
||||||
value: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
||||||
- name: awsSecretAccessKey
|
|
||||||
value: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
||||||
- name: awsDefaultRegion
|
|
||||||
value: eu-west-2
|
|
||||||
- run: |
|
|
||||||
aws s3 cp s3://game-ci-test-storage/${{ steps.aws-fargate-unity-build.outputs.CACHE_KEY }}/build-${{ steps.aws-fargate-unity-build.outputs.BUILD_GUID }}.tar build-${{ steps.aws-fargate-unity-build.outputs.BUILD_GUID }}.tar
|
|
||||||
ls
|
|
||||||
- run: yarn run cli -m aws-garbage-collect
|
|
||||||
###########################
|
|
||||||
# Upload #
|
|
||||||
###########################
|
|
||||||
# download from cloud storage
|
|
||||||
- uses: actions/upload-artifact@v2
|
|
||||||
with:
|
|
||||||
name: AWS Build (${{ matrix.targetPlatform }})
|
|
||||||
path: build-${{ steps.aws-fargate-unity-build.outputs.BUILD_GUID }}.tar
|
|
||||||
retention-days: 14
|
|
@ -1,7 +1,7 @@
|
|||||||
name: Cloud Runner - K8s Tests
|
name: Cloud Runner
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push: { branches: [cloud-runner-develop] }
|
push: { branches: [cloud-runner-develop, main] }
|
||||||
# push: { branches: [main] }
|
# push: { branches: [main] }
|
||||||
# pull_request:
|
# pull_request:
|
||||||
# paths-ignore:
|
# paths-ignore:
|
||||||
@ -26,6 +26,97 @@ env:
|
|||||||
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
awsBuild:
|
||||||
|
name: AWS Fargate Build
|
||||||
|
if: github.event.pull_request.draft == false
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
projectPath:
|
||||||
|
- test-project
|
||||||
|
unityVersion:
|
||||||
|
# - 2019.2.11f1
|
||||||
|
- 2019.3.15f1
|
||||||
|
targetPlatform:
|
||||||
|
#- StandaloneOSX # Build a macOS standalone (Intel 64-bit).
|
||||||
|
- StandaloneWindows64 # Build a Windows 64-bit standalone.
|
||||||
|
- StandaloneLinux64 # Build a Linux 64-bit standalone.
|
||||||
|
- WebGL # WebGL.
|
||||||
|
#- iOS # Build an iOS player.
|
||||||
|
#- Android # Build an Android .apk.
|
||||||
|
# - StandaloneWindows # Build a Windows standalone.
|
||||||
|
# - WSAPlayer # Build an Windows Store Apps player.
|
||||||
|
# - PS4 # Build a PS4 Standalone.
|
||||||
|
# - XboxOne # Build a Xbox One Standalone.
|
||||||
|
# - tvOS # Build to Apple's tvOS platform.
|
||||||
|
# - Switch # Build a Nintendo Switch player
|
||||||
|
# steps
|
||||||
|
steps:
|
||||||
|
- name: Checkout (default)
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
if: github.event.event_type != 'pull_request_target'
|
||||||
|
with:
|
||||||
|
lfs: true
|
||||||
|
- name: Configure AWS Credentials
|
||||||
|
uses: aws-actions/configure-aws-credentials@v1
|
||||||
|
with:
|
||||||
|
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
|
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
aws-region: eu-west-2
|
||||||
|
- run: yarn
|
||||||
|
- run: yarn run cli --help
|
||||||
|
- run: yarn run test "caching"
|
||||||
|
- run: yarn run test-i-aws
|
||||||
|
env:
|
||||||
|
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
||||||
|
PROJECT_PATH: ${{ matrix.projectPath }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
TARGET_PLATFORM: ${{ matrix.targetPlatform }}
|
||||||
|
cloudRunnerTests: true
|
||||||
|
versioning: None
|
||||||
|
- uses: ./
|
||||||
|
id: aws-fargate-unity-build
|
||||||
|
timeout-minutes: 25
|
||||||
|
with:
|
||||||
|
cloudRunnerCluster: aws
|
||||||
|
versioning: None
|
||||||
|
projectPath: ${{ matrix.projectPath }}
|
||||||
|
unityVersion: ${{ matrix.unityVersion }}
|
||||||
|
targetPlatform: ${{ matrix.targetPlatform }}
|
||||||
|
githubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
postBuildSteps: |
|
||||||
|
- name: upload
|
||||||
|
image: amazon/aws-cli
|
||||||
|
commands: |
|
||||||
|
aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID --profile default
|
||||||
|
aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY --profile default
|
||||||
|
aws configure set region $AWS_DEFAULT_REGION --profile default
|
||||||
|
aws s3 ls
|
||||||
|
aws s3 ls game-ci-test-storage
|
||||||
|
ls /data/cache/$CACHE_KEY
|
||||||
|
ls /data/cache/$CACHE_KEY/build
|
||||||
|
aws s3 cp /data/cache/$CACHE_KEY/build/build-$BUILD_GUID.tar s3://game-ci-test-storage/$CACHE_KEY/build-$BUILD_GUID.tar
|
||||||
|
secrets:
|
||||||
|
- name: awsAccessKeyId
|
||||||
|
value: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
|
- name: awsSecretAccessKey
|
||||||
|
value: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
- name: awsDefaultRegion
|
||||||
|
value: eu-west-2
|
||||||
|
- run: |
|
||||||
|
aws s3 cp s3://game-ci-test-storage/${{ steps.aws-fargate-unity-build.outputs.CACHE_KEY }}/build-${{ steps.aws-fargate-unity-build.outputs.BUILD_GUID }}.tar build-${{ steps.aws-fargate-unity-build.outputs.BUILD_GUID }}.tar
|
||||||
|
ls
|
||||||
|
- run: yarn run cli -m aws-garbage-collect
|
||||||
|
###########################
|
||||||
|
# Upload #
|
||||||
|
###########################
|
||||||
|
# download from cloud storage
|
||||||
|
- uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: AWS Build (${{ matrix.targetPlatform }})
|
||||||
|
path: build-${{ steps.aws-fargate-unity-build.outputs.BUILD_GUID }}.tar
|
||||||
|
retention-days: 14
|
||||||
k8sBuilds:
|
k8sBuilds:
|
||||||
name: K8s (GKE Autopilot) build for ${{ matrix.targetPlatform }} on version ${{ matrix.unityVersion }}
|
name: K8s (GKE Autopilot) build for ${{ matrix.targetPlatform }} on version ${{ matrix.unityVersion }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -105,7 +196,6 @@ jobs:
|
|||||||
aws s3 ls
|
aws s3 ls
|
||||||
aws s3 ls game-ci-test-storage
|
aws s3 ls game-ci-test-storage
|
||||||
ls /data/cache/$CACHE_KEY
|
ls /data/cache/$CACHE_KEY
|
||||||
echo "/data/cache/$CACHE_KEY/build/build-$BUILD_GUID.tar s3://game-ci-test-storage/$CACHE_KEY/$BUILD_FILE"
|
|
||||||
aws s3 cp /data/cache/$CACHE_KEY/build/build-$BUILD_GUID.tar s3://game-ci-test-storage/$CACHE_KEY/build-$BUILD_GUID.tar
|
aws s3 cp /data/cache/$CACHE_KEY/build/build-$BUILD_GUID.tar s3://game-ci-test-storage/$CACHE_KEY/build-$BUILD_GUID.tar
|
||||||
secrets:
|
secrets:
|
||||||
- name: awsAccessKeyId
|
- name: awsAccessKeyId
|
BIN
dist/index.js
generated
vendored
BIN
dist/index.js
generated
vendored
Binary file not shown.
BIN
dist/index.js.map
generated
vendored
BIN
dist/index.js.map
generated
vendored
Binary file not shown.
@ -76,7 +76,7 @@ class BuildParameters {
|
|||||||
// Todo - Don't use process.env directly, that's what the input model class is for.
|
// Todo - Don't use process.env directly, that's what the input model class is for.
|
||||||
// ---
|
// ---
|
||||||
let unitySerial = '';
|
let unitySerial = '';
|
||||||
if (!process.env.UNITY_SERIAL && Input.githubInputEnabled && Cli.options === undefined) {
|
if (!process.env.UNITY_SERIAL && Input.githubInputEnabled) {
|
||||||
// No serial was present, so it is a personal license that we need to convert
|
// No serial was present, so it is a personal license that we need to convert
|
||||||
if (!process.env.UNITY_LICENSE) {
|
if (!process.env.UNITY_LICENSE) {
|
||||||
throw new Error(`Missing Unity License File and no Serial was found. If this
|
throw new Error(`Missing Unity License File and no Serial was found. If this
|
||||||
|
@ -6,8 +6,8 @@ import * as zlib from 'zlib';
|
|||||||
import CloudRunnerLogger from '../../services/cloud-runner-logger';
|
import CloudRunnerLogger from '../../services/cloud-runner-logger';
|
||||||
import { Input } from '../../..';
|
import { Input } from '../../..';
|
||||||
import CloudRunner from '../../cloud-runner';
|
import CloudRunner from '../../cloud-runner';
|
||||||
import { CloudRunnerStatics } from '../../cloud-runner-statics';
|
|
||||||
import { CloudRunnerBuildCommandProcessor } from '../../services/cloud-runner-build-command-process';
|
import { CloudRunnerBuildCommandProcessor } from '../../services/cloud-runner-build-command-process';
|
||||||
|
import { FollowLogStreamService } from '../../services/follow-log-stream-service';
|
||||||
|
|
||||||
class AWSTaskRunner {
|
class AWSTaskRunner {
|
||||||
static async runTask(
|
static async runTask(
|
||||||
@ -126,8 +126,8 @@ class AWSTaskRunner {
|
|||||||
const stream = await AWSTaskRunner.getLogStream(kinesis, kinesisStreamName);
|
const stream = await AWSTaskRunner.getLogStream(kinesis, kinesisStreamName);
|
||||||
let iterator = await AWSTaskRunner.getLogIterator(kinesis, stream);
|
let iterator = await AWSTaskRunner.getLogIterator(kinesis, stream);
|
||||||
|
|
||||||
const logBaseUrl = `https://${Input.region}.console.aws.amazon.com/cloudwatch/home?region=${CF.config.region}#logsV2:log-groups/log-group/${taskDef.taskDefStackName}`;
|
const logBaseUrl = `https://${Input.region}.console.aws.amazon.com/cloudwatch/home?region=${Input.region}#logsV2:log-groups/log-group/${CloudRunner.buildParameters.awsBaseStackName}-${CloudRunner.buildParameters.buildGuid}`;
|
||||||
CloudRunnerLogger.log(`You can also see the logs at AWS Cloud Watch: ${logBaseUrl}`);
|
CloudRunnerLogger.log(`You view the log stream on AWS Cloud Watch: ${logBaseUrl}`);
|
||||||
let shouldReadLogs = true;
|
let shouldReadLogs = true;
|
||||||
let shouldCleanup = true;
|
let shouldCleanup = true;
|
||||||
let timestamp: number = 0;
|
let timestamp: number = 0;
|
||||||
@ -209,30 +209,13 @@ class AWSTaskRunner {
|
|||||||
);
|
);
|
||||||
if (json.messageType === 'DATA_MESSAGE') {
|
if (json.messageType === 'DATA_MESSAGE') {
|
||||||
for (let logEventsIndex = 0; logEventsIndex < json.logEvents.length; logEventsIndex++) {
|
for (let logEventsIndex = 0; logEventsIndex < json.logEvents.length; logEventsIndex++) {
|
||||||
let message = json.logEvents[logEventsIndex].message;
|
const message = json.logEvents[logEventsIndex].message;
|
||||||
if (json.logEvents[logEventsIndex].message.includes(`---${CloudRunner.buildParameters.logId}`)) {
|
({ shouldReadLogs, shouldCleanup, output } = FollowLogStreamService.handleIteration(
|
||||||
CloudRunnerLogger.log('End of log transmission received');
|
message,
|
||||||
shouldReadLogs = false;
|
shouldReadLogs,
|
||||||
} else if (message.includes('Rebuilding Library because the asset database could not be found!')) {
|
shouldCleanup,
|
||||||
core.warning('LIBRARY NOT FOUND!');
|
output,
|
||||||
core.setOutput('library-found', 'false');
|
));
|
||||||
} else if (message.includes('Build succeeded')) {
|
|
||||||
core.setOutput('build-result', 'success');
|
|
||||||
} else if (message.includes('Build fail')) {
|
|
||||||
core.setOutput('build-result', 'failed');
|
|
||||||
core.setFailed('unity build failed');
|
|
||||||
core.error('BUILD FAILED!');
|
|
||||||
} else if (message.includes(': Listening for Jobs')) {
|
|
||||||
core.setOutput('cloud runner stop watching', 'true');
|
|
||||||
shouldReadLogs = false;
|
|
||||||
shouldCleanup = false;
|
|
||||||
core.warning('cloud runner stop watching');
|
|
||||||
}
|
|
||||||
message = `[${CloudRunnerStatics.logPrefix}] ${message}`;
|
|
||||||
if (CloudRunner.buildParameters.cloudRunnerIntegrationTests) {
|
|
||||||
output += message;
|
|
||||||
}
|
|
||||||
CloudRunnerLogger.log(message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
export class BaseStackFormation {
|
export class BaseStackFormation {
|
||||||
|
public static readonly baseStackDecription = `Game-CI base stack`;
|
||||||
public static readonly formation: string = `AWSTemplateFormatVersion: '2010-09-09'
|
public static readonly formation: string = `AWSTemplateFormatVersion: '2010-09-09'
|
||||||
Description: Game-CI base stack
|
Description: ${BaseStackFormation.baseStackDecription}
|
||||||
Parameters:
|
Parameters:
|
||||||
EnvironmentName:
|
EnvironmentName:
|
||||||
Type: String
|
Type: String
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
export class TaskDefinitionFormation {
|
export class TaskDefinitionFormation {
|
||||||
|
public static readonly description: string = `Game CI Cloud Runner Task Stack`;
|
||||||
public static readonly formation: string = `AWSTemplateFormatVersion: 2010-09-09
|
public static readonly formation: string = `AWSTemplateFormatVersion: 2010-09-09
|
||||||
Description: >-
|
Description: ${TaskDefinitionFormation.description}
|
||||||
AWS Fargate cluster that can span public and private subnets. Supports public
|
|
||||||
facing load balancers, private internal load balancers, and both internal and
|
|
||||||
external service discovery namespaces.
|
|
||||||
Parameters:
|
Parameters:
|
||||||
EnvironmentName:
|
EnvironmentName:
|
||||||
Type: String
|
Type: String
|
||||||
|
@ -2,18 +2,67 @@ import AWS from 'aws-sdk';
|
|||||||
import { CliFunction } from '../../../../cli/cli-functions-repository';
|
import { CliFunction } from '../../../../cli/cli-functions-repository';
|
||||||
import Input from '../../../../input';
|
import Input from '../../../../input';
|
||||||
import CloudRunnerLogger from '../../../services/cloud-runner-logger';
|
import CloudRunnerLogger from '../../../services/cloud-runner-logger';
|
||||||
|
import { BaseStackFormation } from '../cloud-formations/base-stack-formation';
|
||||||
|
|
||||||
export class AwsCliCommands {
|
export class AwsCliCommands {
|
||||||
|
@CliFunction(`aws-list-all`, `List all resources`)
|
||||||
|
static async awsListAll() {
|
||||||
|
await AwsCliCommands.awsListStacks(undefined, true);
|
||||||
|
await AwsCliCommands.awsListTasks();
|
||||||
|
await AwsCliCommands.awsListLogGroups(undefined, true);
|
||||||
|
}
|
||||||
|
@CliFunction(`aws-garbage-collect`, `garbage collect aws resources not in use !WIP!`)
|
||||||
|
static async garbageCollectAws() {
|
||||||
|
await AwsCliCommands.cleanup(false);
|
||||||
|
}
|
||||||
|
@CliFunction(`aws-garbage-collect-all`, `garbage collect aws resources regardless of whether they are in use`)
|
||||||
|
static async garbageCollectAwsAll() {
|
||||||
|
await AwsCliCommands.cleanup(true);
|
||||||
|
}
|
||||||
|
@CliFunction(
|
||||||
|
`aws-garbage-collect-all-1d-older`,
|
||||||
|
`garbage collect aws resources created more than 1d ago (ignore if they are in use)`,
|
||||||
|
)
|
||||||
|
static async garbageCollectAwsAllOlderThanOneDay() {
|
||||||
|
await AwsCliCommands.cleanup(true, true);
|
||||||
|
}
|
||||||
|
static isOlderThan1day(date: any) {
|
||||||
|
const ageDate = new Date(date.getTime() - Date.now());
|
||||||
|
|
||||||
|
return ageDate.getDay() > 0;
|
||||||
|
}
|
||||||
@CliFunction(`aws-list-stacks`, `List stacks`)
|
@CliFunction(`aws-list-stacks`, `List stacks`)
|
||||||
static async awsListStacks(perResultCallback: any) {
|
static async awsListStacks(perResultCallback: any = false, verbose: boolean = false) {
|
||||||
process.env.AWS_REGION = Input.region;
|
process.env.AWS_REGION = Input.region;
|
||||||
const CF = new AWS.CloudFormation();
|
const CF = new AWS.CloudFormation();
|
||||||
const stacks =
|
const stacks =
|
||||||
(await CF.listStacks().promise()).StackSummaries?.filter((_x) => _x.StackStatus !== 'DELETE_COMPLETE') || [];
|
(await CF.listStacks().promise()).StackSummaries?.filter(
|
||||||
CloudRunnerLogger.log(`DescribeStacksRequest ${stacks.length}`);
|
(_x) => _x.StackStatus !== 'DELETE_COMPLETE', // &&
|
||||||
|
// _x.TemplateDescription === TaskDefinitionFormation.description.replace('\n', ''),
|
||||||
|
) || [];
|
||||||
|
CloudRunnerLogger.log(`Stacks ${stacks.length}`);
|
||||||
for (const element of stacks) {
|
for (const element of stacks) {
|
||||||
CloudRunnerLogger.log(JSON.stringify(element, undefined, 4));
|
const ageDate = new Date(element.CreationTime.getTime() - Date.now());
|
||||||
CloudRunnerLogger.log(`${element.StackName}`);
|
if (verbose)
|
||||||
|
CloudRunnerLogger.log(
|
||||||
|
`Task Stack ${element.StackName} - Age D${ageDate.getDay()} H${ageDate.getHours()} M${ageDate.getMinutes()}`,
|
||||||
|
);
|
||||||
|
if (perResultCallback) await perResultCallback(element);
|
||||||
|
}
|
||||||
|
const baseStacks =
|
||||||
|
(await CF.listStacks().promise()).StackSummaries?.filter(
|
||||||
|
(_x) =>
|
||||||
|
_x.StackStatus !== 'DELETE_COMPLETE' && _x.TemplateDescription === BaseStackFormation.baseStackDecription,
|
||||||
|
) || [];
|
||||||
|
CloudRunnerLogger.log(`Base Stacks ${baseStacks.length}`);
|
||||||
|
for (const element of baseStacks) {
|
||||||
|
const ageDate = new Date(element.CreationTime.getTime() - Date.now());
|
||||||
|
if (verbose)
|
||||||
|
CloudRunnerLogger.log(
|
||||||
|
`Base Stack ${
|
||||||
|
element.StackName
|
||||||
|
} - Age D${ageDate.getHours()} H${ageDate.getHours()} M${ageDate.getMinutes()}`,
|
||||||
|
);
|
||||||
if (perResultCallback) await perResultCallback(element);
|
if (perResultCallback) await perResultCallback(element);
|
||||||
}
|
}
|
||||||
if (stacks === undefined) {
|
if (stacks === undefined) {
|
||||||
@ -21,15 +70,16 @@ export class AwsCliCommands {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
@CliFunction(`aws-list-tasks`, `List tasks`)
|
@CliFunction(`aws-list-tasks`, `List tasks`)
|
||||||
static async awsListTasks(perResultCallback: any) {
|
static async awsListTasks(perResultCallback: any = false) {
|
||||||
process.env.AWS_REGION = Input.region;
|
process.env.AWS_REGION = Input.region;
|
||||||
CloudRunnerLogger.log(`ECS Clusters`);
|
|
||||||
const ecs = new AWS.ECS();
|
const ecs = new AWS.ECS();
|
||||||
const clusters = (await ecs.listClusters().promise()).clusterArns || [];
|
const clusters = (await ecs.listClusters().promise()).clusterArns || [];
|
||||||
|
CloudRunnerLogger.log(`Clusters ${clusters.length}`);
|
||||||
for (const element of clusters) {
|
for (const element of clusters) {
|
||||||
const input: AWS.ECS.ListTasksRequest = {
|
const input: AWS.ECS.ListTasksRequest = {
|
||||||
cluster: element,
|
cluster: element,
|
||||||
};
|
};
|
||||||
|
|
||||||
const list = (await ecs.listTasks(input).promise()).taskArns || [];
|
const list = (await ecs.listTasks(input).promise()).taskArns || [];
|
||||||
if (list.length > 0) {
|
if (list.length > 0) {
|
||||||
const describeInput: AWS.ECS.DescribeTasksRequest = { tasks: list, cluster: element };
|
const describeInput: AWS.ECS.DescribeTasksRequest = { tasks: list, cluster: element };
|
||||||
@ -37,14 +87,13 @@ export class AwsCliCommands {
|
|||||||
if (describeList === []) {
|
if (describeList === []) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
CloudRunnerLogger.log(`DescribeTasksRequest ${describeList.length}`);
|
CloudRunnerLogger.log(`Tasks ${describeList.length}`);
|
||||||
for (const taskElement of describeList) {
|
for (const taskElement of describeList) {
|
||||||
if (taskElement === undefined) {
|
if (taskElement === undefined) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
taskElement.overrides = {};
|
taskElement.overrides = {};
|
||||||
taskElement.attachments = [];
|
taskElement.attachments = [];
|
||||||
CloudRunnerLogger.log(JSON.stringify(taskElement, undefined, 4));
|
|
||||||
if (taskElement.createdAt === undefined) {
|
if (taskElement.createdAt === undefined) {
|
||||||
CloudRunnerLogger.log(`Skipping ${taskElement.taskDefinitionArn} no createdAt date`);
|
CloudRunnerLogger.log(`Skipping ${taskElement.taskDefinitionArn} no createdAt date`);
|
||||||
continue;
|
continue;
|
||||||
@ -54,39 +103,68 @@ export class AwsCliCommands {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@CliFunction(`aws-list-log-groups`, `List tasks`)
|
||||||
|
static async awsListLogGroups(perResultCallback: any = false, verbose: boolean = false) {
|
||||||
|
process.env.AWS_REGION = Input.region;
|
||||||
|
const ecs = new AWS.CloudWatchLogs();
|
||||||
|
let logStreamInput: AWS.CloudWatchLogs.DescribeLogGroupsRequest = {
|
||||||
|
/* logGroupNamePrefix: 'game-ci' */
|
||||||
|
};
|
||||||
|
let logGroupsDescribe = await ecs.describeLogGroups(logStreamInput).promise();
|
||||||
|
const logGroups = logGroupsDescribe.logGroups || [];
|
||||||
|
while (logGroupsDescribe.nextToken) {
|
||||||
|
logStreamInput = { /* logGroupNamePrefix: 'game-ci',*/ nextToken: logGroupsDescribe.nextToken };
|
||||||
|
logGroupsDescribe = await ecs.describeLogGroups(logStreamInput).promise();
|
||||||
|
logGroups.push(...(logGroupsDescribe?.logGroups || []));
|
||||||
|
}
|
||||||
|
|
||||||
@CliFunction(`aws-garbage-collect`, `garbage collect aws`)
|
CloudRunnerLogger.log(`Log Groups ${logGroups.length}`);
|
||||||
static async garbageCollectAws() {
|
for (const element of logGroups) {
|
||||||
await AwsCliCommands.cleanup(false);
|
if (element.creationTime === undefined) {
|
||||||
}
|
CloudRunnerLogger.log(`Skipping ${element.logGroupName} no createdAt date`);
|
||||||
@CliFunction(`aws-garbage-collect-all`, `garbage collect aws`)
|
continue;
|
||||||
static async garbageCollectAwsAll() {
|
}
|
||||||
await AwsCliCommands.cleanup(true);
|
const ageDate = new Date(new Date(element.creationTime).getTime() - Date.now());
|
||||||
}
|
if (verbose)
|
||||||
@CliFunction(`aws-garbage-collect-all-1d-older`, `garbage collect aws`)
|
CloudRunnerLogger.log(
|
||||||
static async garbageCollectAwsAllOlderThanOneDay() {
|
`Log Group Name ${
|
||||||
await AwsCliCommands.cleanup(true);
|
element.logGroupName
|
||||||
|
} - Age D${ageDate.getDay()} H${ageDate.getHours()} M${ageDate.getMinutes()} - 1d old ${AwsCliCommands.isOlderThan1day(
|
||||||
|
new Date(element.creationTime),
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
|
if (perResultCallback) await perResultCallback(element, element);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async cleanup(deleteResources = false) {
|
private static async cleanup(deleteResources = false, OneDayOlderOnly: boolean = false) {
|
||||||
process.env.AWS_REGION = Input.region;
|
process.env.AWS_REGION = Input.region;
|
||||||
const CF = new AWS.CloudFormation();
|
const CF = new AWS.CloudFormation();
|
||||||
const ecs = new AWS.ECS();
|
const ecs = new AWS.ECS();
|
||||||
|
const cwl = new AWS.CloudWatchLogs();
|
||||||
await AwsCliCommands.awsListStacks(async (element) => {
|
await AwsCliCommands.awsListStacks(async (element) => {
|
||||||
if (deleteResources) {
|
if (deleteResources && (!OneDayOlderOnly || AwsCliCommands.isOlderThan1day(element.CreationTime))) {
|
||||||
if (element.StackName === 'game-ci' || element.TemplateDescription === 'Game-CI base stack') {
|
if (element.StackName === 'game-ci' || element.TemplateDescription === 'Game-CI base stack') {
|
||||||
CloudRunnerLogger.log(`Skipping ${element.StackName} ignore list`);
|
CloudRunnerLogger.log(`Skipping ${element.StackName} ignore list`);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
CloudRunnerLogger.log(`Deleting ${element.logGroupName}`);
|
||||||
const deleteStackInput: AWS.CloudFormation.DeleteStackInput = { StackName: element.StackName };
|
const deleteStackInput: AWS.CloudFormation.DeleteStackInput = { StackName: element.StackName };
|
||||||
await CF.deleteStack(deleteStackInput).promise();
|
await CF.deleteStack(deleteStackInput).promise();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await AwsCliCommands.awsListTasks(async (taskElement, element) => {
|
await AwsCliCommands.awsListTasks(async (taskElement, element) => {
|
||||||
if (deleteResources) {
|
if (deleteResources && (!OneDayOlderOnly || AwsCliCommands.isOlderThan1day(taskElement.CreatedAt))) {
|
||||||
|
CloudRunnerLogger.log(`Stopping task ${taskElement.containers?.[0].name}`);
|
||||||
await ecs.stopTask({ task: taskElement.taskArn || '', cluster: element }).promise();
|
await ecs.stopTask({ task: taskElement.taskArn || '', cluster: element }).promise();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
await AwsCliCommands.awsListLogGroups(async (element) => {
|
||||||
|
if (deleteResources && (!OneDayOlderOnly || AwsCliCommands.isOlderThan1day(new Date(element.createdAt)))) {
|
||||||
|
CloudRunnerLogger.log(`Deleting ${element.logGroupName}`);
|
||||||
|
await cwl.deleteLogGroup({ logGroupName: element.logGroupName || '' }).promise();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,6 @@ class Kubernetes implements ProviderInterface {
|
|||||||
this.podName,
|
this.podName,
|
||||||
'main',
|
'main',
|
||||||
this.namespace,
|
this.namespace,
|
||||||
CloudRunnerLogger.log,
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
@ -4,7 +4,7 @@ import CloudRunnerLogger from '../../services/cloud-runner-logger';
|
|||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import { CloudRunnerStatics } from '../../cloud-runner-statics';
|
import { CloudRunnerStatics } from '../../cloud-runner-statics';
|
||||||
import waitUntil from 'async-wait-until';
|
import waitUntil from 'async-wait-until';
|
||||||
import CloudRunner from '../../cloud-runner';
|
import { FollowLogStreamService } from '../../services/follow-log-stream-service';
|
||||||
|
|
||||||
class KubernetesTaskRunner {
|
class KubernetesTaskRunner {
|
||||||
static async runTask(
|
static async runTask(
|
||||||
@ -14,20 +14,23 @@ class KubernetesTaskRunner {
|
|||||||
podName: string,
|
podName: string,
|
||||||
containerName: string,
|
containerName: string,
|
||||||
namespace: string,
|
namespace: string,
|
||||||
logCallback: any,
|
|
||||||
) {
|
) {
|
||||||
CloudRunnerLogger.log(`Streaming logs from pod: ${podName} container: ${containerName} namespace: ${namespace}`);
|
CloudRunnerLogger.log(`Streaming logs from pod: ${podName} container: ${containerName} namespace: ${namespace}`);
|
||||||
const stream = new Writable();
|
const stream = new Writable();
|
||||||
let output = '';
|
let output = '';
|
||||||
let didStreamAnyLogs: boolean = false;
|
let didStreamAnyLogs: boolean = false;
|
||||||
|
let shouldReadLogs = true;
|
||||||
|
let shouldCleanup = true;
|
||||||
stream._write = (chunk, encoding, next) => {
|
stream._write = (chunk, encoding, next) => {
|
||||||
didStreamAnyLogs = true;
|
didStreamAnyLogs = true;
|
||||||
let message = chunk.toString().trimRight(`\n`);
|
let message = chunk.toString().trimRight(`\n`);
|
||||||
message = `[${CloudRunnerStatics.logPrefix}] ${message}`;
|
message = `[${CloudRunnerStatics.logPrefix}] ${message}`;
|
||||||
if (CloudRunner.buildParameters.cloudRunnerIntegrationTests) {
|
({ shouldReadLogs, shouldCleanup, output } = FollowLogStreamService.handleIteration(
|
||||||
output += message;
|
message,
|
||||||
}
|
shouldReadLogs,
|
||||||
logCallback(message);
|
shouldCleanup,
|
||||||
|
output,
|
||||||
|
));
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
const logOptions = {
|
const logOptions = {
|
||||||
|
34
src/model/cloud-runner/services/follow-log-stream-service.ts
Normal file
34
src/model/cloud-runner/services/follow-log-stream-service.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import CloudRunnerLogger from './cloud-runner-logger';
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import CloudRunner from '../cloud-runner';
|
||||||
|
import { CloudRunnerStatics } from '../cloud-runner-statics';
|
||||||
|
|
||||||
|
export class FollowLogStreamService {
|
||||||
|
public static handleIteration(message, shouldReadLogs, shouldCleanup, output) {
|
||||||
|
if (message.includes(`---${CloudRunner.buildParameters.logId}`)) {
|
||||||
|
CloudRunnerLogger.log('End of log transmission received');
|
||||||
|
shouldReadLogs = false;
|
||||||
|
} else if (message.includes('Rebuilding Library because the asset database could not be found!')) {
|
||||||
|
core.warning('LIBRARY NOT FOUND!');
|
||||||
|
core.setOutput('library-found', 'false');
|
||||||
|
} else if (message.includes('Build succeeded')) {
|
||||||
|
core.setOutput('build-result', 'success');
|
||||||
|
} else if (message.includes('Build fail')) {
|
||||||
|
core.setOutput('build-result', 'failed');
|
||||||
|
core.setFailed('unity build failed');
|
||||||
|
core.error('BUILD FAILED!');
|
||||||
|
} else if (CloudRunner.buildParameters.cloudRunnerIntegrationTests && message.includes(': Listening for Jobs')) {
|
||||||
|
core.setOutput('cloud runner stop watching', 'true');
|
||||||
|
shouldReadLogs = false;
|
||||||
|
shouldCleanup = false;
|
||||||
|
core.warning('cloud runner stop watching');
|
||||||
|
}
|
||||||
|
message = `[${CloudRunnerStatics.logPrefix}] ${message}`;
|
||||||
|
if (CloudRunner.buildParameters.cloudRunnerIntegrationTests) {
|
||||||
|
output += message;
|
||||||
|
}
|
||||||
|
CloudRunnerLogger.log(message);
|
||||||
|
|
||||||
|
return { shouldReadLogs, shouldCleanup, output };
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user