From bec248c53fd4d6bf06c0cba33c9781c8b95ba36f Mon Sep 17 00:00:00 2001 From: Mike Sul <mike.sul@foundries.io> Date: Tue, 17 Dec 2024 11:24:34 +0100 Subject: [PATCH 1/3] root: Add function to expose composectl config Signed-off-by: Mike Sul <mike.sul@foundries.io> --- cmd/composectl/cmd/root.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/composectl/cmd/root.go b/cmd/composectl/cmd/root.go index 651520a..f0ba226 100644 --- a/cmd/composectl/cmd/root.go +++ b/cmd/composectl/cmd/root.go @@ -48,6 +48,10 @@ var ( } ) +func GetConfig() Config { + return config +} + func Execute() { if err := rootCmd.Execute(); err != nil { fmt.Println(err) From 7a0d38696052b43db28812a187ad3730d25ba929 Mon Sep 17 00:00:00 2001 From: Mike Sul <mike.sul@foundries.io> Date: Tue, 17 Dec 2024 14:43:31 +0100 Subject: [PATCH 2/3] check: Make app install status public Signed-off-by: Mike Sul <mike.sul@foundries.io> --- cmd/composectl/cmd/check.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/composectl/cmd/check.go b/cmd/composectl/cmd/check.go index b63eefa..de2f602 100644 --- a/cmd/composectl/cmd/check.go +++ b/cmd/composectl/cmd/check.go @@ -48,13 +48,13 @@ type ( InstallCheck *InstallCheckResult `json:"install_check"` } - appInstallCheckResult struct { + AppInstallCheckResult struct { AppName string `json:"app_name"` MissingImages []string `json:"missing_images"` BundleErrors compose.AppBundleErrs `json:"bundle_errors"` } - InstallCheckResult map[string]*appInstallCheckResult + InstallCheckResult map[string]*AppInstallCheckResult ) const ( @@ -305,7 +305,7 @@ func checkIfInstalled(ctx context.Context, appRefs []string, srcStorePath string if err != nil { return nil, err } - checkResult[appRef] = &appInstallCheckResult{ + checkResult[appRef] = &AppInstallCheckResult{ AppName: app.Name(), MissingImages: missingImages, BundleErrors: errMap, From 294d4215d3e1f33f5e8ba623832067b89f16a54f Mon Sep 17 00:00:00 2001 From: Mike Sul <mike.sul@foundries.io> Date: Tue, 17 Dec 2024 14:45:42 +0100 Subject: [PATCH 3/3] test: Add test for app bundle integrity check Signed-off-by: Mike Sul <mike.sul@foundries.io> --- test/fixtures/composectl_cmds.go | 37 ++++++++++++++++++++ test/integration/edge_cases_test.go | 52 +++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/test/fixtures/composectl_cmds.go b/test/fixtures/composectl_cmds.go index 278a0b5..9d7bfea 100644 --- a/test/fixtures/composectl_cmds.go +++ b/test/fixtures/composectl_cmds.go @@ -223,6 +223,26 @@ func (a *App) CheckInstalled(t *testing.T) { }) } +func (a *App) GetInstallCheckRes(t *testing.T) (checkRes *composectl.AppInstallCheckResult) { + t.Run("check if installed", func(t *testing.T) { + output := runCmd(t, a.Dir, "check", "--local", "--install", a.PublishedUri, "--format", "json") + checkResult := composectl.CheckAndInstallResult{} + check(t, json.Unmarshal(output, &checkResult)) + if len(checkResult.FetchCheck.MissingBlobs) > 0 { + t.Errorf("there are missing app blobs: %+v\n", checkResult.FetchCheck.MissingBlobs) + } + if checkResult.InstallCheck == nil || len(*checkResult.InstallCheck) != 1 { + t.Errorf("invalid install check result: %+v\n", checkResult.InstallCheck) + } + var ok bool + checkRes, ok = (*checkResult.InstallCheck)[a.PublishedUri] + if !ok { + t.Errorf("no app in the install check result: %+v\n", *checkResult.InstallCheck) + } + }) + return +} + func (a *App) CheckRunning(t *testing.T) { t.Run("check if running", func(t *testing.T) { output := runCmd(t, "", "ps", a.PublishedUri, "--format", "json") @@ -241,6 +261,23 @@ func (a *App) CheckRunning(t *testing.T) { }) } +func (a *App) GetRunningStatus(t *testing.T) (appStatus *composectl.App) { + t.Run("check if running", func(t *testing.T) { + output := runCmd(t, "", "ps", a.PublishedUri, "--format", "json") + var psOutput map[string]composectl.App + check(t, json.Unmarshal(output, &psOutput)) + if len(psOutput) != 1 { + t.Errorf("expected one element in ps output, got: %d\n", len(psOutput)) + } + if appStatusRes, ok := psOutput[a.PublishedUri]; ok { + appStatus = &appStatusRes + } else { + t.Errorf("no app URI in the ps output: %+v\n", psOutput) + } + }) + return +} + func (a *App) runCmd(t *testing.T, desc string, args ...string) { t.Run(desc, func(t *testing.T) { runCmd(t, a.Dir, args...) diff --git a/test/integration/edge_cases_test.go b/test/integration/edge_cases_test.go index f1fc355..0bd9c46 100644 --- a/test/integration/edge_cases_test.go +++ b/test/integration/edge_cases_test.go @@ -1,7 +1,10 @@ package e2e_tests import ( + composectl "github.com/foundriesio/composeapp/cmd/composectl/cmd" f "github.com/foundriesio/composeapp/test/fixtures" + "os" + "path" "testing" ) @@ -67,3 +70,52 @@ services: defer app.Stop(t) app.CheckRunning(t) } + +func TestAppBundleBroken(t *testing.T) { + appComposeDef := ` +services: + srvs-01: + image: registry:5000/factory/runner-image:v0.1 + command: sh -c "while true; do sleep 60; done" + ports: + - 8080:80 + srvs-02: + image: registry:5000/factory/runner-image:v0.1 + command: sh -c "while true; do sleep 60; done" +` + app := f.NewApp(t, appComposeDef) + app.Publish(t) + + app.Pull(t) + defer app.Remove(t) + + app.Install(t) + defer app.Uninstall(t) + app.CheckInstalled(t) + + composeFilePath := path.Join(composectl.GetConfig().ComposeRoot, app.Name, "docker-compose.yml") + if err := os.WriteFile(composeFilePath, []byte("foo bar"), 0x644); err != nil { + t.Fatal(err) + } + checkRes := app.GetInstallCheckRes(t) + if len(checkRes.BundleErrors) != 1 { + t.Fatalf("expected 1 app bundle integrity error, got: %d", len(checkRes.BundleErrors)) + } + if _, ok := checkRes.BundleErrors["docker-compose.yml"]; !ok { + t.Fatalf("expected error for: %s, got: %+v", "docker-compose.yml", checkRes.BundleErrors) + } + + app.Run(t) + defer app.Stop(t) + app.CheckRunning(t) + + if err := os.WriteFile(composeFilePath, []byte("foo bar"), 0x644); err != nil { + t.Fatal(err) + } + appStatus := app.GetRunningStatus(t) + if appStatus.State != "running with an invalid app bundle" { + t.Fatalf("expected `running with an invalid app bundle`, got: %s", appStatus.State) + } + // Install app again, so it can be stopped without any error + app.Install(t) +}