Skip to content
7 changes: 3 additions & 4 deletions cmd/gen-manifests/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import (
"github.com/osbuild/images/pkg/distrofactory"
"github.com/osbuild/images/pkg/experimentalflags"
"github.com/osbuild/images/pkg/manifest"
"github.com/osbuild/images/pkg/manifestgen"
"github.com/osbuild/images/pkg/manifestgen/manifestmock"
"github.com/osbuild/images/pkg/ostree"
"github.com/osbuild/images/pkg/rhsm/facts"
Expand Down Expand Up @@ -301,7 +300,7 @@ func makeManifestJob(
var depsolvedSets map[string]depsolvednf.DepsolveResult
if content["packages"] {
solver := depsolvednf.NewSolver(distribution.ModulePlatformID(), distribution.Releasever(), archName, distribution.Name(), cacheDir)
depsolvedSets, err = manifestgen.DefaultDepsolve(solver, cacheDir, os.Stderr, common.Must(manifest.GetPackageSetChains()), distribution, archName)
depsolvedSets, err = solver.DepsolveAll(common.Must(manifest.GetPackageSetChains()))
if err != nil {
err = fmt.Errorf("[%s] depsolve failed: %s", filename, err.Error())
return
Expand All @@ -318,7 +317,7 @@ func makeManifestJob(

var containerSpecs map[string][]container.Spec
if content["containers"] {
containerSpecs, err = manifestgen.DefaultContainerResolver(manifest.GetContainerSourceSpecs(), archName)
containerSpecs, err = container.NewResolver(archName).ResolveAll(manifest.GetContainerSourceSpecs())
if err != nil {
return fmt.Errorf("[%s] container resolution failed: %s", filename, err.Error())
}
Expand All @@ -328,7 +327,7 @@ func makeManifestJob(

var commitSpecs map[string][]ostree.CommitSpec
if content["commits"] {
commitSpecs, err = manifestgen.DefaultCommitResolver(manifest.GetOSTreeSourceSpecs())
commitSpecs, err = ostree.ResolveAll(manifest.GetOSTreeSourceSpecs())
if err != nil {
return fmt.Errorf("[%s] ostree commit resolution failed: %s", filename, err.Error())
}
Expand Down
45 changes: 45 additions & 0 deletions pkg/container/blocking_resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,51 @@ func TestBlockingResolver(t *testing.T) {
assert.ElementsMatch(t, have, want)
}

func TestBlockingResolverResolveAll(t *testing.T) {
assert := assert.New(t)
registry := testregistry.New()
defer registry.Close()
repo := registry.AddRepo("library/osbuild")
ref := registry.GetRef("library/osbuild")

sources := make(map[string][]container.SourceSpec, 2)
expected := make(map[string][]container.Spec, 2)
for _, name := range []string{"pipeline-one", "pipeline-two"} {
for idx := 0; idx < 10; idx++ {
checksum := repo.AddImage(
[]testregistry.Blob{testregistry.NewDataBlobFromBase64(testregistry.RootLayer)},
[]string{"amd64", "ppc64le"},
fmt.Sprintf("image %s %d", name, idx),
time.Time{})

tag := fmt.Sprintf("%s%d", name, idx)
repo.AddTag(checksum, tag)

plSources := sources[name]
refTag := fmt.Sprintf("%s:%s", ref, tag)
sources[name] = append(plSources, container.SourceSpec{
Source: refTag,
Name: "",
Digest: common.ToPtr(""),
TLSVerify: common.ToPtr(false),
Local: false,
})

expSpec, err := registry.Resolve(refTag, arch.ARCH_X86_64)
assert.NoError(err)
expSpecs := expected[name]
expected[name] = append(expSpecs, expSpec)
}
}

resolver := container.NewBlockingResolver("amd64")

have, err := resolver.ResolveAll(sources)
assert.NoError(err)
assert.NotNil(have)
assert.Equal(have, expected)
}

func TestBlockingResolverFail(t *testing.T) {
resolver := container.NewBlockingResolver("amd64")

Expand Down
93 changes: 75 additions & 18 deletions pkg/container/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"sort"
"strings"
"time"
)

type resolveResult struct {
Expand All @@ -15,14 +16,15 @@ type resolveResult struct {
type Resolver interface {
Add(spec SourceSpec)
Finish() ([]Spec, error)

Resolve(spec SourceSpec) (Spec, error)
ResolveAll(sources map[string][]SourceSpec) (map[string][]Spec, error)
}

type asyncResolver struct {
jobs int
queue chan resolveResult

ctx context.Context

Arch string
AuthFilePath string

Expand All @@ -42,7 +44,6 @@ func NewResolver(arch string) *asyncResolver {
// NOTE: this should return the Resolver interface, but osbuild-composer
// sets the AuthFilePath and for now we don't want to break the API.
return &asyncResolver{
ctx: context.Background(),
queue: make(chan resolveResult, 2),
Arch: arch,

Expand All @@ -66,7 +67,9 @@ func (r *asyncResolver) Add(spec SourceSpec) {
}

go func() {
spec, err := client.Resolve(r.ctx, spec.Name, spec.Local)
ctx, cancelTimeout := context.WithTimeout(context.Background(), 60*time.Second)
defer cancelTimeout()
spec, err := client.Resolve(ctx, spec.Name, spec.Local)
if err != nil {
err = fmt.Errorf("'%s': %w", spec.Source, err)
}
Expand All @@ -75,7 +78,6 @@ func (r *asyncResolver) Add(spec SourceSpec) {
}

func (r *asyncResolver) Finish() ([]Spec, error) {

specs := make([]Spec, 0, r.jobs)
errs := make([]string, 0, r.jobs)
for r.jobs > 0 {
Expand All @@ -100,6 +102,37 @@ func (r *asyncResolver) Finish() ([]Spec, error) {
return specs, nil
}

func (r *asyncResolver) Resolve(spec SourceSpec) (Spec, error) {
r.Add(spec)
resSlice, err := r.Finish()
if err != nil {
return Spec{}, err
}

if len(resSlice) != 1 {
return Spec{}, fmt.Errorf("unexpected results in container resolver: expected 1 but received %d", len(resSlice))
}

return resSlice[0], nil
}

func (r *asyncResolver) ResolveAll(sources map[string][]SourceSpec) (map[string][]Spec, error) {
containers := make(map[string][]Spec, len(sources))
for name, srcList := range sources {
containerSpecs := make([]Spec, len(srcList))
for idx, src := range srcList {
res, err := r.Resolve(src)
if err != nil {
return nil, fmt.Errorf("failed to resolve container: %w", err)
}
containerSpecs[idx] = res
}
containers[name] = containerSpecs
}

return containers, nil
}

type blockingResolver struct {
Arch string
AuthFilePath string
Expand All @@ -120,19 +153,7 @@ func NewBlockingResolver(arch string) Resolver {
}

func (r *blockingResolver) Add(src SourceSpec) {
client, err := r.newClient(src.Source)
if err != nil {
r.results = append(r.results, resolveResult{err: err})
return
}

client.SetTLSVerify(src.TLSVerify)
client.SetArchitectureChoice(r.Arch)
if r.AuthFilePath != "" {
client.SetAuthFilePath(r.AuthFilePath)
}

spec, err := client.Resolve(context.TODO(), src.Name, src.Local)
spec, err := r.Resolve(src)
if err != nil {
err = fmt.Errorf("'%s': %w", src.Source, err)
}
Expand Down Expand Up @@ -160,3 +181,39 @@ func (r *blockingResolver) Finish() ([]Spec, error) {

return specs, nil
}

func (r *blockingResolver) Resolve(source SourceSpec) (Spec, error) {
client, err := r.newClient(source.Source)
if err != nil {
return Spec{}, err
}

client.SetTLSVerify(source.TLSVerify)
client.SetArchitectureChoice(r.Arch)
if r.AuthFilePath != "" {
client.SetAuthFilePath(r.AuthFilePath)
}

ctx, cancelTimeout := context.WithTimeout(context.Background(), 60*time.Second)
defer cancelTimeout()
return client.Resolve(ctx, source.Name, source.Local)
}

// ResolveAll calls [Resolve] with each container source spec in the map and
// returns a map of results with the corresponding keys as the input argument.
func (r *blockingResolver) ResolveAll(sources map[string][]SourceSpec) (map[string][]Spec, error) {
containers := make(map[string][]Spec, len(sources))
for name, srcList := range sources {
containerSpecs := make([]Spec, len(srcList))
for idx, src := range srcList {
res, err := r.Resolve(src)
if err != nil {
return nil, fmt.Errorf("failed to resolve container: %w", err)
}
containerSpecs[idx] = res
}
containers[name] = containerSpecs
}

return containers, nil
}
21 changes: 21 additions & 0 deletions pkg/depsolvednf/depsolvednf.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ type Solver struct {
subscriptions *rhsm.Subscriptions
subscriptionsErr error

sbomType sbom.StandardType

// Stderr is the stderr output from osbuild-depsolve-dnf, if unset os.Stderr
// will be used.
//
Expand Down Expand Up @@ -252,6 +254,11 @@ func collectRepos(pkgSets []rpmmd.PackageSet) []rpmmd.RepoConfig {
return repos
}

// Set the SBOM type to generate with the depsolve.
func (s *Solver) SetSBOMType(sbomType sbom.StandardType) {
s.sbomType = sbomType
}

// Depsolve the list of required package sets with explicit excludes using
// their associated repositories. Each package set is depsolved as a separate
// transactions in a chain. It returns a list of all packages (with solved
Expand Down Expand Up @@ -317,6 +324,20 @@ func (s *Solver) Depsolve(pkgSets []rpmmd.PackageSet, sbomType sbom.StandardType
}, nil
}

// DepsolveAll calls [Solver.Depsolve] with each package set slice in the map and
// returns a map of results with the corresponding keys as the input argument.
func (s *Solver) DepsolveAll(pkgSetsMap map[string][]rpmmd.PackageSet) (map[string]DepsolveResult, error) {
results := make(map[string]DepsolveResult, len(pkgSetsMap))
for name, pkgSet := range pkgSetsMap {
res, err := s.Depsolve(pkgSet, s.sbomType)
if err != nil {
return nil, fmt.Errorf("error depsolving package sets for %q: %w", name, err)
}
results[name] = *res
}
return results, nil
}

// FetchMetadata returns the list of all the available packages in repos and
// their info.
func (s *Solver) FetchMetadata(repos []rpmmd.RepoConfig) (rpmmd.PackageList, error) {
Expand Down
Loading
Loading