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

add ubuntu agent and cdn with lambda@edge resource for public access #134

Merged
merged 4 commits into from
Jun 17, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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',
rishabh6788 marked this conversation as resolved.
Show resolved Hide resolved
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