Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Plan Preview for Lambda #5046

Merged
merged 8 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion pkg/app/pipectl/cmd/planpreview/planpreview_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,13 @@ NOTE: An error occurred while building plan-preview for applications of the foll
ApplicationKind: model.ApplicationKind_ECS,
Error: "wrong application configuration",
},
{
ApplicationId: "app-6",
ApplicationName: "app-6",
ApplicationUrl: "https://pipecd.dev/app-6",
ApplicationKind: model.ApplicationKind_LAMBDA,
Error: "wrong application configuration",
},
},
},
{
Expand Down Expand Up @@ -203,7 +210,7 @@ changes-1
changes-2
---DETAILS_END---

NOTE: An error occurred while building plan-preview for the following 3 applications:
NOTE: An error occurred while building plan-preview for the following 4 applications:

1. app: app-3, env: env-3, kind: TERRAFORM
reason: wrong application configuration
Expand All @@ -214,6 +221,9 @@ NOTE: An error occurred while building plan-preview for the following 3 applicat
3. app: app-5, kind: ECS
reason: wrong application configuration

4. app: app-6, kind: LAMBDA
reason: wrong application configuration

NOTE: An error occurred while building plan-preview for applications of the following 2 Pipeds:

1. piped: piped-name-1 (piped-1)
Expand Down
3 changes: 2 additions & 1 deletion pkg/app/piped/planpreview/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,9 @@
dr, err = b.cloudrundiff(ctx, app, targetDSP, preCommit, &buf)
case model.ApplicationKind_ECS:
dr, err = b.ecsdiff(ctx, app, targetDSP, preCommit, &buf)
case model.ApplicationKind_LAMBDA:
dr, err = b.lambdadiff(ctx, app, targetDSP, preCommit, &buf)

Check warning on line 255 in pkg/app/piped/planpreview/builder.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/planpreview/builder.go#L254-L255

Added lines #L254 - L255 were not covered by tests
default:
// TODO: Calculating planpreview's diff for other application kinds.
dr = &diffResult{
summary: fmt.Sprintf("%s application is not implemented yet (coming soon)", app.Kind.String()),
}
Expand Down
124 changes: 124 additions & 0 deletions pkg/app/piped/planpreview/lambdadiff.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Copyright 2024 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 planpreview

import (
"bytes"
"context"
"fmt"
"io"

"github.com/pipe-cd/pipecd/pkg/app/piped/deploysource"
provider "github.com/pipe-cd/pipecd/pkg/app/piped/platformprovider/lambda"
"github.com/pipe-cd/pipecd/pkg/diff"
"github.com/pipe-cd/pipecd/pkg/model"
)

func (b *builder) lambdadiff(
ctx context.Context,
app *model.Application,
targetDSP deploysource.Provider,
lastCommit string,
buf *bytes.Buffer,
) (*diffResult, error) {
var (
oldManifest, newManifest provider.FunctionManifest
err error
)

newManifest, err = b.loadFunctionManifest(ctx, *app, targetDSP)
if err != nil {
fmt.Fprintf(buf, "failed to load lambda manifest at the head commit (%v)\n", err)
return nil, err
}

Check warning on line 45 in pkg/app/piped/planpreview/lambdadiff.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/planpreview/lambdadiff.go#L35-L45

Added lines #L35 - L45 were not covered by tests

if lastCommit == "" {
fmt.Fprintf(buf, "failed to find the commit of the last successful deployment")
return nil, fmt.Errorf("cannot get the old manifest without the last successful deployment")
}

Check warning on line 50 in pkg/app/piped/planpreview/lambdadiff.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/planpreview/lambdadiff.go#L47-L50

Added lines #L47 - L50 were not covered by tests

runningDSP := deploysource.NewProvider(
b.workingDir,
deploysource.NewGitSourceCloner(b.gitClient, b.repoCfg, "running", lastCommit),
*app.GitPath,
b.secretDecrypter,
)

oldManifest, err = b.loadFunctionManifest(ctx, *app, runningDSP)
if err != nil {
fmt.Fprintf(buf, "failed to load lambda manifest at the running commit (%v)\n", err)
return nil, err
}

Check warning on line 63 in pkg/app/piped/planpreview/lambdadiff.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/planpreview/lambdadiff.go#L52-L63

Added lines #L52 - L63 were not covered by tests

result, err := provider.Diff(
oldManifest,
newManifest,
diff.WithEquateEmpty(),
diff.WithCompareNumberAndNumericString(),
)
if err != nil {
fmt.Fprintf(buf, "failed to compare manifest (%v)\n", err)
return nil, err
}

Check warning on line 74 in pkg/app/piped/planpreview/lambdadiff.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/planpreview/lambdadiff.go#L65-L74

Added lines #L65 - L74 were not covered by tests

if result.NoChange() {
fmt.Fprintln(buf, "No changes were detected")
return &diffResult{
summary: "No changes were detected",
noChange: true,
}, nil
}

Check warning on line 82 in pkg/app/piped/planpreview/lambdadiff.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/planpreview/lambdadiff.go#L76-L82

Added lines #L76 - L82 were not covered by tests

details := result.Render(provider.DiffRenderOptions{
UseDiffCommand: true,
})
fmt.Fprintf(buf, "--- Last Deploy\n+++ Head Commit\n\n%s\n", details)

return &diffResult{
summary: fmt.Sprintf("%d changes were detected", len(result.Diff.Nodes())),
}, nil

Check warning on line 91 in pkg/app/piped/planpreview/lambdadiff.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/planpreview/lambdadiff.go#L84-L91

Added lines #L84 - L91 were not covered by tests
}

func (b *builder) loadFunctionManifest(ctx context.Context, app model.Application, dsp deploysource.Provider) (provider.FunctionManifest, error) {
commit := dsp.Revision()
cache := provider.FunctionManifestCache{
AppID: app.Id,
Cache: b.appManifestsCache,
Logger: b.logger,
}

manifest, ok := cache.Get(commit)
if ok {
return manifest, nil
}

Check warning on line 105 in pkg/app/piped/planpreview/lambdadiff.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/planpreview/lambdadiff.go#L94-L105

Added lines #L94 - L105 were not covered by tests

ds, err := dsp.Get(ctx, io.Discard)
if err != nil {
return provider.FunctionManifest{}, err
}

Check warning on line 110 in pkg/app/piped/planpreview/lambdadiff.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/planpreview/lambdadiff.go#L107-L110

Added lines #L107 - L110 were not covered by tests

appCfg := ds.ApplicationConfig.LambdaApplicationSpec
if appCfg == nil {
return provider.FunctionManifest{}, fmt.Errorf("malformed application configuration file")
}

Check warning on line 115 in pkg/app/piped/planpreview/lambdadiff.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/planpreview/lambdadiff.go#L112-L115

Added lines #L112 - L115 were not covered by tests

manifest, err = provider.LoadFunctionManifest(ds.AppDir, appCfg.Input.FunctionManifestFile)
if err != nil {
return provider.FunctionManifest{}, err
}

Check warning on line 120 in pkg/app/piped/planpreview/lambdadiff.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/planpreview/lambdadiff.go#L117-L120

Added lines #L117 - L120 were not covered by tests

cache.Put(commit, manifest)
return manifest, nil

Check warning on line 123 in pkg/app/piped/planpreview/lambdadiff.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/planpreview/lambdadiff.go#L122-L123

Added lines #L122 - L123 were not covered by tests
}
68 changes: 68 additions & 0 deletions pkg/app/piped/platformprovider/lambda/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright 2024 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 (
"errors"
"fmt"

"go.uber.org/zap"

"github.com/pipe-cd/pipecd/pkg/cache"
)

type FunctionManifestCache struct {
AppID string
Cache cache.Cache
Logger *zap.Logger
}

func (c FunctionManifestCache) Get(commit string) (FunctionManifest, bool) {
key := manifestCacheKey(c.AppID, commit)
item, err := c.Cache.Get(key)
if err == nil {
return item.(FunctionManifest), true
}

Check warning on line 37 in pkg/app/piped/platformprovider/lambda/cache.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/platformprovider/lambda/cache.go#L32-L37

Added lines #L32 - L37 were not covered by tests

if errors.Is(err, cache.ErrNotFound) {
c.Logger.Info("function manifest wes not found in cache",
zap.String("app-id", c.AppID),
zap.String("commit-hash", commit),
)
return FunctionManifest{}, false
}

Check warning on line 45 in pkg/app/piped/platformprovider/lambda/cache.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/platformprovider/lambda/cache.go#L39-L45

Added lines #L39 - L45 were not covered by tests

c.Logger.Error("failed while retrieving function manifest from cache",
zap.String("app-id", c.AppID),
zap.String("commit-hash", commit),
zap.Error(err),
)
return FunctionManifest{}, false

Check warning on line 52 in pkg/app/piped/platformprovider/lambda/cache.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/platformprovider/lambda/cache.go#L47-L52

Added lines #L47 - L52 were not covered by tests
}

func (c FunctionManifestCache) Put(commit string, sm FunctionManifest) {
key := manifestCacheKey(c.AppID, commit)
if err := c.Cache.Put(key, sm); err != nil {
c.Logger.Error("failed while putting function manifest into cache",
zap.String("app-id", c.AppID),
zap.String("commit-hash", commit),
zap.Error(err),
)
}

Check warning on line 63 in pkg/app/piped/platformprovider/lambda/cache.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/platformprovider/lambda/cache.go#L55-L63

Added lines #L55 - L63 were not covered by tests
}

func manifestCacheKey(appID, commit string) string {
return fmt.Sprintf("%s/%s", appID, commit)

Check warning on line 67 in pkg/app/piped/platformprovider/lambda/cache.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/platformprovider/lambda/cache.go#L66-L67

Added lines #L66 - L67 were not covered by tests
}
80 changes: 80 additions & 0 deletions pkg/app/piped/platformprovider/lambda/diff.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright 2024 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"
"strings"

"github.com/pipe-cd/pipecd/pkg/diff"
)

