From 7a0e292aa7dddf42b7459fadad41cbee452aa7cf Mon Sep 17 00:00:00 2001 From: jonathan stewart <5750305+jon-stewart@users.noreply.github.com> Date: Mon, 4 Mar 2024 17:16:34 +0000 Subject: [PATCH] feat: CDK v4 feature flag and improvements (#1576) --- cli/cmd/component.go | 13 +- integration/component_test.go | 435 ++++++++++++++-------------------- lwcomponent/catalog.go | 4 +- lwcomponent/catalog_test.go | 274 +++++++-------------- lwcomponent/staging_test.go | 17 ++ 5 files changed, 288 insertions(+), 455 deletions(-) diff --git a/cli/cmd/component.go b/cli/cmd/component.go index c8e41f653..5e3a08b05 100644 --- a/cli/cmd/component.go +++ b/cli/cmd/component.go @@ -444,13 +444,13 @@ func installComponent(args []string) (err error) { stageClose, err := catalog.Stage(component, versionArg, progressClosure) defer stageClose() + downloadComplete <- 0 + if err != nil { cli.StopProgress() return } - downloadComplete <- 0 - params["stage_duration_ms"] = time.Since(start).Milliseconds() cli.Event.FeatureData = params @@ -633,13 +633,12 @@ func updateComponent(args []string) (err error) { stageClose, err := catalog.Stage(component, versionArg, progressClosure) defer stageClose() + downloadComplete <- 0 if err != nil { cli.StopProgress() return } - downloadComplete <- 0 - params["stage_duration_ms"] = time.Since(start).Milliseconds() cli.Event.FeatureData = params @@ -923,14 +922,13 @@ func prototypeRunComponentsInstall(_ *cobra.Command, args []string) (err error) cli.StartProgress(fmt.Sprintf("Installing component %s...", component.Name)) err = cli.LwComponents.Install(component, version, progressClosure) + downloadComplete <- 0 cli.StopProgress() if err != nil { err = errors.Wrap(err, "unable to install component") return } - downloadComplete <- 0 - cli.OutputChecklist(successIcon, "Component %s installed\n", color.HiYellowString(component.Name)) params["install_duration_ms"] = time.Since(start).Milliseconds() @@ -1025,14 +1023,13 @@ func prototypeRunComponentsUpdate(args []string) (err error) { cli.StartProgress(fmt.Sprintf("Updating component %s to version %s...", component.Name, &updateTo)) err = cli.LwComponents.Install(component, updateTo.String(), progressClosure) + downloadComplete <- 0 cli.StopProgress() if err != nil { err = errors.Wrap(err, "unable to update component") return } - downloadComplete <- 0 - cli.OutputChecklist(successIcon, "Component %s updated to %s\n", color.HiYellowString(component.Name), color.HiCyanString(fmt.Sprintf("v%s", updateTo.String()))) diff --git a/integration/component_test.go b/integration/component_test.go index 1c7c1ac3b..ad7a58bf6 100644 --- a/integration/component_test.go +++ b/integration/component_test.go @@ -1,7 +1,7 @@ //go:build component -// Author:: Salim Afiune Maya () -// Copyright:: Copyright 2022, Lacework Inc. +// Author:: Jon Stewart () +// Copyright:: Copyright 2024, Lacework Inc. // License:: Apache License, Version 2.0 // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,287 +20,208 @@ package integration import ( "fmt" "os" - "path/filepath" - "runtime" + "regexp" "testing" "github.com/stretchr/testify/assert" - - "github.com/lacework/go-sdk/internal/file" ) -func TestComponentList(t *testing.T) { - out, err, exitcode := LaceworkCLIWithTOMLConfig("component", "list") - assert.Empty(t, err.String(), "STDERR should be empty") - assert.Equal(t, 0, exitcode, "EXITCODE is not the expected one") - assert.Contains(t, out.String(), - "Loading components state...", - "STDOUT changed, please check") - - assert.Contains(t, out.String(), "STATUS", - "STDOUT table headers changed, please check") - assert.Contains(t, out.String(), "NAME", - "STDOUT table headers changed, please check") - assert.Contains(t, out.String(), "VERSION", - "STDOUT table headers changed, please check") - assert.Contains(t, out.String(), "DESCRIPTION", - "STDOUT table headers changed, please check") - - assert.Contains(t, out.String(), "Not Installed", - "STDOUT our first component is not found, why?") - assert.Contains(t, out.String(), "iac", - "STDOUT our first component is not found, why?") +func TestCDKComponentList(t *testing.T) { + dir := setup() + + out := run(t, dir, "component", "list") + + found, err := regexp.Match("Not Installed\\s+component-example", []byte(out)) + assert.True(t, found) + assert.Nil(t, err) + + cleanup(dir) } -func TestComponentListJSON(t *testing.T) { - out, err, exitcode := LaceworkCLIWithTOMLConfig("component", "list", "--json") - assert.Empty(t, err.String(), "STDERR should be empty") - assert.Equal(t, 0, exitcode, "EXITCODE is not the expected one") +func TestCDKComponentListJSON(t *testing.T) { + dir := setup() - expectedJsonKeys := []string{ - "\"components\"", - "\"artifacts\"", - "\"breadcrumbs\"", - "\"installationMessage\"", - "\"description\"", - "\"name\"", - "\"type\"", - "\"version\"", - "\"arch\"", - "\"os\"", - "\"url\"", - "\"signature\"", + out := run(t, dir, "component", "list", "--json") + + expectedKeys := []string{ + "description", + "latest_version", + "name", + "status", + "type", + "version", } - t.Run("verify json keys", func(t *testing.T) { - for _, header := range expectedJsonKeys { - assert.Contains(t, out.String(), header, - "STDOUT json keys changed, please check") - } - }) - assert.Contains(t, out.String(), "\"name\": \"iac\"", - "missing IaC component in JSON output") - assert.Contains(t, out.String(), "\"type\": \"CLI_COMMAND\"", - "missing IaC component in JSON output") + for _, key := range expectedKeys { + assert.Contains(t, out, fmt.Sprintf("\"%s\"", key), + "STDOUT json keys changed") + } + + cleanup(dir) } -func TestComponentDevModeGolangFromScratch(t *testing.T) { - cName := "go-component" - dir := createTOMLConfigFromCIvars() - defer os.RemoveAll(dir) - - t.Run("component not found", func(t *testing.T) { - out, err, exitcode := LaceworkCLIWithHome(dir, "component", "list") - assert.Empty(t, err.String(), "STDERR should be empty") - assert.Equal(t, 0, exitcode, "EXITCODE is not the expected one") - assert.NotContains(t, out.String(), cName, - "the test component should not be here already, check!") - - out, err, exitcode = LaceworkCLIWithHome(dir, cName) - assert.Empty(t, out.String(), "STDOUT should be empty") - assert.Equal(t, 1, exitcode, "EXITCODE is not the expected one") - assert.NotContains(t, out.String(), cName, - fmt.Sprintf("ERROR unknown command \"%s\"", cName)) - }) +func TestCDKComponentShow(t *testing.T) { + dir := setup() - t.Run("enter dev-mode", func(t *testing.T) { - out, err, exitcode := LaceworkCLIWithHome( - dir, "component", "dev", cName, "--type", "CLI_COMMAND", "--nocolor", - "--description", "A Go component for testing", "--noninteractive", - ) - assert.Empty(t, err.String(), "STDERR should be empty") - assert.Equal(t, 0, exitcode, "EXITCODE is not the expected one") - assert.Contains(t, out.String(), - fmt.Sprintf("Component '%s' in now in development mode.", cName), - "the test component should not be here already, check!") - assert.Contains(t, out.String(), - fmt.Sprintf("lacework/components/%s/.dev", cName), - "the test component should not be here already, check!") - assert.Contains(t, out.String(), - "Deploy your dev component at:", - "the test component should not be here already, check!") - }) + out := run(t, dir, "component", "show", "component-example") - t.Run(fmt.Sprintf("deploy %s component", cName), func(t *testing.T) { - osBin := fmt.Sprintf("%s-%s-%s", cName, runtime.GOOS, runtime.GOARCH) - if runtime.GOOS == "windows" { - osBin += ".exe" - } - fromBin := filepath.Join( - "test_resources", "cdk", cName, "bin", osBin, - ) - toBin := filepath.Join( - dir, ".config", "lacework", "components", cName, cName, - ) - assert.Nil(t, file.Copy(fromBin, toBin)) - }) + found, err := regexp.Match("Not Installed\\s+component-example", []byte(out)) + assert.True(t, found) + assert.Nil(t, err) - t.Run("component should be found and installed", func(t *testing.T) { - out, err, exitcode := LaceworkCLIWithHome(dir, "component", "list") - assert.Empty(t, err.String(), "STDERR should be empty") - assert.Equal(t, 0, exitcode, "EXITCODE is not the expected one") - assert.Contains(t, out.String(), cName, - "the test component SHOULD be here already, check!") - assert.Contains(t, out.String(), "Installed", - "the test component SHOULD be installed, check!") - assert.Contains(t, out.String(), "0.0.0-dev", - "the test component SHOULD be installed, check!") - assert.Contains(t, out.String(), "(dev-mode) A Go component for testing", - "the test component SHOULD match descriptiohn, check!") - }) + assert.Contains(t, out, "The following versions of this component are available to install") - t.Run("execute component", func(t *testing.T) { - out, err, exitcode := LaceworkCLIWithHome(dir, cName) - assert.Empty(t, out.String(), "STDOUT should be empty") - assert.Equal(t, 1, exitcode, "EXITCODE is not the expected one") - assert.Contains(t, err.String(), - fmt.Sprintf("ERROR This is a dummy %s for testing", cName), - "go component changed? Or something is wrong, check!") - - out, err, exitcode = LaceworkCLIWithHome(dir, cName, "run") - assert.Empty(t, err.String(), "STDERR should be empty") - assert.Equal(t, 0, exitcode, "EXITCODE is not the expected one") - assert.Contains(t, out.String(), "Running...", - "go component changed? Or something is wrong, check!") - assert.Contains(t, out.String(), "Highest Severity:", - "go component changed? Or something is wrong, check!") - - out, err, exitcode = LaceworkCLIWithHome(dir, cName, "fail") - assert.Empty(t, out.String(), "STDOUT should be empty") - assert.Equal(t, 1, exitcode, "EXITCODE is not the expected one") - assert.Contains(t, err.String(), "ERROR Purposely failing...", - "go component changed? Or something is wrong, check!") - }) + cleanup(dir) +} - t.Run("global help should show component", func(t *testing.T) { - out, err, exitcode := LaceworkCLIWithHome(dir, "help") - assert.Empty(t, err.String(), "STDERR should be empty") - assert.Equal(t, 0, exitcode, "EXITCODE is not the expected one") - assert.Contains(t, out.String(), cName, - "the test component SHOULD be shown in help message, check!") - assert.Contains(t, out.String(), "(dev-mode) A Go component for testing", - "the test component SHOULD be shown in help message, check!") - }) +func TestCDKComponentShowJSON(t *testing.T) { + dir := setup() - t.Run("component should be displayed in version command", func(t *testing.T) { - out, err, exitcode := LaceworkCLIWithHome(dir, "version") - assert.Empty(t, err.String(), "STDERR should be empty") - assert.Equal(t, 0, exitcode, "EXITCODE is not the expected one") - assert.Contains(t, out.String(), fmt.Sprintf("> %s v0.0.0-dev", cName), - "the test component SHOULD be shown in version command, check!") - }) + out := run(t, dir, "component", "show", "component-example", "--json") - t.Run("execute component to test cache", func(t *testing.T) { - out, err, exitcode := LaceworkCLIWithHome(dir, cName, "writecache") - assert.Equal(t, 0, exitcode, "EXITCODE is not the expected one") - assert.Empty(t, err.String(), "STDERR should be empty") + expectedKeys := []string{ + "description", + "latest_version", + "name", + "status", + "type", + "version", + } - out, err, exitcode = LaceworkCLIWithHome(dir, cName, "readcache") - assert.Equal(t, 0, exitcode, "EXITCODE is not the expected one") - assert.Empty(t, err.String(), "STDERR should be empty") - assert.Contains(t, out.String(), "[\"data\",\"data\",\"data\"]") - }) + for _, key := range expectedKeys { + assert.Contains(t, out, fmt.Sprintf("\"%s\"", key), "STDOUT json keys changed") + } + + cleanup(dir) } -func TestComponentDevModeGolangScaffolding(t *testing.T) { - cName := "go-component" - dir := createTOMLConfigFromCIvars() - defer os.RemoveAll(dir) - - t.Run("component not found", func(t *testing.T) { - out, err, exitcode := LaceworkCLIWithHome(dir, "component", "list") - assert.Empty(t, err.String(), "STDERR should be empty") - assert.Equal(t, 0, exitcode, "EXITCODE is not the expected one") - assert.NotContains(t, out.String(), cName, - "the test component should not be here already, check!") - - out, err, exitcode = LaceworkCLIWithHome(dir, cName) - assert.Empty(t, out.String(), "STDOUT should be empty") - assert.Equal(t, 1, exitcode, "EXITCODE is not the expected one") - assert.NotContains(t, out.String(), cName, - fmt.Sprintf("ERROR unknown command \"%s\"", cName)) - }) +func TestCDKComponentInstall(t *testing.T) { + dir := setup() - t.Run("deploy new component using scaffolding", func(t *testing.T) { - out, err, exitcode := LaceworkCLIWithHome( - dir, "component", "dev", cName, "--type", "CLI_COMMAND", "--nocolor", - "--description", "A Go component for testing", "--noninteractive", - "--scaffolding", "Golang", - ) - assert.Empty(t, err.String(), "STDERR should be empty") - assert.Equal(t, 0, exitcode, "EXITCODE is not the expected one") - assert.Contains(t, out.String(), - fmt.Sprintf("Component '%s' in now in development mode.", cName), - "the test component should not be here already, check!") - assert.Contains(t, out.String(), - fmt.Sprintf("lacework/components/%s/.dev", cName), - "the test component should not be here already, check!") - - checklistOut := []string{ - "Deploying Golang scaffolding", - "cmd/cdk.go deployed", - "cmd/root.go deployed", - "Makefile deployed", - "VERSION deployed", - "cmd/placeholder.go deployed", - ".gitignore deployed", - "main.go deployed", - "LICENSE deployed", - "bin/.gitkeeper deployed", - "go.mod deployed", - "Git repository initialized", - "Deployment completed!", - "Go dependencies downloaded", - "Dev component built", - fmt.Sprintf("Your new component '%s' is ready!", cName), - "Component verified", - } - for _, expectedOutput := range checklistOut { - assert.Contains(t, out.String(), expectedOutput, "missing checklist output") - } - }) + run(t, dir, "component", "install", "component-example") - t.Run("execute component", func(t *testing.T) { - out, err, exitcode := LaceworkCLIWithHome(dir, cName) - assert.Equal(t, 0, exitcode, "EXITCODE is not the expected one") - assert.Empty(t, err.String(), "STDERR should be empty") - assert.Contains(t, out.String(), - "A placeholder command", - "Golang scaffolding changed? Or something is wrong, check!") - assert.Contains(t, out.String(), - fmt.Sprintf("%s is a scaffolding for Lacework CDK components. It helps developers", cName), - "Golang scaffolding changed? Or something is wrong, check!") - - out, err, exitcode = LaceworkCLIWithHome(dir, cName, "placeholder") - assert.Empty(t, err.String(), "STDERR should be empty") - assert.Equal(t, 0, exitcode, "EXITCODE is not the expected one") - assert.Contains(t, out.String(), - fmt.Sprintf("Your new component '%s' is ready!", cName), - "Golang scaffolding changed? Or something is wrong, check!") - - out, err, exitcode = LaceworkCLIWithHome(dir, cName, "should-fail") - assert.Empty(t, out.String(), "STDOUT should be empty") - assert.Equal(t, 1, exitcode, "EXITCODE is not the expected one") - assert.Contains(t, err.String(), - fmt.Sprintf("Error: unknown command \"should-fail\" for \"%s\"", cName), - "Golang scaffolding changed? Or something is wrong, check!") - }) + out := run(t, dir, "component", "list") + + found, err := regexp.Match("Installed\\s+component-example", []byte(out)) + assert.True(t, found) + assert.Nil(t, err) + + out = run(t, dir, "component-example") + assert.Contains(t, out, "component") - t.Run("global help should show component", func(t *testing.T) { - out, err, exitcode := LaceworkCLIWithHome(dir, "help") - assert.Empty(t, err.String(), "STDERR should be empty") - assert.Equal(t, 0, exitcode, "EXITCODE is not the expected one") - assert.Contains(t, out.String(), cName, - "the test component SHOULD be shown in help message, check!") - assert.Contains(t, out.String(), "(dev-mode) A Go component for testing", - "the test component SHOULD be shown in help message, check!") + cleanup(dir) +} + +func TestCDKComponentUpdate(t *testing.T) { + dir := setup() + + run(t, dir, "component", "install", "component-example", "--version", "0.9.0") + + out := run(t, dir, "component-example") + assert.Contains(t, out, "component") + + t.Run("upgrade", func(t *testing.T) { + run(t, dir, "component", "update", "component-example", "--version", "0.9.1") + + out := run(t, dir, "component-example") + assert.Contains(t, out, "component") }) - t.Run("component should be displayed in version command", func(t *testing.T) { - out, err, exitcode := LaceworkCLIWithHome(dir, "version") - assert.Empty(t, err.String(), "STDERR should be empty") - assert.Equal(t, 0, exitcode, "EXITCODE is not the expected one") - assert.Contains(t, out.String(), fmt.Sprintf("> %s v0.0.0-dev", cName), - "the test component SHOULD be shown in version command, check!") + t.Run("downgrade", func(t *testing.T) { + run(t, dir, "component", "update", "component-example", "--version", "0.8.0") + + out := run(t, dir, "component-example") + assert.Contains(t, out, "component") }) + + cleanup(dir) +} + +func TestCDKComponentUninstall(t *testing.T) { + dir := setup() + + run(t, dir, "component", "install", "component-example") + + out := run(t, dir, "component-example") + assert.Contains(t, out, "component") + + run(t, dir, "component", "uninstall", "component-example") + + _, err, _ := LaceworkCLIWithHome(dir, "component-example") + assert.NotNil(t, err) + + out = run(t, dir, "component", "list") + + found, err2 := regexp.Match("Not Installed\\s+component-example", []byte(out)) + assert.True(t, found) + assert.Nil(t, err2) + + cleanup(dir) +} + +func TestCDKComponentDev(t *testing.T) { + dir := setup() + + // GoLang scaffolding is too slow for the test + run(t, dir, "component", "dev", "dev-component", "--description", "dev-component", "--type", "CLI_COMMAND", "--scaffolding", "\"No. Start from scratch\"") + + out := run(t, dir, "component", "list") + + found, err := regexp.Match("Development\\s+dev-component\\s+0.0.0-dev", []byte(out)) + assert.True(t, found) + assert.Nil(t, err) + + run(t, dir, "component", "uninstall", "dev-component") + + out = run(t, dir, "component", "list") + assert.NotContains(t, out, "dev-component") + + cleanup(dir) +} + +func TestCDKComponentDevEnter(t *testing.T) { + dir := setup() + + run(t, dir, "component", "install", "component-example") + + out := run(t, dir, "component", "list") + + found, err := regexp.Match("Installed\\s+component-example", []byte(out)) + assert.True(t, found) + assert.Nil(t, err) + + out = run(t, dir, "component", "dev", "component-example", "--scaffolding", "\"No. Start from scratch\"") + assert.Contains(t, out, "now in development mode") + + out = run(t, dir, "component", "list") + + found, err = regexp.Match("Development\\s+component-example", []byte(out)) + assert.True(t, found) + assert.Nil(t, err) + + run(t, dir, "component", "uninstall", "component-example") + + out = run(t, dir, "component", "list") + + found, err = regexp.Match("Not Installed\\s+component-example", []byte(out)) + assert.True(t, found) + assert.Nil(t, err) + + cleanup(dir) +} + +func setup() string { + return createTOMLConfigFromCIvars() +} + +func run(t *testing.T, dir string, command ...string) string { + out, err, exitcode := LaceworkCLIWithHome(dir, command...) + assert.Empty(t, err.String(), "STDERR should be empty") + assert.Equal(t, 0, exitcode, "EXITCODE is not the expected one") + + return out.String() +} + +func cleanup(dir string) { + os.RemoveAll(dir) } diff --git a/lwcomponent/catalog.go b/lwcomponent/catalog.go index 0e55af29e..c98a6c015 100644 --- a/lwcomponent/catalog.go +++ b/lwcomponent/catalog.go @@ -17,7 +17,7 @@ import ( const ( componentCacheDir string = "components" cdkCacheName string = "cdk_cache" - featureFlag string = "PUBLIC.cdk.v3" + featureFlag string = "PUBLIC.cdk.v4" operatingSystem string = runtime.GOOS architecture string = runtime.GOARCH ) @@ -227,7 +227,7 @@ func (c *Catalog) Install(component *CDKComponent) (err error) { return } - path := filepath.Join(component.stage.Directory(), component.Name) + path := filepath.Join(componentDir, component.Name) if operatingSystem == "windows" { path = fmt.Sprintf("%s.exe", path) diff --git a/lwcomponent/catalog_test.go b/lwcomponent/catalog_test.go index ac68428c3..122013b93 100644 --- a/lwcomponent/catalog_test.go +++ b/lwcomponent/catalog_test.go @@ -19,8 +19,53 @@ import ( "github.com/stretchr/testify/assert" ) +var ( + VERSIONS = [4]string{"1.0.0", "1.1.1", "3.0.1", "5.4.3"} +) + func ProgressClosure(filepath string, size int64) {} +func mockLatestComponentVersions(t *testing.T, fakeServer *lacework.Mock, prefix string, componentCount int) { + fakeServer.MockAPI("Components", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "GET", r.Method, "Components API only accepts HTTP GET") + + fmt.Fprint(w, latestComponentVersionsResponse(prefix, componentCount)) + }) +} + +func mockListComponentVersions(t *testing.T, fakeServer *lacework.Mock, prefix string, componentCount int) { + for i := 0; i < componentCount; i++ { + name := fmt.Sprintf("%s-%d", prefix, i) + path := fmt.Sprintf("Components/%d", i) + versions := []string{"1.0.0", "1.1.1", "3.0.1", "5.4.3"} + + fakeServer.MockAPI(path, func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "GET", r.Method, "Components API only accepts HTTP GET") + fmt.Fprint(w, listComponentVersionsResponse(name, versions)) + }) + } +} + +func mockFetchComponent(t *testing.T, fakeServer *lacework.Mock, id int32, name string, version string, url string) { + fakeServer.MockAPI(fmt.Sprintf("Components/Artifact/%d", id), func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "GET", r.Method, "Components API only accepts HTTP GET") + + if r.URL.Query().Get("version") != version { + http.Error(w, "component version not found", http.StatusNotFound) + } else { + fmt.Fprint(w, fetchComponentResponse(id, name, version, url)) + } + }) +} + +func getClient(fakeServer *lacework.Mock) *api.Client { + client, _ := api.NewClient("catalog_test", + api.WithToken("TOKEN"), + api.WithURL(fakeServer.URL())) + + return client +} + func TestCatalogNewCatalog(t *testing.T) { var ( prefix = "testNewCatalog" @@ -31,26 +76,11 @@ func TestCatalogNewCatalog(t *testing.T) { fakeServer := lacework.MockServer() defer fakeServer.Close() - fakeServer.MockAPI("Components", func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "GET", r.Method, "Components API only accepts HTTP GET") - fmt.Fprint(w, generateComponentsResponse(prefix, apiComponentCount)) - }) + mockLatestComponentVersions(t, fakeServer, prefix, apiComponentCount) - for i := 0; i < apiComponentCount; i++ { - name := fmt.Sprintf("%s-%d", prefix, i) - path := fmt.Sprintf("Components/%d", i) - versions := []string{"1.0.0", "1.1.1", "3.0.1", "5.4.3"} - - fakeServer.MockAPI(path, func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "GET", r.Method, "Components API only accepts HTTP GET") - fmt.Fprint(w, generateComponentVersionsResponse(name, versions)) - }) - } + mockListComponentVersions(t, fakeServer, prefix, apiComponentCount) - client, _ := api.NewClient("catalog_test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL()), - ) + client := getClient(fakeServer) catalog, err := lwcomponent.NewCatalog(client, newTestStage) assert.NotNil(t, catalog) @@ -64,26 +94,11 @@ func TestCatalogNewCatalog(t *testing.T) { _, home := FakeHome() defer ResetHome(home) - fakeServer.MockAPI("Components", func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "GET", r.Method, "Components API only accepts HTTP GET") - fmt.Fprint(w, generateComponentsResponse(prefix, apiComponentCount)) - }) - - for i := 0; i < apiComponentCount; i++ { - name := fmt.Sprintf("%s-%d", prefix, i) - path := fmt.Sprintf("Components/%d", i) - versions := []string{"1.0.0", "1.1.1", "3.0.1", "5.4.3"} + mockLatestComponentVersions(t, fakeServer, prefix, apiComponentCount) - fakeServer.MockAPI(path, func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "GET", r.Method, "Components API only accepts HTTP GET") - fmt.Fprint(w, generateComponentVersionsResponse(name, versions)) - }) - } + mockListComponentVersions(t, fakeServer, prefix, apiComponentCount) - client, _ := api.NewClient("catalog_test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL()), - ) + client := getClient(fakeServer) name := fmt.Sprintf("%s-%d", prefix, 1) version := fmt.Sprintf("%d.0.0", 1) @@ -112,10 +127,7 @@ func TestCatalogNewCatalog(t *testing.T) { fmt.Fprint(w, generateInvalidComponentsResponse()) }) - client, _ := api.NewClient("catalog_test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL()), - ) + client := getClient(fakeServer) catalog, err := lwcomponent.NewCatalog(client, newTestStage) assert.Nil(t, catalog) @@ -129,23 +141,13 @@ func TestCatalogNewCatalog(t *testing.T) { _, home := FakeHome() defer ResetHome(home) - fakeServer.MockAPI("Components", func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "GET", r.Method, "Components API only accepts HTTP GET") - fmt.Fprint(w, generateComponentsResponse(prefix, 1)) - }) + mockLatestComponentVersions(t, fakeServer, prefix, apiComponentCount) - name := fmt.Sprintf("%s-%d", prefix, 1) - versions := []string{"1.0.0", "1.1.1", "3.0.1", "5.4.3"} + mockListComponentVersions(t, fakeServer, prefix, apiComponentCount) - fakeServer.MockAPI("Components/0", func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "GET", r.Method, "Components API only accepts HTTP GET") - fmt.Fprint(w, generateComponentVersionsResponse(name, versions)) - }) + client := getClient(fakeServer) - client, _ := api.NewClient("catalog_test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL()), - ) + name := fmt.Sprintf("%s-%d", prefix, 1) CreateLocalComponent(name, "invalid-version", false, lwcomponent.BinaryType) @@ -158,7 +160,6 @@ func TestCatalogNewCatalog(t *testing.T) { assert.Nil(t, err) assert.Equal(t, lwcomponent.UnknownStatus, component.Status) - }) } @@ -176,10 +177,7 @@ func TestCatalogNewCachedCatalog(t *testing.T) { fakeServer := lacework.MockServer() defer fakeServer.Close() - client, _ := api.NewClient("catalog_test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL()), - ) + client := getClient(fakeServer) allVersions := []*semver.Version{} versionStrings := []string{"1.0.0", "1.1.1", "3.0.1", "5.4.3"} @@ -249,26 +247,11 @@ func TestCatalogComponentCount(t *testing.T) { fakeServer := lacework.MockServer() defer fakeServer.Close() - fakeServer.MockAPI("Components", func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "GET", r.Method, "Components API only accepts HTTP GET") - fmt.Fprint(w, generateComponentsResponse(prefix, apiCount)) - }) + mockLatestComponentVersions(t, fakeServer, prefix, apiCount) - for i := 0; i < apiCount; i++ { - name := fmt.Sprintf("%s-%d", prefix, i) - path := fmt.Sprintf("Components/%d", i) - versions := []string{"1.0.0", "1.1.1", "3.0.1", "5.4.3"} - - fakeServer.MockAPI(path, func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "GET", r.Method, "Components API only accepts HTTP GET") - fmt.Fprint(w, generateComponentVersionsResponse(name, versions)) - }) - } + mockListComponentVersions(t, fakeServer, prefix, apiCount) - client, _ := api.NewClient("catalog_test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL()), - ) + client := getClient(fakeServer) t.Run("no api components installed", func(t *testing.T) { _, home := FakeHome() @@ -350,26 +333,11 @@ func TestCatalogGetComponent(t *testing.T) { defer fakeServer.Close() - fakeServer.MockAPI("Components", func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "GET", r.Method, "Components API only accepts HTTP GET") - fmt.Fprint(w, generateComponentsResponse(prefix, count)) - }) + mockLatestComponentVersions(t, fakeServer, prefix, count) - for i := 0; i < count; i++ { - name := fmt.Sprintf("%s-%d", prefix, i) - path := fmt.Sprintf("Components/%d", i) - versions := []string{"1.0.0", "1.1.1", "3.0.1", "5.4.3"} + mockListComponentVersions(t, fakeServer, prefix, count) - fakeServer.MockAPI(path, func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "GET", r.Method, "Components API only accepts HTTP GET") - fmt.Fprint(w, generateComponentVersionsResponse(name, versions)) - }) - } - - client, _ := api.NewClient("catalog_test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL()), - ) + client := getClient(fakeServer) t.Run("found", func(t *testing.T) { var ( @@ -460,23 +428,13 @@ func TestCatalogListComponentVersions(t *testing.T) { fakeServer := lacework.MockServer() defer fakeServer.Close() - fakeServer.MockAPI("Components", func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "GET", r.Method, "Components API only accepts HTTP GET") - fmt.Fprint(w, generateComponentsResponse(prefix, 1)) - }) + mockLatestComponentVersions(t, fakeServer, prefix, 1) + + mockListComponentVersions(t, fakeServer, prefix, 1) name := fmt.Sprintf("%s-%d", prefix, 0) - versions := []string{"0.1.0", "1.1.1", "3.0.1", "5.4.3"} - fakeServer.MockAPI("Components/0", func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "GET", r.Method, "Components API only accepts HTTP GET") - fmt.Fprint(w, generateComponentVersionsResponse(name, versions)) - }) - - client, _ := api.NewClient("catalog_test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL()), - ) + client := getClient(fakeServer) catalog, err := lwcomponent.NewCatalog(client, newTestStage) assert.NotNil(t, catalog) @@ -489,7 +447,7 @@ func TestCatalogListComponentVersions(t *testing.T) { vers, err := catalog.ListComponentVersions(component) assert.Nil(t, err) - for idx, v := range versions { + for idx, v := range VERSIONS { assert.Equal(t, v, vers[idx].String()) } }) @@ -498,23 +456,17 @@ func TestCatalogListComponentVersions(t *testing.T) { fakeServer := lacework.MockServer() defer fakeServer.Close() - fakeServer.MockAPI("Components", func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "GET", r.Method, "Components API only accepts HTTP GET") - fmt.Fprint(w, generateComponentsResponse(prefix, 1)) - }) + mockLatestComponentVersions(t, fakeServer, prefix, 1) name := fmt.Sprintf("%s-%d", prefix, 0) versions := []string{"0.1.0", "1.invalid.1", "3.0.1", "5.4.3"} fakeServer.MockAPI("Components/0", func(w http.ResponseWriter, r *http.Request) { assert.Equal(t, "GET", r.Method, "Components API only accepts HTTP GET") - fmt.Fprint(w, generateComponentVersionsResponse(name, versions)) + fmt.Fprint(w, listComponentVersionsResponse(name, versions)) }) - client, _ := api.NewClient("catalog_test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL()), - ) + client := getClient(fakeServer) catalog, err := lwcomponent.NewCatalog(client, newTestStage) assert.NotNil(t, catalog) @@ -544,10 +496,7 @@ func TestCatalogStage(t *testing.T) { _, home := FakeHome() defer ResetHome(home) - fakeServer.MockAPI("Components", func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "GET", r.Method, "Components API only accepts HTTP GET") - fmt.Fprint(w, generateComponentsResponse(prefix, apiComponentCount)) - }) + mockLatestComponentVersions(t, fakeServer, prefix, apiComponentCount) url := "s3-download" @@ -590,34 +539,20 @@ func TestCatalogStage(t *testing.T) { switch l { case "1.0.0": { - fmt.Fprint(w, generateFetchResponse(1, name, version, fmt.Sprintf("%s/api/v2/%s", fakeServer.URL(), url))) + fmt.Fprint(w, fetchComponentResponse(1, name, version, fmt.Sprintf("%s/api/v2/%s", fakeServer.URL(), url))) } case "3.0.1": { - fmt.Fprint(w, generateFetchResponse(1, name, version, fmt.Sprintf("%s/api/v2/%s", fakeServer.URL(), EOFUrl))) + fmt.Fprint(w, fetchComponentResponse(1, name, version, fmt.Sprintf("%s/api/v2/%s", fakeServer.URL(), EOFUrl))) } case "5.4.3": { - fmt.Fprint(w, generateFetchResponse(1, name, version, fmt.Sprintf("%s/api/v2/%s", fakeServer.URL(), XMLUrl))) + fmt.Fprint(w, fetchComponentResponse(1, name, version, fmt.Sprintf("%s/api/v2/%s", fakeServer.URL(), XMLUrl))) } } }) - for i := 0; i < apiComponentCount; i++ { - name := fmt.Sprintf("%s-%d", prefix, i) - path := fmt.Sprintf("Components/%d", i) - versions := []string{"1.0.0", "1.1.1", "3.0.1", "5.4.3"} - - fakeServer.MockAPI(path, func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "GET", r.Method, "Components API only accepts HTTP GET") - fmt.Fprint(w, generateComponentVersionsResponse(name, versions)) - }) - } - - client, _ := api.NewClient("catalog_test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL()), - ) + client := getClient(fakeServer) catalog, err := lwcomponent.NewCatalog(client, lwcomponent.NewStageTarGz) assert.NotNil(t, catalog) @@ -691,7 +626,6 @@ func TestCatalogStage(t *testing.T) { assert.NotNil(t, err) defer stageClose() }) - } type testStage struct { @@ -767,12 +701,6 @@ func newTestStage(name, artifactUrl string, size int64) (stage lwcomponent.Stage } func TestCatalogVerify(t *testing.T) { - - t.Run("", func(t *testing.T) {}) - t.Run("", func(t *testing.T) {}) - t.Run("", func(t *testing.T) {}) - t.Run("", func(t *testing.T) {}) - t.Run("", func(t *testing.T) {}) } func TestCatalogInstall(t *testing.T) { @@ -788,33 +716,17 @@ func TestCatalogInstall(t *testing.T) { _, home := FakeHome() defer ResetHome(home) - for i := 0; i < apiComponentCount; i++ { - name := fmt.Sprintf("%s-%d", prefix, i) - - fakeServer.MockAPI(fmt.Sprintf("Components/Artifact/%d", i), func(w http.ResponseWriter, r *http.Request) { - fmt.Fprint(w, generateFetchResponse(int32(i), name, version, "")) - }) - } + mockLatestComponentVersions(t, fakeServer, prefix, apiComponentCount) - fakeServer.MockAPI("Components", func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "GET", r.Method, "Components API only accepts HTTP GET") - fmt.Fprint(w, generateComponentsResponse(prefix, apiComponentCount)) - }) + mockListComponentVersions(t, fakeServer, prefix, apiComponentCount) for i := 0; i < apiComponentCount; i++ { name := fmt.Sprintf("%s-%d", prefix, i) - path := fmt.Sprintf("Components/%d", i) - versions := []string{"1.0.0", "1.1.1", "3.0.1", "5.4.3"} - fakeServer.MockAPI(path, func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "GET", r.Method, "Components API only accepts HTTP GET") - fmt.Fprint(w, generateComponentVersionsResponse(name, versions)) - }) + mockFetchComponent(t, fakeServer, int32(i), name, version, "") } - client, _ := api.NewClient("catalog_test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL())) + client := getClient(fakeServer) catalog, err := lwcomponent.NewCatalog(client, newTestStage) assert.NotNil(t, catalog) @@ -866,25 +778,11 @@ func TestCatalogDelete(t *testing.T) { _, home := FakeHome() defer ResetHome(home) - fakeServer.MockAPI("Components", func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "GET", r.Method, "Components API only accepts HTTP GET") - fmt.Fprint(w, generateComponentsResponse(prefix, apiComponentCount)) - }) - - for i := 0; i < apiComponentCount; i++ { - name := fmt.Sprintf("%s-%d", prefix, i) - path := fmt.Sprintf("Components/%d", i) - versions := []string{"1.0.0", "1.1.1", "3.0.1", "5.4.3"} + mockLatestComponentVersions(t, fakeServer, prefix, apiComponentCount) - fakeServer.MockAPI(path, func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "GET", r.Method, "Components API only accepts HTTP GET") - fmt.Fprint(w, generateComponentVersionsResponse(name, versions)) - }) - } + mockListComponentVersions(t, fakeServer, prefix, apiComponentCount) - client, _ := api.NewClient("catalog_test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL())) + client := getClient(fakeServer) t.Run("delete-installed", func(t *testing.T) { name := fmt.Sprintf("%s-1", prefix) @@ -971,7 +869,7 @@ func TestCatalogDelete(t *testing.T) { } -func generateComponentsResponse(prefix string, count int) string { +func latestComponentVersionsResponse(prefix string, count int) string { var ( components = []api.LatestComponentVersion{} idx int32 @@ -1014,7 +912,7 @@ func generateInvalidComponentsResponse() string { return string(result) } -func generateComponentVersionsResponse(name string, versions []string) string { +func listComponentVersionsResponse(name string, versions []string) string { response := api.ListComponentVersionsResponse{ Data: []api.ComponentVersions{{ Id: 1, @@ -1033,7 +931,7 @@ func generateComponentVersionsResponse(name string, versions []string) string { } -func generateFetchResponse(id int32, name string, version string, url string) string { +func fetchComponentResponse(id int32, name string, version string, url string) string { response := api.FetchComponentResponse{ Data: []api.Artifact{{ Id: id, diff --git a/lwcomponent/staging_test.go b/lwcomponent/staging_test.go index d31cddc63..df5c1365c 100644 --- a/lwcomponent/staging_test.go +++ b/lwcomponent/staging_test.go @@ -3,6 +3,7 @@ package lwcomponent_test import ( "archive/tar" "compress/gzip" + "encoding/base64" "fmt" "io" "os" @@ -81,6 +82,22 @@ func TestStagingTarGzSignature(t *testing.T) { assert.Equal(t, expectedSig, string(result)) }) + t.Run("ok b64", func(t *testing.T) { + stage, err := lwcomponent.NewStageTarGz(name, "", 0) + assert.Nil(t, err) + defer stage.Close() + + fileName := filepath.Join(stage.Directory(), lwcomponent.SignatureFile) + + data := base64.StdEncoding.EncodeToString([]byte(expectedSig)) + + os.WriteFile(fileName, []byte(data), os.ModePerm) + + result, err := stage.Signature() + assert.Nil(t, err) + assert.Equal(t, expectedSig, string(result)) + }) + t.Run("no signature file", func(t *testing.T) { stage, err := lwcomponent.NewStageTarGz(name, "", 0) assert.Nil(t, err)