Skip to content

Commit aa9a550

Browse files
adrianspacelyAdrian Brownaarondfrancis
authored
Add support for environment variable configuration. (#25)
* Add support for environment variable configuration. * Add docs. * Set environment variables before activation, only if they are an array * Docs Co-authored-by: Adrian Brown <adrian.brown@plantracker.com.au> Co-authored-by: Aaron Francis <aarondfrancis@gmail.com>
1 parent 00d5ce0 commit aa9a550

File tree

5 files changed

+160
-16
lines changed

5 files changed

+160
-16
lines changed

docs/functions/customization.md

Lines changed: 75 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ The only two things _required_ for a Sidecar function are the [package and the h
55

66
## Runtime
77

8-
Lambda supports multiple languages through the use of runtimes. You can choose any of the following runtimes by returning its corresponding identifier:
8+
Lambda supports multiple languages through the use of runtimes. You can choose any of the following runtimes by returning its corresponding identifier:
99

1010
- Node.js 14: `nodejs14.x`
1111
- Node.js 12: `nodejs12.x`
@@ -20,16 +20,16 @@ Lambda supports multiple languages through the use of runtimes. You can choose a
2020
- Java 8: `java8`
2121
- Go 1.x: `go1.x`
2222
- .NET Core 3.1: `dotnetcore3.1`
23-
- .NET Core 2.1: `dotnetcore2.1`
23+
- .NET Core 2.1: `dotnetcore2.1`
2424

2525
E.g. to use the Go runtime, you would return `go1.x`:
2626

2727
```php
2828
class ExampleFunction extends LambdaFunction
2929
{
30-
public function runtime() // [tl! focus:3]
30+
public function runtime() // [tl! focus:3]
3131
{
32-
return 'go1.x';
32+
return 'go1.x';
3333
}
3434
}
3535
```
@@ -49,7 +49,7 @@ To change the allocated memory of your function, return the number in megabytes.
4949
```php
5050
class ExampleFunction extends LambdaFunction
5151
{
52-
public function memory() // [tl! focus:4]
52+
public function memory() // [tl! focus:4]
5353
{
5454
// 2GB of memory
5555
return 2048;
@@ -80,7 +80,7 @@ class ExampleFunction extends LambdaFunction
8080

8181
## Layers
8282

83-
Some functions require extra code or data beyond what is in your code package. From [Amazon's documentation](https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html):
83+
Some functions require extra code or data beyond what is in your code package. From [Amazon's documentation](https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html):
8484

8585
> A Lambda layer is a .zip file archive that can contain additional code or data. A layer can contain libraries, a custom runtime, data, or configuration files. Layers promote code sharing and separation of responsibilities so that you can iterate faster on writing business logic.
8686
@@ -92,43 +92,102 @@ class ExampleFunction extends LambdaFunction
9292
{
9393
public function handler()
9494
{
95-
//
95+
//
9696
}
9797

9898
public function package()
9999
{
100100
//
101101
}
102-
103-
public function layers() // [tl! focus:start]
102+
103+
public function layers() // [tl! focus:start]
104104
{
105105
return [
106-
// Node Canvas from https://github.com/jwerre/node-canvas-lambda
107-
'arn:aws:lambda:us-east-2:XXXX:layer:node_canvas:1',
108-
];
106+
// Node Canvas from https://github.com/jwerre/node-canvas-lambda
107+
'arn:aws:lambda:us-east-2:XXXX:layer:node_canvas:1',
108+
];
109109
} // [tl! focus:end]
110110
}
111111
```
112112

113113
Note that your layers must be in the same AWS region as your Lambdas!
114114

115+
## Environment Variables
116+
117+
Some functions or layers may require configuration via Lambda environment variables. The Lambda runtime makes environment variables available to the code.
118+
119+
In this example below, we're providing a path to a font directory as an environment variable.
120+
```php
121+
class ExampleFunction extends LambdaFunction
122+
{
123+
public function handler()
124+
{
125+
//
126+
}
127+
128+
public function package()
129+
{
130+
//
131+
}
132+
133+
public function variables() // [tl! focus:start]
134+
{
135+
return [
136+
'FONTCONFIG_PATH' => '/opt/etc/fonts',
137+
];
138+
} // [tl! focus:end]
139+
}
140+
```
141+
142+
It is **very important** to note that if you allow Sidecar to manage your Lambda's environment variables, any changes made to environment variables in the AWS UI will be overwritten next time you deploy your function.
143+
144+
By default, Sidecar doesn't touch your Lambda's environment at all. Only when you return an array from `variables` will Sidecar take control of the env vars.
145+
146+
Another important thing to note is that Sidecar sets your environment variables as a part of the _activation_ process, not the _deploy_ process. This means that the environment variables will be pulled from the machine that calls `sidecar:activate`.
147+
148+
For example, if you are sharing secrets with your Lambda, then the values passed to your Lambda will be equal to the ones on the machine that called `sidecar:activate`, not the one that called `sidecar:deploy`:
149+
150+
```php
151+
class ExampleFunction extends LambdaFunction
152+
{
153+
public function handler()
154+
{
155+
//
156+
}
157+
158+
public function package()
159+
{
160+
//
161+
}
162+
163+
public function variables() // [tl! focus:start]
164+
{
165+
// Values will come from the "activating" machine.
166+
return [
167+
'aws_key' => config('services.aws.key'),
168+
'aws_secret' => config('services.aws.secret'),
169+
];
170+
} // [tl! focus:end]
171+
}
172+
```
173+
115174
## Name
116175

117176
Your function has a `name` method that determines how Sidecar names your Lambda functions. By default it is based on the name and path of your PHP class. You are free to change this if you want, but you're unlikely to need to.
118177

119178
```php
120179
class ExampleFunction extends LambdaFunction
121180
{
122-
public function name() // [tl! focus:3]
181+
public function name() // [tl! focus:3]
123182
{
124-
return 'Function Name';
183+
return 'Function Name';
125184
}
126185
}
127186
```
128187

129188
### Name Prefix
130189

131-
Regardless of what you choose for your function names, Sidecar will prepend the name of your app and the current environment.
190+
Regardless of what you choose for your function names, Sidecar will prepend the name of your app and the current environment.
132191

133192
Lambda function names must be unique, so adding these variables to your function names prevents collisions between different apps and environments.
134193

@@ -143,7 +202,7 @@ You likely won't need to change this, but if you do, *you must include the envir
143202
// Don't forget the environment!
144203
return 'My App ' . Sidecar::getEnvironment()
145204
}
146-
}
205+
}
147206
```
148207

149208
## Description

src/Deployment.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ protected function activateSingle(LambdaFunction $function, $prewarm)
123123
{
124124
$function->beforeActivation();
125125

126+
$this->setEnvironmentVariables($function);
127+
126128
if ($prewarm) {
127129
$this->warmLatestVersion($function);
128130
}
@@ -158,6 +160,29 @@ protected function updateExistingFunction(LambdaFunction $function)
158160
}
159161
}
160162

163+
/**
164+
* Add environment variables to the Lambda function, if they are provided.
165+
*
166+
* @param LambdaFunction $function
167+
*/
168+
protected function setEnvironmentVariables(LambdaFunction $function)
169+
{
170+
$variables = $function->variables();
171+
172+
if (!is_array($variables)) {
173+
return Sidecar::log('Environment variables not managed by Sidecar. Skipping.');
174+
}
175+
176+
Sidecar::log('Updating environment variables.');
177+
178+
$this->lambda->updateFunctionConfiguration([
179+
'FunctionName' => $function->nameWithPrefix(),
180+
'Environment' => [
181+
'Variables' => $variables,
182+
],
183+
]);
184+
}
185+
161186
/**
162187
* Send warming requests to the latest version.
163188
*

src/LambdaFunction.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,20 @@ public function layers()
209209
];
210210
}
211211

212+
/**
213+
* A key/value array of environment variables that will be injected into the
214+
* environment of the Lambda function. If Sidecar manages your environment
215+
* variables, it will overwrite all variables that you set through the
216+
* AWS UI. Return false to disable.
217+
*
218+
* @return bool|array
219+
*/
220+
public function variables()
221+
{
222+
// By default, Sidecar does not manage your environment variables.
223+
return false;
224+
}
225+
212226
/**
213227
* The function within your code that Lambda calls to begin execution.
214228
* For Node.js, it is the `module-name.export` value in your function.

tests/Unit/DeploymentTest.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Hammerstone\Sidecar\Events\BeforeFunctionsDeployed;
1515
use Hammerstone\Sidecar\Exceptions\NoFunctionsRegisteredException;
1616
use Hammerstone\Sidecar\Tests\Unit\Support\DeploymentTestFunction;
17+
use Hammerstone\Sidecar\Tests\Unit\Support\DeploymentTestFunctionWithVariables;
1718
use Illuminate\Support\Facades\Event;
1819
use Mockery;
1920

@@ -197,6 +198,31 @@ public function it_updates_and_activates_an_existing_function()
197198
$this->assertEvents($deployed = true, $activated = true);
198199
}
199200

201+
/** @test */
202+
public function it_sets_environment_variables()
203+
{
204+
$this->lambda->shouldReceive('functionExists')->andReturn(true);
205+
$this->lambda->shouldReceive('getVersions')->andReturn([]);
206+
$this->lambda->shouldReceive('updateExistingFunction')->once()->withArgs(function ($function) {
207+
return $function instanceof DeploymentTestFunctionWithVariables;
208+
});
209+
210+
$this->lambda->shouldReceive('updateFunctionConfiguration')->with([
211+
'FunctionName' => 'test-FunctionName',
212+
'Environment' => [
213+
'Variables' => [
214+
'env' => 'value'
215+
],
216+
],
217+
]);
218+
219+
$this->mockActivating();
220+
221+
DeploymentTestFunctionWithVariables::deploy($activate = true);
222+
223+
$this->assertEvents($deployed = true, $activated = true);
224+
}
225+
200226
/** @test */
201227
public function it_throws_an_exception_if_there_are_no_functions()
202228
{
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
/**
3+
* @author Aaron Francis <aarondfrancis@gmail.com|https://twitter.com/aarondfrancis>
4+
*/
5+
6+
namespace Hammerstone\Sidecar\Tests\Unit\Support;
7+
8+
use Hammerstone\Sidecar\LambdaFunction;
9+
use Hammerstone\Sidecar\WarmingConfig;
10+
11+
class DeploymentTestFunctionWithVariables extends DeploymentTestFunction
12+
{
13+
public function variables()
14+
{
15+
return [
16+
'env' => 'value'
17+
];
18+
}
19+
20+
}

0 commit comments

Comments
 (0)