Check / check (pull_request) Skipped
App names may now contain dots, such as sneak.berlin: runs of lowercase letters and numbers joined by single dots or by hyphens, 2 to 63 characters. This is Docker's rule for an image name, and the app's image is upaas-<name>; it also keeps a dot off either end, so the name is safe as a directory and log file name. The new and edit app forms use the same pattern. Their old one was not a valid regular expression under the flag browsers compile it with, so browsers ignored it. A test deploys an app named sneak.berlin against the fake Docker API and checks the image and container names with Docker's own rules. Model: opus-5-5
134 lines
3.3 KiB
Go
134 lines
3.3 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)
|
|
}
|
|
|
|
// DeployContainer exposes deployContainerWithTimeout for testing.
|
|
func (svc *Service) DeployContainer(
|
|
ctx context.Context,
|
|
app *models.App,
|
|
deployment *models.Deployment,
|
|
imageID docker.ImageID,
|
|
) error {
|
|
return svc.deployContainerWithTimeout(ctx, app, deployment, imageID)
|
|
}
|
|
|
|
// 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)
|
|
}
|