Skip to content

Commit

Permalink
add ubuntu agent and cdn with lambda@edge resource for public access (#…
Browse files Browse the repository at this point in the history
…134)

Signed-off-by: Rishabh Singh <[email protected]>
  • Loading branch information
rishabh6788 authored Jun 17, 2022
1 parent 6421528 commit 131de5a
Show file tree
Hide file tree
Showing 17 changed files with 791 additions and 12 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/cdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@ jobs:
- name: Run CDK Build and Test
run: |
npm install
cd resources/cf-url-rewriter
npm install
cd -
npm run build
4 changes: 4 additions & 0 deletions bin/ci-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import { App } from '@aws-cdk/core';
import { CIStack } from '../lib/ci-stack';
import { CIConfigStack } from '../lib/ci-config-stack';
import { CiCdnStack } from '../lib/ci-cdn-stack';

const app = new App();

Expand All @@ -17,3 +18,6 @@ const defaultEnv: string = 'Dev';
const ciConfigStack = new CIConfigStack(app, `OpenSearch-CI-Config-${defaultEnv}`, {});

const ciStack = new CIStack(app, `OpenSearch-CI-${defaultEnv}`, {});

const ciCdnStack = new CiCdnStack(app, `OpenSearch-CI-Cdn-${defaultEnv}`, {});
ciCdnStack.addDependency(ciStack);
69 changes: 69 additions & 0 deletions lib/buildArtifacts/artifacts-public-access.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/**
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

import { Bucket } from '@aws-cdk/aws-s3';
import {
CloudFrontAllowedMethods, CloudFrontWebDistribution, LambdaEdgeEventType, OriginAccessIdentity,
} from '@aws-cdk/aws-cloudfront';
import { CanonicalUserPrincipal, PolicyStatement } from '@aws-cdk/aws-iam';
import { NodejsFunction } from '@aws-cdk/aws-lambda-nodejs';
import { Architecture, Runtime } from '@aws-cdk/aws-lambda';
import { CfnOutput, Duration } from '@aws-cdk/core';
import { CiCdnStack } from '../ci-cdn-stack';

export class ArtifactsPublicAccess {
constructor(stack: CiCdnStack, buildBucketArn: string) {
const buildBucket = Bucket.fromBucketArn(stack, 'artifactBuildBucket', `${buildBucketArn.toString()}`);

const originAccessIdentity = new OriginAccessIdentity(stack, 'cloudfront-OAI', {
comment: `OAI for ${buildBucket.bucketName}`,
});

buildBucket.addToResourcePolicy(new PolicyStatement({
actions: ['s3:GetObject'],
resources: [buildBucket.arnForObjects('*')],
principals: [new CanonicalUserPrincipal(originAccessIdentity.cloudFrontOriginAccessIdentityS3CanonicalUserId)],
}));

const urlRewriter = new NodejsFunction(stack, 'CfUrlRewriter', {
runtime: Runtime.NODEJS_14_X,
entry: `${__dirname}/../../resources/cf-url-rewriter/cf-url-rewriter.ts`,
handler: 'handler',
memorySize: 128,
architecture: Architecture.X86_64,
});

const distro = new CloudFrontWebDistribution(stack, 'CloudFrontBuildBucket', {
originConfigs: [
{
s3OriginSource: {
s3BucketSource: buildBucket,
originAccessIdentity,
},
behaviors: [
{
isDefaultBehavior: true,
compress: true,
allowedMethods: CloudFrontAllowedMethods.GET_HEAD,
lambdaFunctionAssociations: [{
eventType: LambdaEdgeEventType.VIEWER_REQUEST,
lambdaFunction: urlRewriter.currentVersion,
}],
defaultTtl: Duration.seconds(300),
},
],
},
],
});

new CfnOutput(stack, 'BuildDistributionDomainName', {
value: distro.distributionDomainName,
description: 'The domain name where the build artifacts will be available',
});
}
}
91 changes: 91 additions & 0 deletions lib/buildArtifacts/build-artifacts-permissions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/**
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

import { Stack } from '@aws-cdk/core';
import {
ArnPrincipal, Effect, Policy, PolicyStatement, Role,
} from '@aws-cdk/aws-iam';
import { CiCdnStack } from '../ci-cdn-stack';

export interface buildArtifactProps {
mainNodeArn: string,
agentNodeArn: string,
buildBucketArn: string
}

export class BuildArtifactsPermissions {
private static readonly BUNDLE_ROLE_NAME = 'opensearch-bundle';

constructor(stack: CiCdnStack, props: buildArtifactProps) {
const opensearchBundleRole = new Role(stack, BuildArtifactsPermissions.BUNDLE_ROLE_NAME, {
assumedBy: Role.fromRoleArn(stack, 'MainNodeRole', `${props.mainNodeArn}`),
roleName: 'opensearch-bundle',
});

opensearchBundleRole.assumeRolePolicy?.addStatements(new PolicyStatement({
actions: ['sts:AssumeRole'],
principals: [new ArnPrincipal(`${props.agentNodeArn}`)],
}));

const opensearchBundlePolicies = BuildArtifactsPermissions.getOpensearchBundlePolicies(stack, props.buildBucketArn);
opensearchBundlePolicies.forEach((policy) => {
policy.attachToRole(opensearchBundleRole);
});
}

private static getOpensearchBundlePolicies(scope: Stack, s3BucketArn: string): Policy[] {
const policies: Policy[] = [];
const signingPolicy = new Policy(scope, 'signing-policy', {
statements: [
new PolicyStatement(
{
actions: ['sts:AssumeRole'],
resources: ['arn:aws:iam::447201093745:role/OpenSearchSignerPGPSigning-ArtifactAccessRole'],
effect: Effect.ALLOW,
},
),
],
});

const openSearchBundlePolicy = new Policy(scope, 'opensearch-bundle-policy', {
statements: [
new PolicyStatement({
actions: [
's3:GetObject*',
's3:ListBucket',
],
resources: [`${s3BucketArn}`],
effect: Effect.ALLOW,
}),
new PolicyStatement(
{
actions: [
's3:GetObject*',
's3:ListBucket',
's3:PutObject',
's3:PutObjectLegalHold',
's3:PutObjectRetention',
's3:PutObjectTagging',
's3:PutObjectVersionTagging',
's3:Abort*',
],
resources: [
`${s3BucketArn}/*/builds/*`,
`${s3BucketArn}/*/shas/*`,
`${s3BucketArn}/*/dist/*`,
`${s3BucketArn}/*/index.json`,
],
effect: Effect.ALLOW,
},
),
],
});
policies.push(signingPolicy, openSearchBundlePolicy);
return policies;
}
}
30 changes: 30 additions & 0 deletions lib/ci-cdn-stack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

import {
Construct, Fn, Stack, StackProps,
} from '@aws-cdk/core';
import { BuildArtifactsPermissions } from './buildArtifacts/build-artifacts-permissions';
import { ArtifactsPublicAccess } from './buildArtifacts/artifacts-public-access';

export class CiCdnStack extends Stack {
constructor(scope: Construct, id: string, props: StackProps) {
super(scope, id, props);
const mainNodeRoleArn = Fn.importValue('mainNodeRoleArn');
const agentNodeRoleArn = Fn.importValue('agentNodeRoleArn');
const buildBucketArn = Fn.importValue('buildBucketArn');

const buildArtifacts = new BuildArtifactsPermissions(this, {
mainNodeArn: mainNodeRoleArn,
agentNodeArn: agentNodeRoleArn,
buildBucketArn,
});

const artifactPublicAccess = new ArtifactsPublicAccess(this, buildBucketArn);
}
}
15 changes: 13 additions & 2 deletions lib/ci-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@
import { FlowLogDestination, FlowLogTrafficType, Vpc } from '@aws-cdk/aws-ec2';
import { Secret } from '@aws-cdk/aws-secretsmanager';
import {
CfnParameter, Construct, Fn, Stack, StackProps,
CfnOutput,
CfnParameter, Construct, Fn, RemovalPolicy, Stack, StackProps,
} from '@aws-cdk/core';
import { ListenerCertificate } from '@aws-cdk/aws-elasticloadbalancingv2';
import { FileSystem } from '@aws-cdk/aws-efs';
import { Bucket } from '@aws-cdk/aws-s3';
import { CIConfigStack } from './ci-config-stack';
import { JenkinsMainNode } from './compute/jenkins-main-node';
import { JenkinsMonitoring } from './monitoring/ci-alarms';
Expand Down Expand Up @@ -97,7 +100,8 @@ export class CIStack extends Stack {
const certificateArn = Secret.fromSecretCompleteArn(this, 'certificateArn', importedArnSecretBucketValue.toString());
const listenerCertificate = ListenerCertificate.fromArn(certificateArn.secretValue.toString());
const agentNode = new AgentNodes(this);
const agentNodes: AgentNodeProps[] = [agentNode.AL2_X64, agentNode.AL2_X64_DOCKER_1, agentNode.AL2_ARM64, agentNode.AL2_ARM64_DOCKER_1];
const agentNodes: AgentNodeProps[] = [agentNode.AL2_X64, agentNode.AL2_X64_DOCKER_1, agentNode.AL2_ARM64, agentNode.AL2_ARM64_DOCKER_1,
agentNode.UBUNTU_X64_DOCKER];

const mainJenkinsNode = new JenkinsMainNode(this, {
vpc,
Expand Down Expand Up @@ -126,10 +130,17 @@ export class CIStack extends Stack {
useSsl,
});

const artifactBucket = new Bucket(this, 'BuildBucket');

this.monitoring = new JenkinsMonitoring(this, externalLoadBalancer, mainJenkinsNode);

if (additionalCommandsContext.toString() !== 'undefined') {
new RunAdditionalCommands(this, additionalCommandsContext.toString());
}

new CfnOutput(this, 'Artifact Bucket Arn', {
value: artifactBucket.bucketArn.toString(),
exportName: 'buildBucketArn',
});
}
}
8 changes: 4 additions & 4 deletions lib/compute/agent-node-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,12 @@ export class AgentNodeConfig {
});
}

public addAgentConfigToJenkinsYaml(templates: AgentNodeProps[], props: AgentNodeNetworkProps): any {
public addAgentConfigToJenkinsYaml(stack: Stack, templates: AgentNodeProps[], props: AgentNodeNetworkProps): any {
const jenkinsYaml: any = load(readFileSync(JenkinsMainNode.BASE_JENKINS_YAML_PATH, 'utf-8'));
const configTemplates: any = [];

templates.forEach((element) => {
configTemplates.push(this.getTemplate(element, props));
configTemplates.push(this.getTemplate(stack, element, props));
});

const agentNodeYamlConfig = [{
Expand All @@ -144,7 +144,7 @@ export class AgentNodeConfig {
return jenkinsYaml;
}

private getTemplate(config: AgentNodeProps, props: AgentNodeNetworkProps): { [x: string]: any; } {
private getTemplate(stack: Stack, config: AgentNodeProps, props: AgentNodeNetworkProps): { [x: string]: any; } {
return {
ami: config.amiId,
amiType:
Expand Down Expand Up @@ -177,7 +177,7 @@ export class AgentNodeConfig {
t2Unlimited: false,
tags: [{
name: 'Name',
value: 'OpenSearch-CI-Prod/AgentNode',
value: `${stack.stackName}/AgentNode`,
},
{
name: 'type',
Expand Down
9 changes: 9 additions & 0 deletions lib/compute/agent-nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export class AgentNodes {

readonly AL2_ARM64_DOCKER_1: AgentNodeProps;

readonly UBUNTU_X64_DOCKER: AgentNodeProps;

constructor(stack: Stack) {
this.AL2_X64 = {
workerLabelString: 'AL2-X64',
Expand Down Expand Up @@ -61,5 +63,12 @@ export class AgentNodes {
amiId: 'ami-020c52efb1a60f1ae',
initScript: 'sudo yum update -y || sudo yum update -y',
};
this.UBUNTU_X64_DOCKER = {
workerLabelString: 'Jenkins-Agent-Ubuntu2004-X64-m52xlarge-Docker-Builder',
instanceType: 'M52xlarge',
remoteUser: 'ubuntu',
amiId: 'ami-0f6ceb3b3687a3fba',
initScript: 'sudo apt-mark hold docker docker.io openssh-server && docker ps',
};
}
}
7 changes: 3 additions & 4 deletions lib/compute/jenkins-main-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export class JenkinsMainNode {
};

const agentNodeConfig = new AgentNodeConfig(stack, assumeRole);
const jenkinsyaml = JenkinsMainNode.addConfigtoJenkinsYaml(props, props, agentNodeConfig, props, agentNode);
const jenkinsyaml = JenkinsMainNode.addConfigtoJenkinsYaml(stack, props, props, agentNodeConfig, props, agentNode);
if (props.dataRetention) {
const efs = new FileSystem(stack, 'EFSfilesystem', {
vpc: props.vpc,
Expand Down Expand Up @@ -395,10 +395,9 @@ export class JenkinsMainNode {
];
}

public static addConfigtoJenkinsYaml(jenkinsMainNodeProps:JenkinsMainNodeProps, oidcProps: OidcFederateProps, agentNodeObject: AgentNodeConfig,
public static addConfigtoJenkinsYaml(stack: Stack, jenkinsMainNodeProps:JenkinsMainNodeProps, oidcProps: OidcFederateProps, agentNodeObject: AgentNodeConfig,
props: AgentNodeNetworkProps, agentNode: AgentNodeProps[]): string {
let updatedConfig = agentNodeObject.addAgentConfigToJenkinsYaml(agentNode, props);

let updatedConfig = agentNodeObject.addAgentConfigToJenkinsYaml(stack, agentNode, props);
if (oidcProps.runWithOidc) {
updatedConfig = OidcConfig.addOidcConfigToJenkinsYaml(updatedConfig, oidcProps.adminUsers);
}
Expand Down
Loading

0 comments on commit 131de5a

Please sign in to comment.