Skip to content

Commit

Permalink
Merge pull request #29 from benbjurstrom/main
Browse files Browse the repository at this point in the history
Add support for container images
  • Loading branch information
aarondfrancis authored Aug 13, 2021
2 parents 37ab313 + ed5e23a commit ffb21c5
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 5 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# Changelog
## Unreleased

## 0.3.2 - 2021-08-13

### Added

- Support for Container Images. [#29](https://github.com/hammerstonedev/sidecar/pull/29)

## 0.3.1 - 2021-07-31

### Fixed
Expand Down
30 changes: 30 additions & 0 deletions docs/functions/handlers-and-packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,36 @@ class ScreenshotFunction extends LambdaFunction
}
```

## Container Images

In addition to the standard runtimes AWS Lambda offers the ability to package your Lambda function code and dependencies as a container image of up to 10 GB in size.

To use a container image with Sidecar you must first build a Lambda compatible docker image and push it to the Amazon Elastic Container Registry (ECR). See [the official docs](https://docs.aws.amazon.com/lambda/latest/dg/images-create.html) for step-by-step instructions.

Once the container has been added to the registry update the Sidecar function's handler method to return the `Package::CONTAINER_HANDLER` constant. Finally, update the function's `package` method to return the ECR Image URI as shown below.

```php
use Hammerstone\Sidecar\LambdaFunction;
use Hammerstone\Sidecar\Package;

