debian-forge-composer/internal/cloudapi/v2/depsolve.go
Achilleas Koutsou 8d9f52cf1f go.mod: update osbuild/images (v0.172.0) and osbuild/blueprint (v1.12.0)
The internal blueprint implementation has been removed from
osbuild/images.  Conversion from osbuild/blueprint blueprints to
osbuild/images blueprints is no longer necessary.
2025-08-07 20:15:33 +02:00

145 lines
4.2 KiB
Go

package v2
// DepsolveRequest methods
import (
"context"
"crypto/rand"
"fmt"
"math"
"math/big"
"time"
"github.com/osbuild/images/pkg/distrofactory"
"github.com/osbuild/images/pkg/reporegistry"
"github.com/osbuild/images/pkg/rpmmd"
"github.com/osbuild/images/pkg/sbom"
"github.com/osbuild/osbuild-composer/internal/worker"
)
func (request *DepsolveRequest) Depsolve(df *distrofactory.Factory, rr *reporegistry.RepoRegistry, workers *worker.Server) ([]rpmmd.PackageSpec, error) {
// Convert the requested blueprint to a composer blueprint
bp, err := ConvertRequestBP(request.Blueprint)
if err != nil {
return nil, err
}
// If the blueprint include distro and/or architecture they must match the ones
// in request -- otherwise the results may not be what is expected.
if len(bp.Distro) > 0 && bp.Distro != request.Distribution {
return nil, HTTPError(ErrorMismatchedDistribution)
}
// XXX CloudAPI Blueprint needs to have missing Architecture added first
/*
if len(bp.Architecture) > 0 && bp.Architecture != request.Architecture {
return nil, HTTPError(ErrorMismatchedArchitecture)
}
*/
distro := df.GetDistro(request.Distribution)
if distro == nil {
return nil, HTTPError(ErrorUnsupportedDistribution)
}
distroArch, err := distro.GetArch(request.Architecture)
if err != nil {
return nil, HTTPErrorWithInternal(ErrorUnsupportedArchitecture, err)
}
var repos []rpmmd.RepoConfig
if request.Repositories != nil {
repos, err = convertRepos(*request.Repositories, []Repository{}, []string{})
if err != nil {
// Error comes from genRepoConfig and is already an HTTPError
return nil, err
}
} else {
repos, err = rr.ReposByArchName(request.Distribution, distroArch.Name(), false)
if err != nil {
return nil, HTTPErrorWithInternal(ErrorInvalidRepository, err)
}
}
packageSet := make(map[string][]rpmmd.PackageSet, 1)
// If there is an optional image_type we use manifest to setup the package/repo list
if request.ImageType != nil {
imageType, err := distroArch.GetImageType(imageTypeFromApiImageType(*request.ImageType, distroArch))
if err != nil {
return nil, HTTPError(ErrorUnsupportedImageType)
}
// use the same seed for all images so we get the same IDs
bigSeed, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
if err != nil {
return nil, HTTPError(ErrorFailedToGenerateManifestSeed)
}
manifestSeed := bigSeed.Int64()
ir := imageRequest{
imageType: imageType,
repositories: repos,
manifestSeed: manifestSeed,
}
manifestSource, _, err := ir.imageType.Manifest(&bp, ir.imageOptions, ir.repositories, &ir.manifestSeed)
if err != nil {
return nil, HTTPErrorWithInternal(ErrorFailedToDepsolve, err)
}
packageSet = manifestSource.GetPackageSetChains()
} else {
// Send the depsolve request to the worker
packageSet["os"] = []rpmmd.PackageSet{
{
Include: bp.GetPackages(),
EnabledModules: bp.GetEnabledModules(),
Repositories: repos,
},
}
}
depsolveJobID, err := workers.EnqueueDepsolve(&worker.DepsolveJob{
PackageSets: packageSet,
ModulePlatformID: distro.ModulePlatformID(),
Arch: distroArch.Name(),
Releasever: distro.Releasever(),
SbomType: sbom.StandardTypeNone,
}, "")
if err != nil {
return nil, HTTPErrorWithInternal(ErrorEnqueueingJob, err)
}
// Limit how long a depsolve can take
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*depsolveTimeoutMin)
defer cancel()
// Wait until depsolve job is finished, fails, or is canceled
var result worker.DepsolveJobResult
for {
time.Sleep(time.Millisecond * 50)
info, err := workers.DepsolveJobInfo(depsolveJobID, &result)
if err != nil {
return nil, HTTPErrorWithInternal(ErrorFailedToDepsolve, err)
}
if result.JobError != nil {
return nil, HTTPErrorWithInternal(ErrorFailedToDepsolve, err)
}
if info.JobStatus != nil {
if info.JobStatus.Canceled {
return nil, HTTPErrorWithInternal(ErrorFailedToDepsolve, err)
}
if !info.JobStatus.Finished.IsZero() {
break
}
}
select {
case <-ctx.Done():
return nil, HTTPErrorWithInternal(ErrorFailedToDepsolve, fmt.Errorf("Depsolve job %q timed out", depsolveJobID))
default:
}
}
return result.PackageSpecs["os"], nil
}