Skip to content
This repository was archived by the owner on Dec 2, 2020. It is now read-only.

Commit 4a0369b

Browse files
committed
Publish layers to Lambda
1 parent 36ed5f4 commit 4a0369b

File tree

5 files changed

+110
-14
lines changed

5 files changed

+110
-14
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@ docker run lambda-php goodbye '{"name": "World"}'
1313
```
1414

1515
TODO:
16+
* Support image types other than local Docker images, where the layer format is tar. For example, layers directly from a Docker registry will be .tar.gz-formatted. OCI images can be either tar or tar.gz, based on the layer's media type.
17+
* De-dupe Lambda layers before publishing them (compare local file's SHA256 to published layer versions with the same name)
18+
* Accept additional parameters for PublishLayerVersion API (license, description, etc)
19+
* Support Lambda compatible runtimes other than 'provided'
1620
* Utility for creating a function deployment package from a Docker image
17-
* Support image types other than local Docker images ('docker-daemon' transport), where the layer format is tar. For example, layers directly from a Docker registry will be .tar.gz-formatted. OCI images can be either tar or tar.gz, based on the layer's media type.
1821

1922
## License Summary
2023

main.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"errors"
45
"log"
56
"os"
67

@@ -28,7 +29,7 @@ func createApp() (*cli.App, *cmdOptions) {
2829
app.Flags = []cli.Flag{
2930
cli.StringFlag{
3031
Name: "image, i",
31-
Usage: "Name of the source container image. Format is 'transport:details', such as 'docker-daemon:my-docker-image:latest",
32+
Usage: "Name of the source container image. For example, 'my-docker-image:latest'",
3233
Destination: &opts.image,
3334
},
3435
cli.StringFlag{
@@ -59,7 +60,23 @@ func createApp() (*cli.App, *cmdOptions) {
5960
}
6061

6162
func repackImageAction(opts *cmdOptions) error {
62-
return RepackImage(opts.image, opts.outputDir)
63+
layers, err := RepackImage("docker-daemon:"+opts.image, opts.outputDir)
64+
if err != nil {
65+
return err
66+
}
67+
68+
if len(layers) == 0 {
69+
return errors.New("No compatible layers found in the image (likely nothing found in /opt)")
70+
}
71+
72+
if !opts.dryRun {
73+
err := PublishLambdaLayers(opts.image, layers, opts.region, opts.layerNamespace, opts.outputDir)
74+
if err != nil {
75+
return err
76+
}
77+
}
78+
79+
return nil
6380
}
6481

6582
func main() {

publish_layers.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"io/ioutil"
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
10+
"github.com/aws/aws-sdk-go/aws"
11+
"github.com/aws/aws-sdk-go/aws/session"
12+
"github.com/aws/aws-sdk-go/service/lambda"
13+
)
14+
15+
func PublishLambdaLayers(sourceImageName string, layers []LambdaLayer, region string, layerPrefix string, resultsDir string) error {
16+
layerArns := []string{}
17+
18+
sess := session.Must(session.NewSessionWithOptions(session.Options{
19+
SharedConfigState: session.SharedConfigEnable,
20+
}))
21+
22+
svc := lambda.New(sess, &aws.Config{Region: aws.String(region)})
23+
24+
for _, layer := range layers {
25+
layerName := layerPrefix + "-" + strings.Replace(layer.Digest, ":", "-", -1)
26+
27+
layerContents, err := ioutil.ReadFile(layer.File)
28+
if err != nil {
29+
return err
30+
}
31+
32+
publishArgs := &lambda.PublishLayerVersionInput{
33+
CompatibleRuntimes: []*string{aws.String("provided")},
34+
Content: &lambda.LayerVersionContentInput{ZipFile: layerContents},
35+
Description: aws.String("created by img2lambda from image " + sourceImageName),
36+
LayerName: aws.String(layerName),
37+
}
38+
39+
resp, err := svc.PublishLayerVersion(publishArgs)
40+
if err != nil {
41+
return err
42+
}
43+
44+
layerArns = append(layerArns, *resp.LayerVersionArn)
45+
46+
err = os.Remove(layer.File)
47+
if err != nil {
48+
return err
49+
}
50+
}
51+
52+
jsonArns, err := json.MarshalIndent(layerArns, "", " ")
53+
if err != nil {
54+
return err
55+
}
56+
57+
resultsPath := filepath.Join(resultsDir, "layers.json")
58+
jsonFile, err := os.Create(resultsPath)
59+
if err != nil {
60+
return err
61+
}
62+
defer jsonFile.Close()
63+
64+
_, err = jsonFile.Write(jsonArns)
65+
if err != nil {
66+
return err
67+
}
68+
69+
return nil
70+
}

repack_image.go

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,17 @@ import (
1313
"github.com/pkg/errors"
1414
)
1515

16+
type LambdaLayer struct {
17+
Digest string
18+
File string
19+
}
20+
1621
// Converts container image to Lambda layer archive files
17-
func RepackImage(imageName string, layerOutputDir string) (retErr error) {
22+
func RepackImage(imageName string, layerOutputDir string) (layers []LambdaLayer, retErr error) {
1823
// Get image's layer data from image name
1924
ref, err := alltransports.ParseImageName(imageName)
2025
if err != nil {
21-
return err
26+
return nil, err
2227
}
2328

2429
sys := &types.SystemContext{}
@@ -29,16 +34,16 @@ func RepackImage(imageName string, layerOutputDir string) (retErr error) {
2934

3035
rawSource, err := ref.NewImageSource(ctx, sys)
3136
if err != nil {
32-
return err
37+
return nil, err
3338
}
3439

3540
src, err := image.FromSource(ctx, sys, rawSource)
3641
if err != nil {
3742
if closeErr := rawSource.Close(); closeErr != nil {
38-
return errors.Wrapf(err, " (close error: %v)", closeErr)
43+
return nil, errors.Wrapf(err, " (close error: %v)", closeErr)
3944
}
4045

41-
return err
46+
return nil, err
4247
}
4348
defer func() {
4449
if err := src.Close(); err != nil {
@@ -50,7 +55,7 @@ func RepackImage(imageName string, layerOutputDir string) (retErr error) {
5055

5156
// Unpack and inspect each image layer, copy relevant files to new Lambda layer
5257
if err := os.MkdirAll(layerOutputDir, 0777); err != nil {
53-
return err
58+
return nil, err
5459
}
5560

5661
lambdaLayerNum := 1
@@ -60,19 +65,20 @@ func RepackImage(imageName string, layerOutputDir string) (retErr error) {
6065

6166
layerStream, _, err := rawSource.GetBlob(ctx, layerInfo, cache)
6267
if err != nil {
63-
return err
68+
return nil, err
6469
}
6570
defer layerStream.Close()
6671

6772
fileCreated, err := RepackLayer(lambdaLayerFilename, layerStream)
6873
if err != nil {
69-
return err
74+
return nil, err
7075
}
7176

7277
if fileCreated {
7378
lambdaLayerNum++
79+
layers = append(layers, LambdaLayer{Digest: string(layerInfo.Digest), File: lambdaLayerFilename})
7480
}
7581
}
7682

77-
return nil
83+
return layers, nil
7884
}

repack_layer.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ func RepackLayer(outputFilename string, layerContents io.Reader) (created bool,
2626
defer t.Close()
2727

2828
// Walk the files in the tar
29-
z := (*archiver.Zip)(nil)
30-
out := (*os.File)(nil)
29+
var z *archiver.Zip
30+
var out *os.File
3131
defer func() {
3232
if z != nil {
3333
if err := z.Close(); err != nil {

0 commit comments

Comments
 (0)