class ExampleFunction extends LambdaFunction
{
public function handler()
{
return Package::CONTAINER_HANDLER;
}

public function package()
{
return [
'ImageUri' => '123456789012.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest',
];
}
}
```

With the above configuration in place you can create and activate the AWS Lambda function with the `artisan sidecar:deploy --activate` command. From there you can use Sidecar to interact with the container image in the same manner as traditional runtimes.

## Further Reading

To read more about what you can and should include in your package, based on your runtime, see the following pages in Amazon's documentation:
Expand Down
9 changes: 7 additions & 2 deletions src/Clients/LambdaClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,16 @@ public function updateExistingFunction(LambdaFunction $function)
// and S3Key be top level instead of nested under `Code`.
$code = [
'FunctionName' => $config['FunctionName'],
'S3Bucket' => $config['Code']['S3Bucket'],
'S3Key' => $config['Code']['S3Key'],
'Publish' => $config['Publish']
];

if ($function->packageType() === 'Zip') {
$code['S3Bucket'] = $config['Code']['S3Bucket'];
$code['S3Key'] = $config['Code']['S3Key'];
} else {
$code = array_merge($code, $function->package());
}

$config = Arr::except($config, ['Code', 'Publish']);

$this->updateFunctionConfiguration($config);
Expand Down
6 changes: 5 additions & 1 deletion src/Deployment.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,11 @@ public function activate($prewarm = false)
protected function deploySingle(LambdaFunction $function)
{
Sidecar::log('Environment: ' . Sidecar::getEnvironment());
Sidecar::log('Runtime: ' . $function->runtime());
Sidecar::log('Package Type: ' . $function->packageType());
if ($function->packageType() === 'Zip') {
Sidecar::log('Runtime: ' . $function->runtime());
}


$function->beforeDeployment();

Expand Down
30 changes: 28 additions & 2 deletions src/LambdaFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Hammerstone\Sidecar\Exceptions\SidecarException;
use Hammerstone\Sidecar\Results\PendingResult;
use Hammerstone\Sidecar\Results\SettledResult;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;

abstract class LambdaFunction
Expand Down Expand Up @@ -197,6 +198,18 @@ public function runtime()
return 'nodejs14.x';
}

/**
* The type of deployment package. Set to Image for container
* image and set Zip for .zip file archive.
*
* @see https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-lambda-2015-03-31.html#createfunction
* @return string
*/
public function packageType()
{
return $this->handler() === Package::CONTAINER_HANDLER ? 'Image' : 'Zip';
}

/**
* An array full of ARN strings. Totally optional.
*
Expand Down Expand Up @@ -233,6 +246,7 @@ public function variables()
* If your file lived in a folder called "lambda", you can just prepend the
* path to your handler, leaving you with e.g. "lambda/image.generate".
*
* @see https://hammerstone.dev/sidecar/docs/main/functions/handlers-and-packages
* @see https://docs.aws.amazon.com/aws-sdk-php/v2/api/class-Aws.Lambda.LambdaClient.html#_createFunction
* @return string
*/
Expand Down Expand Up @@ -320,17 +334,29 @@ public function makePackage()
*/
public function toDeploymentArray()
{
return [
$config = [
'FunctionName' => $this->nameWithPrefix(),
'Runtime' => $this->runtime(),
'Role' => config('sidecar.execution_role'),
'Handler' => $this->handler(),
'Code' => $this->makePackage()->deploymentConfiguration(),
'Code' => $this->packageType() === 'Zip'
? $this->makePackage()->deploymentConfiguration()
: $this->package(),
'Description' => $this->description(),
'Timeout' => (int)$this->timeout(),
'MemorySize' => (int)$this->memory(),
'Layers' => $this->layers(),
'Publish' => true,
'PackageType' => $this->packageType(),
];

// For container image packages, we need to remove the Runtime
// and Handler since the container handles both of those
// things inherently.
if ($this->packageType() === 'Image') {
$config = Arr::except($config, ['Runtime', 'Handler']);
}

return $config;
}
}
9 changes: 9 additions & 0 deletions src/Package.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@

class Package
{
/**
* If your package is a container image, it does not require
* a handler function. Use this constant instead.
*
* @see https://hammerstone.dev/sidecar/docs/main/functions/handlers-and-packages
* @var string
*/
public const CONTAINER_HANDLER = 'container';

/**
* @var array
*/
Expand Down
33 changes: 33 additions & 0 deletions tests/Unit/LambdaClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Aws\Lambda\Exception\LambdaException;
use Hammerstone\Sidecar\Clients\LambdaClient;
use Hammerstone\Sidecar\Tests\Unit\Support\DeploymentTestFunction;
use Hammerstone\Sidecar\Tests\Unit\Support\DeploymentTestFunctionWithImage;
use Hammerstone\Sidecar\Tests\Unit\Support\EmptyTestFunction;
use Illuminate\Support\Facades\Event;
use Mockery;
Expand Down Expand Up @@ -199,6 +200,38 @@ public function update_existing_function()
$this->lambda->updateExistingFunction($function);
}

/** @test */
public function update_existing_image_function()
{
$function = new DeploymentTestFunctionWithImage;

$this->lambda->shouldReceive('functionExists')
->once()
->andReturn(false);

$this->lambda->shouldReceive('updateFunctionConfiguration')
->once()
->with([
'FunctionName' => 'test-FunctionName',
'Role' => null,
'Description' => 'test-Description [ffeb0fec]',
'Timeout' => 300,
'MemorySize' => 512,
'Layers' => [],
'PackageType' => 'Image',
]);

$this->lambda->shouldReceive('updateFunctionCode')
->once()
->with([
'FunctionName' => 'test-FunctionName',
'Publish' => 'test-Publish',
'ImageUri' => '123.dkr.ecr.us-west-2.amazonaws.com/image:latest',
]);

$this->lambda->updateExistingFunction($function);
}

/** @test */
public function existing_function_unchanged()
{
Expand Down
34 changes: 34 additions & 0 deletions tests/Unit/Support/DeploymentTestFunctionWithImage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php
/**
* @author Aaron Francis <[email protected]|https://twitter.com/aarondfrancis>
*/

namespace Hammerstone\Sidecar\Tests\Unit\Support;

use Hammerstone\Sidecar\LambdaFunction;
use Hammerstone\Sidecar\Package;

class DeploymentTestFunctionWithImage extends LambdaFunction
{
public function handler()
{
return Package::CONTAINER_HANDLER;
}

public function package()
{
return [
'ImageUri' => '123.dkr.ecr.us-west-2.amazonaws.com/image:latest',
];
}

public function nameWithPrefix()
{
return 'test-FunctionName';
}

public function description()
{
return 'test-Description';
}
}

0 comments on commit ffb21c5

Please sign in to comment.