All checks were successful
Check / check (pull_request) Successful in 11m24s
Wire the imageID parameter (returned from docker build) through createAndStartContainer and buildContainerOptions instead of reconstructing a mutable tag via fmt.Sprintf. This ensures containers reference the immutable image digest, avoiding tag-reuse races when deploys overlap. Changes: - Rename _ string to imageID string in createAndStartContainer - Change buildContainerOptions to accept imageID string instead of deploymentID int64 - Use imageID directly as the Image field in container options - Update rollback path to pass previousImageID directly - Add test verifying imageID flows through to container options - Add database.NewTestDatabase and logger.NewForTest test helpers
93 lines
2.4 KiB
Go
93 lines
2.4 KiB
Go
package deploy
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"git.eeqj.de/sneak/upaas/internal/config"
|
|
"git.eeqj.de/sneak/upaas/internal/docker"
|
|
"git.eeqj.de/sneak/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 and docker client for testing.
|
|
func NewTestServiceWithConfig(log *slog.Logger, cfg *config.Config, dockerClient *docker.Client) *Service {
|
|
return &Service{
|
|
log: log,
|
|
config: cfg,
|
|
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)
|
|
}
|
|
|
|
// BuildContainerOptionsExported exposes buildContainerOptions for testing.
|
|
func (svc *Service) BuildContainerOptionsExported(
|
|
ctx context.Context,
|
|
app *models.App,
|
|
imageID string,
|
|
) (docker.CreateContainerOptions, error) {
|
|
return svc.buildContainerOptions(ctx, app, imageID)
|
|
}
|