109 lines
3.6 KiB
Go
109 lines
3.6 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package app
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"slices"
|
|
"time"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/tentacle"
|
|
)
|
|
|
|
// Preparation runs on the caller's worker. Its busy reservation remains owned
|
|
// by Manage through the lifecycle reply, including rollback after cancellation.
|
|
func (runtime *tentacleRuntime) beginRepositoryWork() bool {
|
|
runtime.mutex.Lock()
|
|
defer runtime.mutex.Unlock()
|
|
if runtime.repositoryBusy {
|
|
return false
|
|
}
|
|
runtime.repositoryBusy = true
|
|
return true
|
|
}
|
|
|
|
func (runtime *tentacleRuntime) endRepositoryWork() {
|
|
runtime.mutex.Lock()
|
|
runtime.repositoryBusy = false
|
|
runtime.mutex.Unlock()
|
|
}
|
|
|
|
func repositoryForInstallation(snapshot tentacle.Snapshot, request tentacle.ManagementRequest) (tentacle.Repository, error) {
|
|
if request.Entry != nil {
|
|
return tentacle.Repository{}, errors.New("unexpected tentacle entry in installation request")
|
|
}
|
|
if snapshot.Suppressed || snapshot.Message != "" {
|
|
return tentacle.Repository{}, tentacle.ErrUnavailable
|
|
}
|
|
index := slices.IndexFunc(snapshot.Repositories, func(value tentacle.Repository) bool { return value.URL == request.URL })
|
|
if index < 0 {
|
|
return tentacle.Repository{}, errors.New("repository is not configured")
|
|
}
|
|
repository := snapshot.Repositories[index]
|
|
id := tentacle.PackageID(repository, request.ID)
|
|
installed := slices.ContainsFunc(snapshot.Entries, func(value tentacle.EntryStatus) bool { return value.Entry.ID == id })
|
|
if request.Action == "upgrade" && !installed {
|
|
return tentacle.Repository{}, errors.New("tentacle is not installed")
|
|
}
|
|
if request.Action != "upgrade" && installed {
|
|
return tentacle.Repository{}, errors.New("tentacle is already installed")
|
|
}
|
|
if request.Action == "upgrade" {
|
|
for _, status := range snapshot.Entries {
|
|
if status.Entry.ID != id {
|
|
continue
|
|
}
|
|
for _, manifest := range repository.Tentacles {
|
|
if manifest.ID != request.ID {
|
|
continue
|
|
}
|
|
if manifest.Version == status.Entry.Version {
|
|
return tentacle.Repository{}, errors.New("tentacle is already at the repository version")
|
|
}
|
|
newer, err := tentacle.IsNewerVersion(manifest.Version, status.Entry.Version)
|
|
if err != nil {
|
|
return tentacle.Repository{}, fmt.Errorf("cannot compare tentacle versions: %w", err)
|
|
}
|
|
if !newer {
|
|
return tentacle.Repository{}, errors.New("repository tentacle version is not newer than installed version")
|
|
}
|
|
break
|
|
}
|
|
break
|
|
}
|
|
}
|
|
return repository, nil
|
|
}
|
|
|
|
func (runtime *tentacleRuntime) prepareManagedPackage(ctx context.Context, repository tentacle.Repository, id string) (*tentacle.PreparedPackage, error) {
|
|
prepare := runtime.preparePackage
|
|
if prepare == nil {
|
|
prepare = tentacle.PreparePackage
|
|
}
|
|
prepareCtx, cancel := context.WithTimeout(ctx, 45*time.Second)
|
|
stop := context.AfterFunc(runtime.lifecycleContext, cancel)
|
|
prepared, err := prepare(prepareCtx, tentacle.PackageRoot(runtime.registry.Path), repository, id)
|
|
stop()
|
|
cancel()
|
|
return prepared, err
|
|
}
|
|
|
|
func (runtime *tentacleRuntime) fetchManagedRepository(ctx context.Context, origin string) (tentacle.Repository, error) {
|
|
fetch := runtime.fetchRepository
|
|
if fetch == nil {
|
|
fetch = tentacle.FetchRepository
|
|
}
|
|
// Fetch on the Preferences worker, leaving lifecycle and native input
|
|
// free to disable, revoke, and process failures while the network waits.
|
|
fetchCtx, cancel := context.WithTimeout(ctx, 45*time.Second)
|
|
stop := context.AfterFunc(runtime.lifecycleContext, cancel)
|
|
repository, err := fetch(fetchCtx, origin)
|
|
stop()
|
|
cancel()
|
|
return repository, err
|
|
}
|