feat(preview): add deployer, route manager, volume cloner deps to service
- commit
- ae320663f1337bac5f91900e5341cb06a05cd2df
- author
- brice · · 2026-03-18 17:07 UTC
- committer
- brice · · 2026-03-18 20:39 UTC
- parent
- fe012a8
- signature
- unsigned
2 files · +58 -10
M internal/usecase/auto/preview/service.go +43 -6
| @@ -17,14 +17,31 @@ type nameLockEntry struct { | |||
| 17 | 17 | refs int | |
| 18 | 18 | } | |
| 19 | 19 | ||
| 20 | + | // Deployer abstracts the container service deploy method. | |
| 21 | + | type Deployer interface { | |
| 22 | + | Deploy(ctx context.Context, route domain.Route) (*domain.Container, error) | |
| 23 | + | } | |
| 24 | + | ||
| 25 | + | // RouteManager abstracts config service methods needed for preview route lifecycle. | |
| 26 | + | type RouteManager interface { | |
| 27 | + | AddRoute(ctx context.Context, route domain.Route) error | |
| 28 | + | RemoveRoute(ctx context.Context, domain string) error | |
| 29 | + | GetRoute(ctx context.Context, domain string) (*domain.Route, error) | |
| 30 | + | GetVolumeConfig() (autoCreate bool, prefix string, preserve bool) | |
| 31 | + | } | |
| 32 | + | ||
| 20 | 33 | // Service manages preview lifecycle: CRUD, TTL, persistence. | |
| 21 | 34 | type Service struct { | |
| 22 | - | store out.PreviewStore | |
| 23 | - | defaultTTL time.Duration | |
| 24 | - | previews []domain.PreviewRoute | |
| 25 | - | mu sync.RWMutex | |
| 26 | - | nameLocks map[string]*nameLockEntry | |
| 27 | - | nameLockMu sync.Mutex | |
| 35 | + | store out.PreviewStore | |
| 36 | + | defaultTTL time.Duration | |
| 37 | + | deployer Deployer | |
| 38 | + | routeManager RouteManager | |
| 39 | + | volumeCloner VolumeCloner | |
| 40 | + | registryDomain string | |
| 41 | + | previews []domain.PreviewRoute | |
| 42 | + | mu sync.RWMutex | |
| 43 | + | nameLocks map[string]*nameLockEntry | |
| 44 | + | nameLockMu sync.Mutex | |
| 28 | 45 | } | |
| 29 | 46 | ||
| 30 | 47 | func NewService(store out.PreviewStore, defaultTTL time.Duration) *Service { | |
| @@ -35,6 +52,26 @@ func NewService(store out.PreviewStore, defaultTTL time.Duration) *Service { | |||
| 35 | 52 | } | |
| 36 | 53 | } | |
| 37 | 54 | ||
| 55 | + | func (s *Service) WithDeployer(d Deployer) *Service { | |
| 56 | + | s.deployer = d | |
| 57 | + | return s | |
| 58 | + | } | |
| 59 | + | ||
| 60 | + | func (s *Service) WithRouteManager(rm RouteManager) *Service { | |
| 61 | + | s.routeManager = rm | |
| 62 | + | return s | |
| 63 | + | } | |
| 64 | + | ||
| 65 | + | func (s *Service) WithVolumeCloner(vc VolumeCloner) *Service { | |
| 66 | + | s.volumeCloner = vc | |
| 67 | + | return s | |
| 68 | + | } | |
| 69 | + | ||
| 70 | + | func (s *Service) WithRegistryDomain(d string) *Service { | |
| 71 | + | s.registryDomain = d | |
| 72 | + | return s | |
| 73 | + | } | |
| 74 | + | ||
| 38 | 75 | func (s *Service) Load(ctx context.Context) error { | |
| 39 | 76 | previews, err := s.store.Load(ctx) | |
| 40 | 77 | if err != nil { | |
| @@ -17,14 +17,31 @@ type nameLockEntry struct { | |||||
| 17 | refs int | 17 | refs int | ||
| 18 | } | 18 | } | ||
| 19 | 19 | ||||
| 20 | + | // Deployer abstracts the container service deploy method. | |||
| 21 | + | type Deployer interface { | |||
| 22 | + | Deploy(ctx context.Context, route domain.Route) (*domain.Container, error) | |||
| 23 | + | } | |||
| 24 | + | ||||
| 25 | + | // RouteManager abstracts config service methods needed for preview route lifecycle. | |||
| 26 | + | type RouteManager interface { | |||
| 27 | + | AddRoute(ctx context.Context, route domain.Route) error | |||
| 28 | + | RemoveRoute(ctx context.Context, domain string) error | |||
| 29 | + | GetRoute(ctx context.Context, domain string) (*domain.Route, error) | |||
| 30 | + | GetVolumeConfig() (autoCreate bool, prefix string, preserve bool) | |||
| 31 | + | } | |||
| 32 | + | ||||
| 20 | // Service manages preview lifecycle: CRUD, TTL, persistence. | 33 | // Service manages preview lifecycle: CRUD, TTL, persistence. | ||
| 21 | type Service struct { | 34 | type Service struct { | ||
| 22 | - | store out.PreviewStore | 35 | + | store out.PreviewStore |
| 23 | - | defaultTTL time.Duration | 36 | + | defaultTTL time.Duration |
| 24 | - | previews []domain.PreviewRoute | 37 | + | deployer Deployer |
| 25 | - | mu sync.RWMutex | 38 | + | routeManager RouteManager |
| 26 | - | nameLocks map[string]*nameLockEntry | 39 | + | volumeCloner VolumeCloner |
| 27 | - | nameLockMu sync.Mutex | 40 | + | registryDomain string |
| 41 | + | previews []domain.PreviewRoute | |||
| 42 | + | mu sync.RWMutex | |||
| 43 | + | nameLocks map[string]*nameLockEntry | |||
| 44 | + | nameLockMu sync.Mutex | |||
| 28 | } | 45 | } | ||
| 29 | 46 | ||||
| 30 | func NewService(store out.PreviewStore, defaultTTL time.Duration) *Service { | 47 | func NewService(store out.PreviewStore, defaultTTL time.Duration) *Service { | ||
| @@ -35,6 +52,26 @@ func NewService(store out.PreviewStore, defaultTTL time.Duration) *Service { | |||||
| 35 | } | 52 | } | ||
| 36 | } | 53 | } | ||
| 37 | 54 | ||||
| 55 | + | func (s *Service) WithDeployer(d Deployer) *Service { | |||
| 56 | + | s.deployer = d | |||
| 57 | + | return s | |||
| 58 | + | } | |||
| 59 | + | ||||
| 60 | + | func (s *Service) WithRouteManager(rm RouteManager) *Service { | |||
| 61 | + | s.routeManager = rm | |||
| 62 | + | return s | |||
| 63 | + | } | |||
| 64 | + | ||||
| 65 | + | func (s *Service) WithVolumeCloner(vc VolumeCloner) *Service { | |||
| 66 | + | s.volumeCloner = vc | |||
| 67 | + | return s | |||
| 68 | + | } | |||
| 69 | + | ||||
| 70 | + | func (s *Service) WithRegistryDomain(d string) *Service { | |||
| 71 | + | s.registryDomain = d | |||
| 72 | + | return s | |||
| 73 | + | } | |||
| 74 | + | ||||
| 38 | func (s *Service) Load(ctx context.Context) error { | 75 | func (s *Service) Load(ctx context.Context) error { | ||
| 39 | previews, err := s.store.Load(ctx) | 76 | previews, err := s.store.Load(ctx) | ||
| 40 | if err != nil { | 77 | if err != nil { | ||
M internal/usecase/auto/preview/volumes.go +15 -4
| @@ -16,6 +16,17 @@ type VolumeCloner interface { | |||
| 16 | 16 | StartContainer(ctx context.Context, containerID string) error | |
| 17 | 17 | WaitForContainer(ctx context.Context, containerID string) error | |
| 18 | 18 | RemoveContainer(ctx context.Context, containerID string, force bool) error | |
| 19 | + | ListVolumes(ctx context.Context) ([]*domain.VolumeInfo, error) | |
| 20 | + | } | |
| 21 | + | ||
| 22 | + | // VolumeNamer is a function that maps a source volume name to a destination volume name. | |
| 23 | + | type VolumeNamer func(sourceVolName string) string | |
| 24 | + | ||
| 25 | + | // DefaultNamer returns a VolumeNamer that uses BuildCloneVolumeName with the given preview name. | |
| 26 | + | func DefaultNamer(previewName string) VolumeNamer { | |
| 27 | + | return func(sourceVolName string) string { | |
| 28 | + | return BuildCloneVolumeName(previewName, sourceVolName) | |
| 29 | + | } | |
| 19 | 30 | } | |
| 20 | 31 | ||
| 21 | 32 | // BuildCloneVolumeName generates a preview volume name. | |
| @@ -36,11 +47,11 @@ func BuildCloneContainerName(previewName, image string) string { | |||
| 36 | 47 | } | |
| 37 | 48 | ||
| 38 | 49 | // CloneVolumes copies volumes from source using a read-only helper container. | |
| 39 | - | // sourceVolumes maps volume display names to actual Docker volume names. | |
| 40 | - | func CloneVolumes(ctx context.Context, cloner VolumeCloner, previewName string, sourceVolumes map[string]string) ([]string, error) { | |
| 50 | + | // sourceVolumes is a list of actual Docker volume names to clone. | |
| 51 | + | func CloneVolumes(ctx context.Context, cloner VolumeCloner, namer VolumeNamer, sourceVolumes []string) ([]string, error) { | |
| 41 | 52 | var clonedNames []string | |
| 42 | - | for volName, sourceVolName := range sourceVolumes { | |
| 43 | - | destName := BuildCloneVolumeName(previewName, volName) | |
| 53 | + | for _, sourceVolName := range sourceVolumes { | |
| 54 | + | destName := namer(sourceVolName) | |
| 44 | 55 | ||
| 45 | 56 | if err := cloner.CreateVolume(ctx, destName); err != nil { | |
| 46 | 57 | cleanupVolumes(ctx, cloner, clonedNames) | |
| @@ -16,6 +16,17 @@ type VolumeCloner interface { | |||||
| 16 | StartContainer(ctx context.Context, containerID string) error | 16 | StartContainer(ctx context.Context, containerID string) error | ||
| 17 | WaitForContainer(ctx context.Context, containerID string) error | 17 | WaitForContainer(ctx context.Context, containerID string) error | ||
| 18 | RemoveContainer(ctx context.Context, containerID string, force bool) error | 18 | RemoveContainer(ctx context.Context, containerID string, force bool) error | ||
| 19 | + | ListVolumes(ctx context.Context) ([]*domain.VolumeInfo, error) | |||
| 20 | + | } | |||
| 21 | + | ||||
| 22 | + | // VolumeNamer is a function that maps a source volume name to a destination volume name. | |||
| 23 | + | type VolumeNamer func(sourceVolName string) string | |||
| 24 | + | ||||
| 25 | + | // DefaultNamer returns a VolumeNamer that uses BuildCloneVolumeName with the given preview name. | |||
| 26 | + | func DefaultNamer(previewName string) VolumeNamer { | |||
| 27 | + | return func(sourceVolName string) string { | |||
| 28 | + | return BuildCloneVolumeName(previewName, sourceVolName) | |||
| 29 | + | } | |||
| 19 | } | 30 | } | ||
| 20 | 31 | ||||
| 21 | // BuildCloneVolumeName generates a preview volume name. | 32 | // BuildCloneVolumeName generates a preview volume name. | ||
| @@ -36,11 +47,11 @@ func BuildCloneContainerName(previewName, image string) string { | |||||
| 36 | } | 47 | } | ||
| 37 | 48 | ||||
| 38 | // CloneVolumes copies volumes from source using a read-only helper container. | 49 | // CloneVolumes copies volumes from source using a read-only helper container. | ||
| 39 | - | // sourceVolumes maps volume display names to actual Docker volume names. | 50 | + | // sourceVolumes is a list of actual Docker volume names to clone. |
| 40 | - | func CloneVolumes(ctx context.Context, cloner VolumeCloner, previewName string, sourceVolumes map[string]string) ([]string, error) { | 51 | + | func CloneVolumes(ctx context.Context, cloner VolumeCloner, namer VolumeNamer, sourceVolumes []string) ([]string, error) { |
| 41 | var clonedNames []string | 52 | var clonedNames []string | ||
| 42 | - | for volName, sourceVolName := range sourceVolumes { | 53 | + | for _, sourceVolName := range sourceVolumes { |
| 43 | - | destName := BuildCloneVolumeName(previewName, volName) | 54 | + | destName := namer(sourceVolName) |
| 44 | 55 | ||||
| 45 | if err := cloner.CreateVolume(ctx, destName); err != nil { | 56 | if err := cloner.CreateVolume(ctx, destName); err != nil { | ||
| 46 | cleanupVolumes(ctx, cloner, clonedNames) | 57 | cleanupVolumes(ctx, cloner, clonedNames) | ||