Skip to content

Commit

Permalink
fix(apprunner): startCommand and environment are ignored in imageConf…
Browse files Browse the repository at this point in the history
…iguration (#16939)

This PR addresses the following issues

1. custom environment variables and start commands should be allowed for `imageConfiguration`
2. buildCommand, environment and startCommand should be allowed for `codeConfigurationValues`


Fixes: #16812 

- [x] add tests
- [x] implementation

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
pahud committed Nov 30, 2021
1 parent c62377e commit d911c58
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 9 deletions.
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');
});

0 comments on commit d911c58

Please sign in to comment.