Check / check (pull_request) Skipped
Builds are tagged upaas-<app>:<short hash>, git's own short form of the commit checked out, instead of the deployment number. A redeploy of a commit gives the tag to the new image. The cleanup after a deploy now also finds the app's untagged images among those its deployments recorded, and removes them by ID once the app neither runs them nor would roll back to them. It keeps every image any app uses, since apps that build the same commit can share one. The clone reads the commits from git's output after removing Docker's log headers, which had hidden the COMMIT: line; commit_sha is now saved on update so manual deploys keep the commit they recorded. Model: opus-5-5
124 lines
3.0 KiB
Go
124 lines
3.0 KiB
Go
package deploy
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"sneak.berlin/go/upaas/internal/config"
|
|
"sneak.berlin/go/upaas/internal/database"
|
|
"sneak.berlin/go/upaas/internal/docker"
|
|
"sneak.berlin/go/upaas/internal/models"
|
|
)
|
|
|
|
// NewTestService creates a Service with minimal dependencies for testing.
|
|
func NewTestService(log *slog.Logger) *Service {
|
|
return &Service{
|
|
log: log,
|
|
}
|
|
}
|
|
|
|
// CancelActiveDeploy exposes cancelActiveDeploy for testing.
|
|
func (svc *Service) CancelActiveDeploy(appID string) {
|
|
svc.cancelActiveDeploy(appID)
|
|
}
|
|
|
|
// RegisterActiveDeploy registers an active deploy for testing.
|
|
func (svc *Service) RegisterActiveDeploy(
|
|
appID string,
|
|
cancel context.CancelFunc,
|
|
done chan struct{},
|
|
) {
|
|
svc.activeDeploys.Store(appID, &activeDeploy{cancel: cancel, done: done})
|
|
}
|
|
|
|
// TryLockApp exposes tryLockApp for testing.
|
|
func (svc *Service) TryLockApp(appID string) bool {
|
|
return svc.tryLockApp(appID)
|
|
}
|
|
|
|
// UnlockApp exposes unlockApp for testing.
|
|
func (svc *Service) UnlockApp(appID string) {
|
|
svc.unlockApp(appID)
|
|
}
|
|
|
|
// NewTestServiceWithConfig creates a Service with config, database and
|
|
// docker client for testing.
|
|
func NewTestServiceWithConfig(
|
|
log *slog.Logger,
|
|
cfg *config.Config,
|
|
db *database.Database,
|
|
dockerClient *docker.Client,
|
|
) *Service {
|
|
return &Service{
|
|
log: log,
|
|
config: cfg,
|
|
db: db,
|
|
docker: dockerClient,
|
|
}
|
|
}
|
|
|
|
// CleanupCancelledDeploy exposes the build directory cleanup portion of
|
|
// cleanupCancelledDeploy for testing. It removes build directories matching
|
|
// the deployment ID prefix.
|
|
func (svc *Service) CleanupCancelledDeploy(
|
|
_ context.Context,
|
|
appName string,
|
|
deploymentID int64,
|
|
_ string,
|
|
) {
|
|
// We can't create real models.App/Deployment in tests easily,
|
|
// so we test the build dir cleanup portion directly.
|
|
buildDir := svc.GetBuildDir(appName)
|
|
|
|
entries, err := os.ReadDir(buildDir)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
prefix := fmt.Sprintf("%d-", deploymentID)
|
|
|
|
for _, entry := range entries {
|
|
if entry.IsDir() && strings.HasPrefix(entry.Name(), prefix) {
|
|
dirPath := filepath.Join(buildDir, entry.Name())
|
|
_ = os.RemoveAll(dirPath)
|
|
}
|
|
}
|
|
}
|
|
|
|
// GetBuildDirExported exposes GetBuildDir for testing.
|
|
func (svc *Service) GetBuildDirExported(appName string) string {
|
|
return svc.GetBuildDir(appName)
|
|
}
|
|
|
|
// RecordDeployedImage exposes recordDeployedImage for testing.
|
|
func (svc *Service) RecordDeployedImage(
|
|
ctx context.Context,
|
|
app *models.App,
|
|
deployment *models.Deployment,
|
|
imageID docker.ImageID,
|
|
) error {
|
|
return svc.recordDeployedImage(ctx, app, deployment, imageID)
|
|
}
|
|
|
|
// BuildImage exposes buildImage for testing.
|
|
func (svc *Service) BuildImage(
|
|
ctx context.Context,
|
|
app *models.App,
|
|
deployment *models.Deployment,
|
|
) (docker.ImageID, error) {
|
|
return svc.buildImage(ctx, app, deployment)
|
|
}
|
|
|
|
// BuildContainerOptionsExported exposes buildContainerOptions for testing.
|
|
func (svc *Service) BuildContainerOptionsExported(
|
|
ctx context.Context,
|
|
app *models.App,
|
|
imageID docker.ImageID,
|
|
) (docker.CreateContainerOptions, error) {
|
|
return svc.buildContainerOptions(ctx, app, imageID)
|
|
}
|