Check / check (pull_request) Skipped
A build whose output ends in Docker's error line now fails with that error, instead of going on to inspect a tag that was never created. The build output is written to the deployment log before the failure is recorded, so the log ends in order. Before building, upaas compares the daemon's API version with 1.39 (Docker Engine 18.09), the first that builds with BuildKit without experimental mode, and fails the deploy on an older daemon instead of letting it use the legacy builder. The README's Compose section gives the update command and the Docker Engine versions builds need. Model: opus-5-5
92 lines
2.8 KiB
Go
92 lines
2.8 KiB
Go
package deploy_test
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/fx/fxtest"
|
|
|
|
"sneak.berlin/go/upaas/internal/config"
|
|
"sneak.berlin/go/upaas/internal/database"
|
|
"sneak.berlin/go/upaas/internal/docker"
|
|
"sneak.berlin/go/upaas/internal/logger"
|
|
"sneak.berlin/go/upaas/internal/models"
|
|
"sneak.berlin/go/upaas/internal/service/deploy"
|
|
)
|
|
|
|
// TestBuildImageLogsBuildErrorBeforeDeployError runs a build that fails
|
|
// against a fake Docker API and checks that the deploy fails with the
|
|
// build's own error, which the deployment log shows before the deploy's.
|
|
func TestBuildImageLogsBuildErrorBeforeDeployError(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(
|
|
func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
switch {
|
|
case strings.HasSuffix(r.URL.Path, "/containers/create"):
|
|
_, _ = w.Write([]byte(`{"Id":"gitcontainer"}`))
|
|
case strings.HasSuffix(r.URL.Path, "/version"):
|
|
_, _ = w.Write([]byte(`{"Version":"27.3.1","ApiVersion":"1.47"}`))
|
|
case strings.HasSuffix(r.URL.Path, "/build"):
|
|
_, _ = w.Write([]byte(`{"stream":"Step 1/1 : RUN false\n"}` + "\n" +
|
|
`{"errorDetail":{"message":"exit code: 1"},"error":"exit code: 1"}`))
|
|
default:
|
|
// The steps of the git clone, which succeeds.
|
|
_, _ = w.Write([]byte(`{}`))
|
|
}
|
|
},
|
|
))
|
|
t.Cleanup(srv.Close)
|
|
|
|
log := slog.New(slog.NewTextHandler(os.Stderr, nil))
|
|
lifecycle := fxtest.NewLifecycle(t)
|
|
|
|
dockerClient, err := docker.New(lifecycle, docker.Params{
|
|
Logger: logger.NewForTest(log),
|
|
Config: &config.Config{DockerHost: "tcp://" + srv.Listener.Addr().String()},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
lifecycle.RequireStart()
|
|
t.Cleanup(lifecycle.RequireStop)
|
|
|
|
db := database.NewTestDatabase(t)
|
|
ctx := context.Background()
|
|
|
|
app := models.NewApp(db)
|
|
app.ID = "buildapp-id"
|
|
app.Name = "buildapp"
|
|
app.Branch = "main"
|
|
require.NoError(t, app.Save(ctx))
|
|
|
|
deployment := models.NewDeployment(db)
|
|
deployment.AppID = app.ID
|
|
require.NoError(t, deployment.Save(ctx))
|
|
|
|
dataDir := t.TempDir()
|
|
cfg := &config.Config{DataDir: dataDir, HostDataDir: dataDir}
|
|
|
|
// The service has no notify service: the app has no ntfy topic and no
|
|
// Slack webhook, so the build failure notification sends nothing.
|
|
svc := deploy.NewTestServiceWithConfig(log, cfg, dockerClient)
|
|
|
|
_, err = svc.BuildImage(ctx, app, deployment)
|
|
require.EqualError(t, err, "failed to build image: exit code: 1")
|
|
|
|
logs := deployment.Logs.String
|
|
buildError := strings.Index(logs, "ERROR: exit code: 1")
|
|
deployError := strings.Index(logs, "ERROR: failed to build image: exit code: 1")
|
|
|
|
require.NotEqual(t, -1, buildError, logs)
|
|
assert.Less(t, buildError, deployError, logs)
|
|
}
|