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

fix(apprunner): startCommand and environment are ignored in imageConfiguration #16939

Merged
merged 10 commits into from
Nov 30, 2021
46 changes: 37 additions & 9 deletions packages/@aws-cdk/aws-apprunner/lib/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@ export class Runtime {
private constructor(public readonly name: string) { }
}

/**
* The environment variable for the service.
*/
interface EnvironmentVariable {
readonly name: string;
readonly value: string;
}

/**
* Result of binding `Source` into a `Service`.
Expand Down Expand Up @@ -707,6 +714,10 @@ export class Service extends cdk.Resource {
private readonly props: ServiceProps;
private accessRole?: iam.IRole;
private source: SourceConfig;
/**
* Environment variables for this service
*/
private environment?: { [key: string]: string } = {};

/**
* The ARN of the Service.
Expand Down Expand Up @@ -798,22 +809,39 @@ export class Service extends cdk.Resource {

}
private renderCodeConfigurationValues(props: CodeConfigurationValues): any {
this.environment = props.environment;
return {
...props,
port: props.port,
buildCommand: props.buildCommand,
runtime: props.runtime.name,
runtimeEnvironmentVariables: this.renderEnvironmentVariables(),
startCommand: props.startCommand,
};
}
private renderImageRepository(): any {
const repo = this.source.imageRepository!;
if (repo.imageConfiguration?.port) {
// convert port from type number to string
return Object.assign(repo, {
imageConfiguration: {
port: repo.imageConfiguration.port.toString(),
},
});
this.environment = repo.imageConfiguration?.environment;
return Object.assign(repo, {
imageConfiguration: {
port: repo.imageConfiguration?.port?.toString(),
startCommand: repo.imageConfiguration?.startCommand,
runtimeEnvironmentVariables: this.renderEnvironmentVariables(),
},
});
}

private renderEnvironmentVariables(): EnvironmentVariable[] | undefined {
if (this.environment) {
let env: EnvironmentVariable[] = [];
for (const [key, value] of Object.entries(this.environment)) {
if (key.startsWith('AWSAPPRUNNER')) {
throw new Error(`Environment variable key ${key} with a prefix of AWSAPPRUNNER is not allowed`);
}
env.push({ name: key, value: value });
}
return env;
} else {
return repo;
return undefined;
}
}

Expand Down
167 changes: 167 additions & 0 deletions packages/@aws-cdk/aws-apprunner/test/service.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as path from 'path';
import '@aws-cdk/assert-internal/jest';
import { Template } from '@aws-cdk/assertions';
import * as ecr from '@aws-cdk/aws-ecr';
import * as ecr_assets from '@aws-cdk/aws-ecr-assets';
Expand Down Expand Up @@ -32,6 +33,92 @@ test('create a service with ECR Public(image repository type: ECR_PUBLIC)', () =
});
});

test('custom environment variables and start commands are allowed for imageConfiguration with defined port', () => {
// GIVEN
const app = new cdk.App();
const stack = new cdk.Stack(app, 'demo-stack');
// WHEN
new Service(stack, 'DemoService', {
source: Source.fromEcrPublic({
imageConfiguration: {
port: 8000,
environment: {
foo: 'fooval',
bar: 'barval',
},
startCommand: '/root/start-command.sh',
},
imageIdentifier: 'public.ecr.aws/aws-containers/hello-app-runner:latest',
}),
});
// we should have the service
Template.fromStack(stack).hasResourceProperties('AWS::AppRunner::Service', {
SourceConfiguration: {
AuthenticationConfiguration: {},
ImageRepository: {
ImageConfiguration: {
Port: '8000',
RuntimeEnvironmentVariables: [
{
Name: 'foo',
Value: 'fooval',
},
{
Name: 'bar',
Value: 'barval',
},
],
StartCommand: '/root/start-command.sh',
},
ImageIdentifier: 'public.ecr.aws/aws-containers/hello-app-runner:latest',
ImageRepositoryType: 'ECR_PUBLIC',
},
},
});
});

test('custom environment variables and start commands are allowed for imageConfiguration with port undefined', () => {
// GIVEN
const app = new cdk.App();
const stack = new cdk.Stack(app, 'demo-stack');
// WHEN
new Service(stack, 'DemoService', {
source: Source.fromEcrPublic({
imageConfiguration: {
environment: {
foo: 'fooval',
bar: 'barval',
},
startCommand: '/root/start-command.sh',
},
imageIdentifier: 'public.ecr.aws/aws-containers/hello-app-runner:latest',
}),
});
// we should have the service
Template.fromStack(stack).hasResourceProperties('AWS::AppRunner::Service', {
SourceConfiguration: {
AuthenticationConfiguration: {},
ImageRepository: {
ImageConfiguration: {
RuntimeEnvironmentVariables: [
{
Name: 'foo',
Value: 'fooval',
},
{
Name: 'bar',
Value: 'barval',
},
],
StartCommand: '/root/start-command.sh',
},
ImageIdentifier: 'public.ecr.aws/aws-containers/hello-app-runner:latest',
ImageRepositoryType: 'ECR_PUBLIC',
},
},
});
});

test('create a service from existing ECR repository(image repository type: ECR)', () => {
// GIVEN
const app = new cdk.App();
Expand Down Expand Up @@ -249,6 +336,66 @@ test('create a service with github repository - undefined branch name is allowed
});
});

test('create a service with github repository - buildCommand, environment and startCommand are allowed', () => {
// GIVEN
const app = new cdk.App();
const stack = new cdk.Stack(app, 'demo-stack');
// WHEN
new Service(stack, 'DemoService', {
source: Source.fromGitHub({
repositoryUrl: 'https://github.com/aws-containers/hello-app-runner',
configurationSource: ConfigurationSourceType.API,
codeConfigurationValues: {
runtime: Runtime.PYTHON_3,
port: '8000',
buildCommand: '/root/build.sh',
environment: {
foo: 'fooval',
bar: 'barval',
},
startCommand: '/root/start.sh',
},
connection: GitHubConnection.fromConnectionArn('MOCK'),
}),
});

// THEN
// we should have the service with the branch value as 'main'
Template.fromStack(stack).hasResourceProperties('AWS::AppRunner::Service', {
SourceConfiguration: {
AuthenticationConfiguration: {
ConnectionArn: 'MOCK',
},
CodeRepository: {
CodeConfiguration: {
CodeConfigurationValues: {
Port: '8000',
Runtime: 'PYTHON_3',
BuildCommand: '/root/build.sh',
RuntimeEnvironmentVariables: [
{
Name: 'foo',
Value: 'fooval',
},
{
Name: 'bar',
Value: 'barval',
},
],
StartCommand: '/root/start.sh',
},
ConfigurationSource: 'API',
},
RepositoryUrl: 'https://github.com/aws-containers/hello-app-runner',
SourceCodeVersion: {
Type: 'BRANCH',
Value: 'main',
},
},
},
});
});


test('import from service name', () => {
// GIVEN
Expand Down Expand Up @@ -417,3 +564,23 @@ test('custom cpu and memory units are allowed', () => {
},
});
});

test('environment variable with a prefix of AWSAPPRUNNER should throw an error', () => {
// GIVEN
const app = new cdk.App();
const stack = new cdk.Stack(app, 'demo-stack');
// WHEN
// we should have the service
expect(() => {
new Service(stack, 'DemoService', {
source: Source.fromEcrPublic({
imageConfiguration: {
environment: {
AWSAPPRUNNER_FOO: 'bar',
},
},
imageIdentifier: 'public.ecr.aws/aws-containers/hello-app-runner:latest',
}),
});
}).toThrow('Environment variable key AWSAPPRUNNER_FOO with a prefix of AWSAPPRUNNER is not allowed');
});