Skip to content

Commit a615696

Browse files
authored
fix: fix project update order and authentication (#2794)
1 parent a89d796 commit a615696

8 files changed

Lines changed: 496 additions & 134 deletions

File tree

backend/internal/di/providers.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,11 @@ func provideContainerRegistryServiceInternal(db *database.DB, docker *services.D
114114
}, kv)
115115
}
116116

117+
func provideProjectServiceInternal(db *database.DB, settings *services.SettingsService, event *services.EventService, image *services.ImageService, docker *services.DockerClientService, build *services.BuildService, environment *services.EnvironmentService, cfg *config.Config) *services.ProjectService {
118+
return services.NewProjectService(db, settings, event, image, docker, build, cfg).
119+
WithRegistryCredentialsProvider(environment.GetEnabledRegistryCredentials)
120+
}
121+
117122
// provideUpdaterServiceInternal passes *SystemUpgradeService for the unexported
118123
// selfUpgradeService parameter so wire never sees the unexported type.
119124
func provideUpdaterServiceInternal(db *database.DB, settings *services.SettingsService, docker *services.DockerClientService, project *services.ProjectService, imageUpdate *services.ImageUpdateService, registry *services.ContainerRegistryService, event *services.EventService, image *services.ImageService, notification *services.NotificationService, systemUpgrade *services.SystemUpgradeService, activity *services.ActivityService) *services.UpdaterService {

backend/internal/di/wire.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ var ServiceSet = wire.NewSet(
4545
services.NewImageService,
4646
services.NewBuildService,
4747
services.NewBuildWorkspaceService,
48-
services.NewProjectService,
48+
provideProjectServiceInternal,
4949
services.NewContainerService,
5050
services.NewDashboardService,
5151
services.NewNetworkService,

backend/internal/di/wire_gen.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/internal/services/project_service.go

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,22 @@ import (
4242
)
4343

4444
type ProjectService struct {
45-
db *database.DB
46-
settingsService *SettingsService
47-
eventService *EventService
48-
imageService *ImageService
49-
dockerService *DockerClientService
50-
buildService *BuildService
51-
config *config.Config
45+
db *database.DB
46+
settingsService *SettingsService
47+
eventService *EventService
48+
imageService *ImageService
49+
dockerService *DockerClientService
50+
buildService *BuildService
51+
config *config.Config
52+
registryCredentialsProvider registryCredentialsProviderInternal
5253

5354
composeNameCacheMu sync.RWMutex
5455
composeNameToProjID map[string]string
5556
composeCache *cache.KeyedCache[string, composeCacheEntry]
5657
}
5758

59+
type registryCredentialsProviderInternal func(context.Context) ([]containerregistry.Credential, error)
60+
5861
type composeCacheEntry struct {
5962
composePath string
6063
composeMtime time.Time
@@ -75,6 +78,27 @@ func NewProjectService(db *database.DB, settingsService *SettingsService, eventS
7578
}
7679
}
7780

81+
func (s *ProjectService) WithRegistryCredentialsProvider(provider func(context.Context) ([]containerregistry.Credential, error)) *ProjectService {
82+
if s == nil {
83+
return nil
84+
}
85+
s.registryCredentialsProvider = provider
86+
return s
87+
}
88+
89+
func (s *ProjectService) resolveRegistryCredentialsInternal(ctx context.Context) ([]containerregistry.Credential, error) {
90+
if s == nil || s.registryCredentialsProvider == nil {
91+
return nil, nil
92+
}
93+
94+
credentials, err := s.registryCredentialsProvider(ctx)
95+
if err != nil {
96+
return nil, fmt.Errorf("get enabled registry credentials: %w", err)
97+
}
98+
99+
return credentials, nil
100+
}
101+
78102
func (s *ProjectService) getPathMapper(ctx context.Context) *projects.PathMapper {
79103
configuredPath := s.settingsService.GetStringSetting(ctx, "projectsDirectory", "/app/data/projects")
80104

@@ -203,7 +227,6 @@ const (
203227
)
204228

205229
var (
206-
composePullProjectServicesInternal = projects.ComposePull
207230
composeStopProjectServicesInternal = projects.ComposeStop
208231
composeUpProjectServicesInternal = projects.ComposeUp
209232
)
@@ -686,17 +709,29 @@ func (s *ProjectService) reconcilePulledImageRefsInternal(ctx context.Context, i
686709
}
687710
}
688711

689-
func (s *ProjectService) composePullSelectedServicesInternal(ctx context.Context, compProj *composetypes.Project, servicesToUpdate []string) error {
712+
func (s *ProjectService) composePullSelectedServicesInternal(
713+
ctx context.Context,
714+
compProj *composetypes.Project,
715+
servicesToUpdate []string,
716+
user models.User,
717+
credentials []containerregistry.Credential,
718+
) error {
690719
if compProj == nil {
691720
return nil
692721
}
693722

694-
imageRefsToReconcile := buildSelectedProjectImageRefsInternal(compProj, servicesToUpdate)
695-
if err := composePullProjectServicesInternal(ctx, compProj, servicesToUpdate); err != nil {
696-
return err
723+
imageRefsToPull := buildSelectedProjectImageRefsInternal(compProj, servicesToUpdate)
724+
if len(imageRefsToPull) == 0 {
725+
return nil
726+
}
727+
728+
progressWriter, _ := ctx.Value(projects.ProgressWriterKey{}).(io.Writer)
729+
for _, imageRef := range imageRefsToPull {
730+
if err := s.pullAndReconcileImageInternal(ctx, imageRef, progressWriter, user, credentials); err != nil {
731+
return err
732+
}
697733
}
698734

699-
s.reconcilePulledImageRefsInternal(ctx, imageRefsToReconcile)
700735
return nil
701736
}
702737

@@ -707,6 +742,10 @@ func (s *ProjectService) pullAndReconcileImageInternal(
707742
user models.User,
708743
credentials []containerregistry.Credential,
709744
) error {
745+
if s == nil || s.imageService == nil {
746+
return errors.New("image service not available")
747+
}
748+
710749
settings := s.settingsService.GetSettingsConfig()
711750

712751
pullCtx, pullCancel := timeouts.WithTimeout(ctx, settings.DockerImagePullTimeout.AsInt(), timeouts.DefaultDockerImagePull)
@@ -761,9 +800,17 @@ func (s *ProjectService) UpdateProjectServices(ctx context.Context, projectID st
761800
return err
762801
}
763802

803+
credentials, err := s.resolveRegistryCredentialsInternal(ctx)
804+
if err != nil {
805+
if statusErr := s.updateProjectStatusInternal(ctx, projectID, previousStatus); statusErr != nil {
806+
slog.ErrorContext(ctx, "UpdateProjectServices: failed to restore project status after credential lookup failure", "projectID", projectID, "error", statusErr)
807+
}
808+
return fmt.Errorf("resolve registry credentials: %w", err)
809+
}
810+
764811
// 3. Pull images for specific services
765812
writeProjectProgressInternal(ctx, "Pulling updated service images", 20, "pull")
766-
if err := s.composePullSelectedServicesInternal(ctx, compProj, servicesToUpdate); err != nil {
813+
if err := s.composePullSelectedServicesInternal(ctx, compProj, servicesToUpdate, user, credentials); err != nil {
767814
if statusErr := s.updateProjectStatusInternal(ctx, projectID, previousStatus); statusErr != nil {
768815
slog.ErrorContext(ctx, "UpdateProjectServices: failed to restore project status after compose pull failure", "projectID", projectID, "error", statusErr)
769816
}

0 commit comments

Comments
 (0)