From cf52a91d57a534ba9efa681307d12ec84e958a66 Mon Sep 17 00:00:00 2001 From: khanhtc1202 Date: Tue, 5 Jan 2021 23:37:43 +0900 Subject: [PATCH 01/10] Add FunctionManifest --- examples/lambda/simple/.pipe.yaml | 4 - examples/lambda/simple/function.yaml | 11 +++ .../piped/cloudprovider/lambda/function.go | 84 +++++++++++++++++++ 3 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 examples/lambda/simple/function.yaml create mode 100644 pkg/app/piped/cloudprovider/lambda/function.go diff --git a/examples/lambda/simple/.pipe.yaml b/examples/lambda/simple/.pipe.yaml index aee7dfee97..c99314e816 100644 --- a/examples/lambda/simple/.pipe.yaml +++ b/examples/lambda/simple/.pipe.yaml @@ -2,7 +2,3 @@ # https://docs.aws.amazon.com/lambda/latest/dg/configuration-versions.html apiVersion: pipecd.dev/v1beta1 kind: LambdaApp -spec: - input: - # Lambda code sourced from the same Git repository. - path: lambdas/helloworld diff --git a/examples/lambda/simple/function.yaml b/examples/lambda/simple/function.yaml new file mode 100644 index 0000000000..e318d52fa6 --- /dev/null +++ b/examples/lambda/simple/function.yaml @@ -0,0 +1,11 @@ +apiVersion: pipecd.dev/v1beta1 +kind: Function +metadata: + name: SimpleFunction +spec: + template: + metadata: + tags: + app: simple + spec: + image: ecr.ap-northeast-1.amazonaws.com/lambda-test:v0.0.1 \ No newline at end of file diff --git a/pkg/app/piped/cloudprovider/lambda/function.go b/pkg/app/piped/cloudprovider/lambda/function.go new file mode 100644 index 0000000000..8b2836da3b --- /dev/null +++ b/pkg/app/piped/cloudprovider/lambda/function.go @@ -0,0 +1,84 @@ +// Copyright 2020 The PipeCD Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package lambda + +import ( + "fmt" + "io/ioutil" + "strings" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/yaml" +) + +type FunctionManifest struct { + Name string + u *unstructured.Unstructured +} + +func loadFunctionManifest(path string) (FunctionManifest, error) { + data, err := ioutil.ReadFile(path) + if err != nil { + return FunctionManifest{}, err + } + return parseFunctionManifest(data) +} + +func parseFunctionManifest(data []byte) (FunctionManifest, error) { + var obj unstructured.Unstructured + if err := yaml.Unmarshal(data, &obj); err != nil { + return FunctionManifest{}, err + } + + return FunctionManifest{ + Name: obj.GetName(), + u: &obj, + }, nil +} + +func DecideRevisionName(fm FunctionManifest, commit string) (string, error) { + tag, err := FindImageTag(fm) + if err != nil { + return "", err + } + tag = strings.ReplaceAll(tag, ".", "") + + if len(commit) > 7 { + commit = commit[:7] + } + return fmt.Sprintf("%s-%s-%s", fm.Name, tag, commit), nil +} + +func FindImageTag(fm FunctionManifest) (string, error) { + imageURI, ok, err := unstructured.NestedString(fm.u.Object, "spec", "template", "spec", "image") + if err != nil { + return "", err + } + if !ok || imageURI == "" { + return "", fmt.Errorf("spec.template.spec.image is missing") + } + _, tag := parseContainerImage(imageURI) + return tag, nil +} + +func parseContainerImage(image string) (name, tag string) { + parts := strings.Split(image, ":") + if len(parts) == 2 { + tag = parts[1] + } + paths := strings.Split(parts[0], "/") + name = paths[len(paths)-1] + return +} From 55d588483bffb219d23cf0ad52aa2fc69437ea02 Mon Sep 17 00:00:00 2001 From: khanhtc1202 Date: Wed, 6 Jan 2021 01:08:40 +0900 Subject: [PATCH 02/10] Lambda provider client --- .../piped/cloudprovider/lambda/BUILD.bazel | 19 ++++- pkg/app/piped/cloudprovider/lambda/client.go | 71 +++++++++++++++++++ pkg/app/piped/cloudprovider/lambda/lambda.go | 69 ++++++++++++++++++ pkg/config/piped.go | 19 +++++ 4 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 pkg/app/piped/cloudprovider/lambda/client.go diff --git a/pkg/app/piped/cloudprovider/lambda/BUILD.bazel b/pkg/app/piped/cloudprovider/lambda/BUILD.bazel index d69867fb1b..b1c64a532c 100644 --- a/pkg/app/piped/cloudprovider/lambda/BUILD.bazel +++ b/pkg/app/piped/cloudprovider/lambda/BUILD.bazel @@ -2,7 +2,24 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "go_default_library", - srcs = ["lambda.go"], + srcs = [ + "client.go", + "lambda.go", + "function.go", + ], importpath = "github.com/pipe-cd/pipe/pkg/app/piped/cloudprovider/lambda", visibility = ["//visibility:public"], + deps = [ + "//pkg/config:go_default_library", + "@io_k8s_apimachinery//pkg/apis/meta/v1/unstructured:go_default_library", + "@io_k8s_sigs_yaml//:go_default_library", + "@com_github_aws_aws_sdk_go//aws:go_default_library", + "@com_github_aws_aws_sdk_go//aws/credentials:go_default_library", + "@com_github_aws_aws_sdk_go//aws/credentials/ec2rolecreds:go_default_library", + "@com_github_aws_aws_sdk_go//aws/ec2metadata:go_default_library", + "@com_github_aws_aws_sdk_go//aws/session:go_default_library", + "@com_github_aws_aws_sdk_go//service/lambda:go_default_library", + "@org_golang_x_sync//singleflight:go_default_library", + "@org_uber_go_zap//:go_default_library", + ], ) diff --git a/pkg/app/piped/cloudprovider/lambda/client.go b/pkg/app/piped/cloudprovider/lambda/client.go new file mode 100644 index 0000000000..4786310798 --- /dev/null +++ b/pkg/app/piped/cloudprovider/lambda/client.go @@ -0,0 +1,71 @@ +// Copyright 2020 The PipeCD Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package lambda + +import ( + "context" + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds" + "github.com/aws/aws-sdk-go/aws/ec2metadata" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/lambda" + "go.uber.org/zap" +) + +type client struct { + region string + client *lambda.Lambda + logger *zap.Logger +} + +func newClient(ctx context.Context, region, profile, credentialsFile string, logger *zap.Logger) (*client, error) { + if region == "" { + return nil, fmt.Errorf("region is required field") + } + + c := &client{ + region: region, + logger: logger.Named("lambda"), + } + + sess, err := session.NewSession() + if err != nil { + return nil, fmt.Errorf("failed to create a session: %w", err) + } + creds := credentials.NewChainCredentials( + []credentials.Provider{ + &credentials.EnvProvider{}, + &credentials.SharedCredentialsProvider{ + Filename: credentialsFile, + Profile: profile, + }, + &ec2rolecreds.EC2RoleProvider{ + Client: ec2metadata.New(sess), + }, + }, + ) + cfg := aws.NewConfig().WithRegion(c.region).WithCredentials(creds) + c.client = lambda.New(sess, cfg) + + return c, nil +} + +func (c *client) Apply(ctx context.Context) error { + // TODO implement + return nil +} diff --git a/pkg/app/piped/cloudprovider/lambda/lambda.go b/pkg/app/piped/cloudprovider/lambda/lambda.go index f5ddda0087..e1a3bed2eb 100644 --- a/pkg/app/piped/cloudprovider/lambda/lambda.go +++ b/pkg/app/piped/cloudprovider/lambda/lambda.go @@ -13,3 +13,72 @@ // limitations under the License. package lambda + +import ( + "context" + "path/filepath" + "sync" + + "go.uber.org/zap" + "golang.org/x/sync/singleflight" + + "github.com/pipe-cd/pipe/pkg/config" +) + +const ( + DefaultFunctionManifestFilename = "function.yaml" +) + +type Client interface { + Apply(ctx context.Context, fm FunctionManifest) error +} + +type Registry interface { + Client(ctx context.Context, name string, cfg *config.CloudProviderLambdaConfig, logger *zap.Logger) (Client, error) +} + +func LoadFunctionManifest(appDir, functionManifestFilename string) (FunctionManifest, error) { + if functionManifestFilename == "" { + functionManifestFilename = DefaultFunctionManifestFilename + } + path := filepath.Join(appDir, functionManifestFilename) + return loadFunctionManifest(path) +} + +type registry struct { + clients map[string]Client + mu sync.RWMutex + newGroup *singleflight.Group +} + +func (r *registry) Client(ctx context.Context, name string, cfg *config.CloudProviderLambdaConfig, logger *zap.Logger) (Client, error) { + r.mu.RLock() + client, ok := r.clients[name] + r.mu.RUnlock() + if ok { + return client, nil + } + + c, err, _ := r.newGroup.Do(name, func() (interface{}, error) { + return newClient(ctx, cfg.Region, cfg.Profile, cfg.CredentialsFile, logger) + }) + if err != nil { + return nil, err + } + + client = c.(Client) + r.mu.Lock() + r.clients[name] = client + r.mu.Unlock() + + return client, nil +} + +var defaultRegistry = ®istry{ + clients: make(map[string]Client), + newGroup: &singleflight.Group{}, +} + +func DefaultRegistry() Registry { + return defaultRegistry +} diff --git a/pkg/config/piped.go b/pkg/config/piped.go index 3be994781c..730bef8263 100644 --- a/pkg/config/piped.go +++ b/pkg/config/piped.go @@ -310,7 +310,26 @@ type CloudProviderCloudRunConfig struct { } type CloudProviderLambdaConfig struct { + // The region to send requests to. This parameter is required. + // e.g. "us-west-2" + // A full list of regions is: https://docs.aws.amazon.com/general/latest/gr/rande.html Region string `json:"region"` + // The AWS account ID associated with the registry that contains the repository + // in which to list images. The "default" registry is assumed by default. + RegistryID string `json:"registryId"` + // Path to the shared credentials file. + // + // Piped attempts to retrieve credentials in the following order: + // 1. from the environment variables. Available environment variables are: + // - AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY + // - AWS_SECRET_ACCESS_KEY or AWS_SECRET_KEY + // 2. from the given credentials file. + // 3. from the EC2 Instance Role + CredentialsFile string `json:"credentialsFile"` + // AWS Profile to extract credentials from the shared credentials file. + // If empty, the environment variable "AWS_PROFILE" is used. + // "default" is populated if the environment variable is also not set. + Profile string `json:"profile"` } type PipedAnalysisProvider struct { From 9d52491944f29a1ae8d65a76cddd5c28b3078d46 Mon Sep 17 00:00:00 2001 From: khanhtc1202 Date: Wed, 6 Jan 2021 11:21:39 +0900 Subject: [PATCH 03/10] Remove unused context on new lambda client --- pkg/app/piped/cloudprovider/lambda/client.go | 2 +- pkg/app/piped/cloudprovider/lambda/lambda.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/app/piped/cloudprovider/lambda/client.go b/pkg/app/piped/cloudprovider/lambda/client.go index 4786310798..d41d096b80 100644 --- a/pkg/app/piped/cloudprovider/lambda/client.go +++ b/pkg/app/piped/cloudprovider/lambda/client.go @@ -33,7 +33,7 @@ type client struct { logger *zap.Logger } -func newClient(ctx context.Context, region, profile, credentialsFile string, logger *zap.Logger) (*client, error) { +func newClient(region, profile, credentialsFile string, logger *zap.Logger) (*client, error) { if region == "" { return nil, fmt.Errorf("region is required field") } diff --git a/pkg/app/piped/cloudprovider/lambda/lambda.go b/pkg/app/piped/cloudprovider/lambda/lambda.go index e1a3bed2eb..4564e50937 100644 --- a/pkg/app/piped/cloudprovider/lambda/lambda.go +++ b/pkg/app/piped/cloudprovider/lambda/lambda.go @@ -34,7 +34,7 @@ type Client interface { } type Registry interface { - Client(ctx context.Context, name string, cfg *config.CloudProviderLambdaConfig, logger *zap.Logger) (Client, error) + Client(name string, cfg *config.CloudProviderLambdaConfig, logger *zap.Logger) (Client, error) } func LoadFunctionManifest(appDir, functionManifestFilename string) (FunctionManifest, error) { @@ -51,7 +51,7 @@ type registry struct { newGroup *singleflight.Group } -func (r *registry) Client(ctx context.Context, name string, cfg *config.CloudProviderLambdaConfig, logger *zap.Logger) (Client, error) { +func (r *registry) Client(name string, cfg *config.CloudProviderLambdaConfig, logger *zap.Logger) (Client, error) { r.mu.RLock() client, ok := r.clients[name] r.mu.RUnlock() @@ -60,7 +60,7 @@ func (r *registry) Client(ctx context.Context, name string, cfg *config.CloudPro } c, err, _ := r.newGroup.Do(name, func() (interface{}, error) { - return newClient(ctx, cfg.Region, cfg.Profile, cfg.CredentialsFile, logger) + return newClient(cfg.Region, cfg.Profile, cfg.CredentialsFile, logger) }) if err != nil { return nil, err From 3cdaad604be6907dbe96423f2d56242f0253f13e Mon Sep 17 00:00:00 2001 From: khanhtc1202 Date: Wed, 6 Jan 2021 15:02:37 +0900 Subject: [PATCH 04/10] Apply implement --- go.mod | 6 ++-- go.sum | 15 ++++++--- .../piped/cloudprovider/lambda/BUILD.bazel | 1 + pkg/app/piped/cloudprovider/lambda/client.go | 33 +++++++++++++++++-- .../piped/cloudprovider/lambda/function.go | 16 ++++++--- pkg/app/piped/cloudprovider/lambda/lambda.go | 2 +- pkg/config/piped.go | 5 ++- repositories.bzl | 26 +++++++++------ 8 files changed, 76 insertions(+), 28 deletions(-) diff --git a/go.mod b/go.mod index 7d34b4a09c..f3485a582b 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( cloud.google.com/go/firestore v1.2.0 cloud.google.com/go/storage v1.11.0 github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46 - github.com/aws/aws-sdk-go v1.34.5 + github.com/aws/aws-sdk-go v1.36.21 github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/docker/distribution v2.7.1+incompatible github.com/docker/go-metrics v0.0.1 // indirect @@ -28,7 +28,7 @@ require ( github.com/prometheus/client_golang v1.6.0 github.com/prometheus/client_model v0.2.0 github.com/prometheus/common v0.9.1 - github.com/robfig/cron/v3 v3.0.1 // indirect + github.com/robfig/cron/v3 v3.0.1 github.com/spf13/cobra v0.0.5 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.5.1 @@ -37,7 +37,7 @@ require ( go.uber.org/multierr v1.2.0 // indirect go.uber.org/zap v1.10.1-0.20190709142728-9a9fa7d4b5f0 golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de - golang.org/x/net v0.0.0-20200822124328-c89045814202 + golang.org/x/net v0.0.0-20201110031124-69a78807bb2b golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 google.golang.org/api v0.31.0 diff --git a/go.sum b/go.sum index b438e90a8b..09d72f65a9 100644 --- a/go.sum +++ b/go.sum @@ -72,8 +72,8 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/aws/aws-sdk-go v1.29.15 h1:0ms/213murpsujhsnxnNKNeVouW60aJqSd992Ks3mxs= github.com/aws/aws-sdk-go v1.29.15/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg= -github.com/aws/aws-sdk-go v1.34.5 h1:FwubVVX9u+kW9qDCjVzyWOdsL+W5wPq683wMk2R2GXk= -github.com/aws/aws-sdk-go v1.34.5/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go v1.36.21 h1:7CcrvoPUYex6CVeEK7/UZlYx5/AcVCWrl1FsX7fVvFw= +github.com/aws/aws-sdk-go v1.36.21/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -282,8 +282,10 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= -github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -395,7 +397,6 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.11 h1:DhHlBtkHWPYi8O2y31JkK0TF+DGM+51OopZjH/Ia5qI= github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -544,6 +545,8 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= @@ -610,6 +613,8 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200828194041-157a740278f4 h1:kCCpuwSAoYJPkNc6x0xT9yTtV4oKtARo4RGBQWOfg9E= golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/pkg/app/piped/cloudprovider/lambda/BUILD.bazel b/pkg/app/piped/cloudprovider/lambda/BUILD.bazel index b1c64a532c..a0f5ec5ad2 100644 --- a/pkg/app/piped/cloudprovider/lambda/BUILD.bazel +++ b/pkg/app/piped/cloudprovider/lambda/BUILD.bazel @@ -14,6 +14,7 @@ go_library( "@io_k8s_apimachinery//pkg/apis/meta/v1/unstructured:go_default_library", "@io_k8s_sigs_yaml//:go_default_library", "@com_github_aws_aws_sdk_go//aws:go_default_library", + "@com_github_aws_aws_sdk_go//aws/awserr:go_default_library", "@com_github_aws_aws_sdk_go//aws/credentials:go_default_library", "@com_github_aws_aws_sdk_go//aws/credentials/ec2rolecreds:go_default_library", "@com_github_aws_aws_sdk_go//aws/ec2metadata:go_default_library", diff --git a/pkg/app/piped/cloudprovider/lambda/client.go b/pkg/app/piped/cloudprovider/lambda/client.go index d41d096b80..88b5f97feb 100644 --- a/pkg/app/piped/cloudprovider/lambda/client.go +++ b/pkg/app/piped/cloudprovider/lambda/client.go @@ -19,6 +19,7 @@ import ( "fmt" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds" "github.com/aws/aws-sdk-go/aws/ec2metadata" @@ -65,7 +66,35 @@ func newClient(region, profile, credentialsFile string, logger *zap.Logger) (*cl return c, nil } -func (c *client) Apply(ctx context.Context) error { - // TODO implement +func (c *client) Apply(ctx context.Context, fm FunctionManifest, role string) error { + imageURI, err := fm.GetImageURI() + if err != nil { + return err + } + if role == "" { + return fmt.Errorf("role arn is required") + } + input := &lambda.CreateFunctionInput{ + Code: &lambda.FunctionCode{ImageUri: &imageURI}, + FunctionName: &fm.Name, + Role: &role, + } + _, err = c.client.CreateFunctionWithContext(ctx, input) + if err != nil { + if aerr, ok := err.(awserr.Error); ok { + switch aerr.Code() { + case lambda.ErrCodeInvalidParameterValueException: + return fmt.Errorf("invalid parameter given: %w", err) + case lambda.ErrCodeServiceException: + return fmt.Errorf("aws lambda service encountered an internal error: %w", err) + case lambda.ErrCodeCodeStorageExceededException: + return fmt.Errorf("total code size per account exceeded: %w", err) + case lambda.ErrCodeResourceNotFoundException: + case lambda.ErrCodeResourceNotReadyException: + return fmt.Errorf("resource error occurred: %w", err) + } + } + return fmt.Errorf("unknown error given: %w", err) + } return nil } diff --git a/pkg/app/piped/cloudprovider/lambda/function.go b/pkg/app/piped/cloudprovider/lambda/function.go index 8b2836da3b..0cabacb28e 100644 --- a/pkg/app/piped/cloudprovider/lambda/function.go +++ b/pkg/app/piped/cloudprovider/lambda/function.go @@ -28,6 +28,17 @@ type FunctionManifest struct { u *unstructured.Unstructured } +func (fm FunctionManifest) GetImageURI() (string, error) { + imageURI, ok, err := unstructured.NestedString(fm.u.Object, "spec", "template", "spec", "image") + if err != nil { + return "", err + } + if !ok || imageURI == "" { + return "", fmt.Errorf("spec.template.spec.image is missing") + } + return imageURI, nil +} + func loadFunctionManifest(path string) (FunctionManifest, error) { data, err := ioutil.ReadFile(path) if err != nil { @@ -62,13 +73,10 @@ func DecideRevisionName(fm FunctionManifest, commit string) (string, error) { } func FindImageTag(fm FunctionManifest) (string, error) { - imageURI, ok, err := unstructured.NestedString(fm.u.Object, "spec", "template", "spec", "image") + imageURI, err := fm.GetImageURI() if err != nil { return "", err } - if !ok || imageURI == "" { - return "", fmt.Errorf("spec.template.spec.image is missing") - } _, tag := parseContainerImage(imageURI) return tag, nil } diff --git a/pkg/app/piped/cloudprovider/lambda/lambda.go b/pkg/app/piped/cloudprovider/lambda/lambda.go index 4564e50937..8edfe1d402 100644 --- a/pkg/app/piped/cloudprovider/lambda/lambda.go +++ b/pkg/app/piped/cloudprovider/lambda/lambda.go @@ -30,7 +30,7 @@ const ( ) type Client interface { - Apply(ctx context.Context, fm FunctionManifest) error + Apply(ctx context.Context, fm FunctionManifest, role string) error } type Registry interface { diff --git a/pkg/config/piped.go b/pkg/config/piped.go index 730bef8263..67805a9cb7 100644 --- a/pkg/config/piped.go +++ b/pkg/config/piped.go @@ -314,9 +314,8 @@ type CloudProviderLambdaConfig struct { // e.g. "us-west-2" // A full list of regions is: https://docs.aws.amazon.com/general/latest/gr/rande.html Region string `json:"region"` - // The AWS account ID associated with the registry that contains the repository - // in which to list images. The "default" registry is assumed by default. - RegistryID string `json:"registryId"` + // The Amazon Resource Name (ARN) of the function's execution role. + Role string `json:"role"` // Path to the shared credentials file. // // Piped attempts to retrieve credentials in the following order: diff --git a/repositories.bzl b/repositories.bzl index 3a43bfdd97..5776b63507 100644 --- a/repositories.bzl +++ b/repositories.bzl @@ -47,8 +47,8 @@ def go_repositories(): go_repository( name = "com_github_aws_aws_sdk_go", importpath = "github.com/aws/aws-sdk-go", - sum = "h1:FwubVVX9u+kW9qDCjVzyWOdsL+W5wPq683wMk2R2GXk=", - version = "v1.34.5", + sum = "h1:7CcrvoPUYex6CVeEK7/UZlYx5/AcVCWrl1FsX7fVvFw=", + version = "v1.36.21", ) go_repository( name = "com_github_azure_go_autorest", @@ -817,8 +817,14 @@ def go_repositories(): go_repository( name = "com_github_jmespath_go_jmespath", importpath = "github.com/jmespath/go-jmespath", - sum = "h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=", - version = "v0.3.0", + sum = "h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=", + version = "v0.4.0", + ) + go_repository( + name = "com_github_jmespath_go_jmespath_internal_testify", + importpath = "github.com/jmespath/go-jmespath/internal/testify", + sum = "h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=", + version = "v1.5.1", ) go_repository( @@ -1237,8 +1243,8 @@ def go_repositories(): go_repository( name = "com_github_robfig_cron_v3", importpath = "github.com/robfig/cron/v3", - sum = "h1:kQ6Cb7aHOHTSzNVNEhmp8EcWKLb4CbiMW9h9VyIhO4E=", - version = "v3.0.0", + sum = "h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=", + version = "v3.0.1", ) go_repository( @@ -1823,8 +1829,8 @@ def go_repositories(): go_repository( name = "org_golang_x_net", importpath = "golang.org/x/net", - sum = "h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=", - version = "v0.0.0-20200822124328-c89045814202", + sum = "h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=", + version = "v0.0.0-20201110031124-69a78807bb2b", ) go_repository( name = "org_golang_x_oauth2", @@ -1841,8 +1847,8 @@ def go_repositories(): go_repository( name = "org_golang_x_sys", importpath = "golang.org/x/sys", - sum = "h1:kCCpuwSAoYJPkNc6x0xT9yTtV4oKtARo4RGBQWOfg9E=", - version = "v0.0.0-20200828194041-157a740278f4", + sum = "h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=", + version = "v0.0.0-20200930185726-fdedc70b468f", ) go_repository( name = "org_golang_x_text", From da4a21a6d4917ad06a4cf5a8bfee32779336e7fa Mon Sep 17 00:00:00 2001 From: khanhtc1202 Date: Wed, 6 Jan 2021 15:21:45 +0900 Subject: [PATCH 05/10] Remove unstructured field from FunctionManifest --- examples/lambda/simple/function.yaml | 2 +- pkg/app/piped/cloudprovider/lambda/client.go | 8 +--- .../piped/cloudprovider/lambda/function.go | 37 +++++++++---------- 3 files changed, 21 insertions(+), 26 deletions(-) diff --git a/examples/lambda/simple/function.yaml b/examples/lambda/simple/function.yaml index e318d52fa6..66c54cba72 100644 --- a/examples/lambda/simple/function.yaml +++ b/examples/lambda/simple/function.yaml @@ -1,5 +1,5 @@ apiVersion: pipecd.dev/v1beta1 -kind: Function +kind: LambdaFunction metadata: name: SimpleFunction spec: diff --git a/pkg/app/piped/cloudprovider/lambda/client.go b/pkg/app/piped/cloudprovider/lambda/client.go index 88b5f97feb..80cef3d785 100644 --- a/pkg/app/piped/cloudprovider/lambda/client.go +++ b/pkg/app/piped/cloudprovider/lambda/client.go @@ -67,19 +67,15 @@ func newClient(region, profile, credentialsFile string, logger *zap.Logger) (*cl } func (c *client) Apply(ctx context.Context, fm FunctionManifest, role string) error { - imageURI, err := fm.GetImageURI() - if err != nil { - return err - } if role == "" { return fmt.Errorf("role arn is required") } input := &lambda.CreateFunctionInput{ - Code: &lambda.FunctionCode{ImageUri: &imageURI}, + Code: &lambda.FunctionCode{ImageUri: &fm.ImageURI}, FunctionName: &fm.Name, Role: &role, } - _, err = c.client.CreateFunctionWithContext(ctx, input) + _, err := c.client.CreateFunctionWithContext(ctx, input) if err != nil { if aerr, ok := err.(awserr.Error); ok { switch aerr.Code() { diff --git a/pkg/app/piped/cloudprovider/lambda/function.go b/pkg/app/piped/cloudprovider/lambda/function.go index 0cabacb28e..529c513e66 100644 --- a/pkg/app/piped/cloudprovider/lambda/function.go +++ b/pkg/app/piped/cloudprovider/lambda/function.go @@ -23,20 +23,10 @@ import ( "sigs.k8s.io/yaml" ) +// FunctionManifest contains configuration for LambdaFunction. type FunctionManifest struct { - Name string - u *unstructured.Unstructured -} - -func (fm FunctionManifest) GetImageURI() (string, error) { - imageURI, ok, err := unstructured.NestedString(fm.u.Object, "spec", "template", "spec", "image") - if err != nil { - return "", err - } - if !ok || imageURI == "" { - return "", fmt.Errorf("spec.template.spec.image is missing") - } - return imageURI, nil + Name string + ImageURI string } func loadFunctionManifest(path string) (FunctionManifest, error) { @@ -53,12 +43,21 @@ func parseFunctionManifest(data []byte) (FunctionManifest, error) { return FunctionManifest{}, err } + imageURI, ok, err := unstructured.NestedString(obj.Object, "spec", "template", "spec", "image") + if err != nil { + return FunctionManifest{}, err + } + if !ok || imageURI == "" { + return FunctionManifest{}, fmt.Errorf("spec.template.spec.image is missing") + } + return FunctionManifest{ - Name: obj.GetName(), - u: &obj, + Name: obj.GetName(), + ImageURI: imageURI, }, nil } +// DecideRevisionName returns revision name to apply. func DecideRevisionName(fm FunctionManifest, commit string) (string, error) { tag, err := FindImageTag(fm) if err != nil { @@ -72,12 +71,12 @@ func DecideRevisionName(fm FunctionManifest, commit string) (string, error) { return fmt.Sprintf("%s-%s-%s", fm.Name, tag, commit), nil } +// FindImageTag parses image tag from given LambdaFunction manifest. func FindImageTag(fm FunctionManifest) (string, error) { - imageURI, err := fm.GetImageURI() - if err != nil { - return "", err + name, tag := parseContainerImage(fm.ImageURI) + if name == "" { + return "", fmt.Errorf("image name could not be empty") } - _, tag := parseContainerImage(imageURI) return tag, nil } From 3840f54b20e4099eb26ccb296e4129e4282067eb Mon Sep 17 00:00:00 2001 From: khanhtc1202 Date: Wed, 6 Jan 2021 15:24:54 +0900 Subject: [PATCH 06/10] Add EOF --- examples/lambda/simple/function.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/lambda/simple/function.yaml b/examples/lambda/simple/function.yaml index 66c54cba72..1e55e1999d 100644 --- a/examples/lambda/simple/function.yaml +++ b/examples/lambda/simple/function.yaml @@ -8,4 +8,4 @@ spec: tags: app: simple spec: - image: ecr.ap-northeast-1.amazonaws.com/lambda-test:v0.0.1 \ No newline at end of file + image: ecr.ap-northeast-1.amazonaws.com/lambda-test:v0.0.1 From 0d5f65994187f1caf51b7a1766ecf77812bca5b2 Mon Sep 17 00:00:00 2001 From: khanhtc1202 Date: Wed, 6 Jan 2021 16:12:47 +0900 Subject: [PATCH 07/10] FunctionName is required on parsing --- pkg/app/piped/cloudprovider/lambda/function.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/app/piped/cloudprovider/lambda/function.go b/pkg/app/piped/cloudprovider/lambda/function.go index 529c513e66..dcdb78652d 100644 --- a/pkg/app/piped/cloudprovider/lambda/function.go +++ b/pkg/app/piped/cloudprovider/lambda/function.go @@ -51,8 +51,13 @@ func parseFunctionManifest(data []byte) (FunctionManifest, error) { return FunctionManifest{}, fmt.Errorf("spec.template.spec.image is missing") } + functionName := obj.GetName() + if functionName == "" { + return FunctionManifest{}, fmt.Errorf("metadata.name is missing") + } + return FunctionManifest{ - Name: obj.GetName(), + Name: functionName, ImageURI: imageURI, }, nil } From f67aa3fac289cc706dc17758a21c651177568ef1 Mon Sep 17 00:00:00 2001 From: khanhtc1202 Date: Wed, 6 Jan 2021 17:07:08 +0900 Subject: [PATCH 08/10] Update files header --- pkg/app/piped/cloudprovider/lambda/client.go | 3 ++- pkg/app/piped/cloudprovider/lambda/function.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/app/piped/cloudprovider/lambda/client.go b/pkg/app/piped/cloudprovider/lambda/client.go index 80cef3d785..92ba6f08b7 100644 --- a/pkg/app/piped/cloudprovider/lambda/client.go +++ b/pkg/app/piped/cloudprovider/lambda/client.go @@ -1,4 +1,4 @@ -// Copyright 2020 The PipeCD Authors. +// Copyright 2021 The PipeCD Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -86,6 +86,7 @@ func (c *client) Apply(ctx context.Context, fm FunctionManifest, role string) er case lambda.ErrCodeCodeStorageExceededException: return fmt.Errorf("total code size per account exceeded: %w", err) case lambda.ErrCodeResourceNotFoundException: + fallthrough case lambda.ErrCodeResourceNotReadyException: return fmt.Errorf("resource error occurred: %w", err) } diff --git a/pkg/app/piped/cloudprovider/lambda/function.go b/pkg/app/piped/cloudprovider/lambda/function.go index dcdb78652d..5b08f3b426 100644 --- a/pkg/app/piped/cloudprovider/lambda/function.go +++ b/pkg/app/piped/cloudprovider/lambda/function.go @@ -1,4 +1,4 @@ -// Copyright 2020 The PipeCD Authors. +// Copyright 2021 The PipeCD Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From d92a7cd3f1cbc26dd7516433a670baf951bd9430 Mon Sep 17 00:00:00 2001 From: khanhtc1202 Date: Wed, 6 Jan 2021 21:22:32 +0900 Subject: [PATCH 09/10] Remove unused unstructured package --- examples/lambda/simple/function.yaml | 12 ++-- .../piped/cloudprovider/lambda/BUILD.bazel | 1 - pkg/app/piped/cloudprovider/lambda/client.go | 4 +- .../piped/cloudprovider/lambda/function.go | 66 ++++++++++++------- pkg/app/piped/cloudprovider/lambda/lambda.go | 4 +- 5 files changed, 50 insertions(+), 37 deletions(-) diff --git a/examples/lambda/simple/function.yaml b/examples/lambda/simple/function.yaml index 1e55e1999d..0c29d74fd6 100644 --- a/examples/lambda/simple/function.yaml +++ b/examples/lambda/simple/function.yaml @@ -1,11 +1,7 @@ apiVersion: pipecd.dev/v1beta1 kind: LambdaFunction -metadata: - name: SimpleFunction spec: - template: - metadata: - tags: - app: simple - spec: - image: ecr.ap-northeast-1.amazonaws.com/lambda-test:v0.0.1 + name: SimpleFunction + image: ecr.ap-northeast-1.amazonaws.com/lambda-test:v0.0.1 + tags: + app: simple diff --git a/pkg/app/piped/cloudprovider/lambda/BUILD.bazel b/pkg/app/piped/cloudprovider/lambda/BUILD.bazel index a0f5ec5ad2..d6d383b672 100644 --- a/pkg/app/piped/cloudprovider/lambda/BUILD.bazel +++ b/pkg/app/piped/cloudprovider/lambda/BUILD.bazel @@ -11,7 +11,6 @@ go_library( visibility = ["//visibility:public"], deps = [ "//pkg/config:go_default_library", - "@io_k8s_apimachinery//pkg/apis/meta/v1/unstructured:go_default_library", "@io_k8s_sigs_yaml//:go_default_library", "@com_github_aws_aws_sdk_go//aws:go_default_library", "@com_github_aws_aws_sdk_go//aws/awserr:go_default_library", diff --git a/pkg/app/piped/cloudprovider/lambda/client.go b/pkg/app/piped/cloudprovider/lambda/client.go index 92ba6f08b7..9b722bd282 100644 --- a/pkg/app/piped/cloudprovider/lambda/client.go +++ b/pkg/app/piped/cloudprovider/lambda/client.go @@ -71,8 +71,8 @@ func (c *client) Apply(ctx context.Context, fm FunctionManifest, role string) er return fmt.Errorf("role arn is required") } input := &lambda.CreateFunctionInput{ - Code: &lambda.FunctionCode{ImageUri: &fm.ImageURI}, - FunctionName: &fm.Name, + Code: &lambda.FunctionCode{ImageUri: &fm.Spec.ImageURI}, + FunctionName: &fm.Spec.Name, Role: &role, } _, err := c.client.CreateFunctionWithContext(ctx, input) diff --git a/pkg/app/piped/cloudprovider/lambda/function.go b/pkg/app/piped/cloudprovider/lambda/function.go index 5b08f3b426..dfb8155274 100644 --- a/pkg/app/piped/cloudprovider/lambda/function.go +++ b/pkg/app/piped/cloudprovider/lambda/function.go @@ -19,14 +19,48 @@ import ( "io/ioutil" "strings" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "sigs.k8s.io/yaml" ) -// FunctionManifest contains configuration for LambdaFunction. +const ( + versionV1Beta1 = "pipecd.dev/v1beta1" + FunctionManifestKind = "LambdaFunction" +) + type FunctionManifest struct { - Name string - ImageURI string + Kind string `json:"kind"` + APIVersion string `json:"apiVersion,omitempty"` + Spec FunctionManifestSpec `json:"spec"` +} + +func (fm *FunctionManifest) validate() error { + if fm.APIVersion != versionV1Beta1 { + return fmt.Errorf("unsupported version: %s", fm.APIVersion) + } + if fm.Kind != FunctionManifestKind { + return fmt.Errorf("invalid manifest kind given: %s", fm.Kind) + } + if err := fm.Spec.validate(); err != nil { + return err + } + return nil +} + +// FunctionManifestSpec contains configuration for LambdaFunction. +type FunctionManifestSpec struct { + Name string `json:"name"` + ImageURI string `json:"image"` + Tags map[string]string `json:"tags,omitempty"` +} + +func (fmp FunctionManifestSpec) validate() error { + if len(fmp.Name) == 0 { + return fmt.Errorf("lambda function is missing") + } + if len(fmp.ImageURI) == 0 { + return fmt.Errorf("image uri is missing") + } + return nil } func loadFunctionManifest(path string) (FunctionManifest, error) { @@ -38,28 +72,14 @@ func loadFunctionManifest(path string) (FunctionManifest, error) { } func parseFunctionManifest(data []byte) (FunctionManifest, error) { - var obj unstructured.Unstructured + var obj FunctionManifest if err := yaml.Unmarshal(data, &obj); err != nil { return FunctionManifest{}, err } - - imageURI, ok, err := unstructured.NestedString(obj.Object, "spec", "template", "spec", "image") - if err != nil { + if err := obj.validate(); err != nil { return FunctionManifest{}, err } - if !ok || imageURI == "" { - return FunctionManifest{}, fmt.Errorf("spec.template.spec.image is missing") - } - - functionName := obj.GetName() - if functionName == "" { - return FunctionManifest{}, fmt.Errorf("metadata.name is missing") - } - - return FunctionManifest{ - Name: functionName, - ImageURI: imageURI, - }, nil + return obj, nil } // DecideRevisionName returns revision name to apply. @@ -73,12 +93,12 @@ func DecideRevisionName(fm FunctionManifest, commit string) (string, error) { if len(commit) > 7 { commit = commit[:7] } - return fmt.Sprintf("%s-%s-%s", fm.Name, tag, commit), nil + return fmt.Sprintf("%s-%s-%s", fm.Spec.Name, tag, commit), nil } // FindImageTag parses image tag from given LambdaFunction manifest. func FindImageTag(fm FunctionManifest) (string, error) { - name, tag := parseContainerImage(fm.ImageURI) + name, tag := parseContainerImage(fm.Spec.ImageURI) if name == "" { return "", fmt.Errorf("image name could not be empty") } diff --git a/pkg/app/piped/cloudprovider/lambda/lambda.go b/pkg/app/piped/cloudprovider/lambda/lambda.go index 8edfe1d402..fa0875e45d 100644 --- a/pkg/app/piped/cloudprovider/lambda/lambda.go +++ b/pkg/app/piped/cloudprovider/lambda/lambda.go @@ -25,9 +25,7 @@ import ( "github.com/pipe-cd/pipe/pkg/config" ) -const ( - DefaultFunctionManifestFilename = "function.yaml" -) +const DefaultFunctionManifestFilename = "function.yaml" type Client interface { Apply(ctx context.Context, fm FunctionManifest, role string) error From 2584986601354051dbafe613511fcd793f2dcd91 Mon Sep 17 00:00:00 2001 From: khanhtc1202 Date: Wed, 6 Jan 2021 22:45:46 +0900 Subject: [PATCH 10/10] Add tests --- .../piped/cloudprovider/lambda/BUILD.bazel | 12 +++- .../piped/cloudprovider/lambda/function.go | 4 +- .../cloudprovider/lambda/function_test.go | 68 +++++++++++++++++++ 3 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 pkg/app/piped/cloudprovider/lambda/function_test.go diff --git a/pkg/app/piped/cloudprovider/lambda/BUILD.bazel b/pkg/app/piped/cloudprovider/lambda/BUILD.bazel index d6d383b672..0f5c166e54 100644 --- a/pkg/app/piped/cloudprovider/lambda/BUILD.bazel +++ b/pkg/app/piped/cloudprovider/lambda/BUILD.bazel @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", @@ -23,3 +23,13 @@ go_library( "@org_uber_go_zap//:go_default_library", ], ) + +go_test( + name = "go_default_test", + size = "small", + srcs = ["function_test.go"], + embed = [":go_default_library"], + deps = [ + "@com_github_stretchr_testify//assert:go_default_library", + ], +) diff --git a/pkg/app/piped/cloudprovider/lambda/function.go b/pkg/app/piped/cloudprovider/lambda/function.go index dfb8155274..4f5469c1bc 100644 --- a/pkg/app/piped/cloudprovider/lambda/function.go +++ b/pkg/app/piped/cloudprovider/lambda/function.go @@ -24,7 +24,7 @@ import ( const ( versionV1Beta1 = "pipecd.dev/v1beta1" - FunctionManifestKind = "LambdaFunction" + functionManifestKind = "LambdaFunction" ) type FunctionManifest struct { @@ -37,7 +37,7 @@ func (fm *FunctionManifest) validate() error { if fm.APIVersion != versionV1Beta1 { return fmt.Errorf("unsupported version: %s", fm.APIVersion) } - if fm.Kind != FunctionManifestKind { + if fm.Kind != functionManifestKind { return fmt.Errorf("invalid manifest kind given: %s", fm.Kind) } if err := fm.Spec.validate(); err != nil { diff --git a/pkg/app/piped/cloudprovider/lambda/function_test.go b/pkg/app/piped/cloudprovider/lambda/function_test.go new file mode 100644 index 0000000000..8f75127adc --- /dev/null +++ b/pkg/app/piped/cloudprovider/lambda/function_test.go @@ -0,0 +1,68 @@ +// Copyright 2021 The PipeCD Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package lambda + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestparseFunctionManifest(t *testing.T) { + testcases := []struct { + name string + data string + wantSpec interface{} + wantErr bool + }{ + { + name: "correct config for LambdaFunction", + data: `{ + "apiVersion": "pipecd.dev/v1beta1", + "kind": "LambdaFunction", + "spec": { + "name": "SimpleFunction", + "image": "ecr.region.amazonaws.com/lambda-simple-function:v0.0.1" + } +}`, + wantSpec: FunctionManifest{ + Kind: "LambdaFunction", + APIVersion: "pipecd.dev/v1beta1", + Spec: FunctionManifestSpec{ + Name: "SimpleFunction", + ImageURI: "ecr.region.amazonaws.com/lambda-simple-function:v0.0.1", + }, + }, + wantErr: false, + }, + { + name: "missing required fields", + data: `{ + "apiVersion": "pipecd.dev/v1beta1", + "kind": "LambdaFunction", + "spec": {} +}`, + wantSpec: FunctionManifest{}, + wantErr: true, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + fm, err := parseFunctionManifest([]byte(tc.data)) + assert.Equal(t, tc.wantErr, err != nil) + assert.Equal(t, tc.wantSpec, fm) + }) + } +}