const (
diffCommand = "diff"
)

type DiffResult struct {
Diff *diff.Result
Old FunctionManifest
New FunctionManifest
}

func (d *DiffResult) NoChange() bool {
return len(d.Diff.Nodes()) == 0
}

func Diff(old, new FunctionManifest, opts ...diff.Option) (*DiffResult, error) {
d, err := diff.DiffStructureds(old, new, opts...)
if err != nil {
return nil, err
}

Check warning on line 42 in pkg/app/piped/platformprovider/lambda/diff.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/platformprovider/lambda/diff.go#L41-L42

Added lines #L41 - L42 were not covered by tests

if !d.HasDiff() {
return &DiffResult{Diff: d}, nil
}

ret := &DiffResult{
Old: old,
New: new,
Diff: d,
}
return ret, nil
}

type DiffRenderOptions struct {
// If true, use "diff" command to render.
UseDiffCommand bool
}

func (d *DiffResult) Render(opt DiffRenderOptions) string {
var b strings.Builder
opts := []diff.RenderOption{
diff.WithLeftPadding(1),
}
renderer := diff.NewRenderer(opts...)
if !opt.UseDiffCommand {
b.WriteString(renderer.Render(d.Diff.Nodes()))
} else {
d, err := diff.RenderByCommand(diffCommand, d.Old, d.New)
if err != nil {
b.WriteString(fmt.Sprintf("An error occurred while rendering diff (%v)", err))

Check warning on line 72 in pkg/app/piped/platformprovider/lambda/diff.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/platformprovider/lambda/diff.go#L72

Added line #L72 was not covered by tests
} else {
b.Write(d)
}
}
b.WriteString("\n")

return b.String()
}
Loading
Loading