Replace all rpmmd.Depsolve() calls with dnfjson

All calls to rpmmd.Depsolve() are now replaced with the equivalent call
to solver.Depsolve() (or dnfjson.Depsolve() for one-off calls).

Attached an unconfigured dnfjson.BaseSolver to all APIs and server
configurations where rpmmd.RPMMD used to be.  This BaseSolver instance
loads the repository credentials from the system and carries the cache
directory, much like the RPMMD field used to do.  The BaseSolver is used
to create an initialised (configured) solver with the platform variables
(module platform ID, release ver, and arch) before running a Depsolve()
or FetchMetadata() using the NewWithConfig() method.

The FillDependencies() call in the modulesInfoHandler() of the weldr API
has been replaced by a direct call to the Depsolve() function.  This
rpmmd function was only used here.  Replacing the rpmmd.Depsolve() call
in rpmmd.FillDependencies() with dnfjson.Depsolve() would have created
an import cycle.  The FillDependencies() function could have been moved
to dnfjson, but since it's only used in one place, moving the one-line
function body into the caller is ok.

For testing:

The mock-dnf-json is compiled to a temporary directory during test
initialisation and used for each Depsolve() or FetchMetadata() call.

The weldr API tests now use the mock dnfjson.  Each rpmmd_mock.Fixture
now also has a dnfjson_mock.ResponseGenerator.

All API calls in the tests use the proper functions from dnfjson and
only the dnf-json script is mocked.  Because of this, some of the
expected results in responses_test had to be changed to match correct
behaviour:
- The "builds" array of each package in the result of a module or
  project list is now sorted by version number (ascending) because we
  sort the package list in the result of dnfjson by NVR.
- 'check_gpg: true' is added to the expected response of the depsolve
  test.  The repository configs in the test weldr API specify 'CheckGPG:
  True', but the mock responses returned it as false, so the expected
  result didn't need to include it.  Since now we're using the actual
  dnfjson code to convert the mock response to the internal structure,
  the repository settings are correctly used to set flag to true for
  each package associated with that repository.
- The word "occurred" was mistyped as "occured" in rpmmd and is now
  fixed in dnfjson.
This commit is contained in:
Achilleas Koutsou 2022-03-22 23:23:05 +01:00 committed by Tom Gundersen
parent e9a7a50496
commit 177ea1b08f
14 changed files with 1618 additions and 1606 deletions

View file

@ -23,11 +23,11 @@ import (
"github.com/osbuild/osbuild-composer/internal/cloudapi"
v2 "github.com/osbuild/osbuild-composer/internal/cloudapi/v2"
"github.com/osbuild/osbuild-composer/internal/distroregistry"
"github.com/osbuild/osbuild-composer/internal/dnfjson"
"github.com/osbuild/osbuild-composer/internal/jobqueue"
"github.com/osbuild/osbuild-composer/internal/jobqueue/dbjobqueue"
"github.com/osbuild/osbuild-composer/internal/jobqueue/fsjobqueue"
"github.com/osbuild/osbuild-composer/internal/kojiapi"
"github.com/osbuild/osbuild-composer/internal/rpmmd"
"github.com/osbuild/osbuild-composer/internal/weldr"
"github.com/osbuild/osbuild-composer/internal/worker"
)
@ -39,7 +39,7 @@ type Composer struct {
logger *log.Logger
distros *distroregistry.Registry
rpm rpmmd.RPMMD
solver *dnfjson.BaseSolver
workers *worker.Server
weldr *weldr.API
@ -73,7 +73,7 @@ func NewComposer(config *ComposerConfigFile, stateDir, cacheDir string) (*Compos
c.distros = distroregistry.NewDefault()
logrus.Infof("Loaded %d distros", len(c.distros.List()))
c.rpm = rpmmd.NewRPMMD(path.Join(c.cacheDir, "rpmmd"))
c.solver = dnfjson.NewBaseSolver(path.Join(c.cacheDir, "rpmmd"))
var jobs jobqueue.JobQueue
if config.Worker.PGDatabase != "" {
@ -117,7 +117,7 @@ func NewComposer(config *ComposerConfigFile, stateDir, cacheDir string) (*Compos
func (c *Composer) InitWeldr(repoPaths []string, weldrListener net.Listener,
distrosImageTypeDenylist map[string][]string) (err error) {
c.weldr, err = weldr.New(repoPaths, c.stateDir, c.rpm, c.distros, c.logger, c.workers, distrosImageTypeDenylist)
c.weldr, err = weldr.New(repoPaths, c.stateDir, c.solver, c.distros, c.logger, c.workers, distrosImageTypeDenylist)
if err != nil {
return err
}
@ -133,7 +133,7 @@ func (c *Composer) InitAPI(cert, key string, enableTLS bool, enableMTLS bool, en
}
c.api = cloudapi.NewServer(c.workers, c.distros, config)
c.koji = kojiapi.NewServer(c.logger, c.workers, c.rpm, c.distros)
c.koji = kojiapi.NewServer(c.logger, c.workers, c.solver, c.distros)
if !enableTLS {
c.apiListener = l

View file

@ -16,6 +16,7 @@ import (
"github.com/osbuild/osbuild-composer/internal/distro"
"github.com/osbuild/osbuild-composer/internal/distro/fedora"
rhel "github.com/osbuild/osbuild-composer/internal/distro/rhel86"
"github.com/osbuild/osbuild-composer/internal/dnfjson"
"github.com/osbuild/osbuild-composer/internal/rpmmd"
"github.com/osbuild/osbuild-composer/internal/test"
)
@ -39,11 +40,13 @@ func TestFetchChecksum(t *testing.T) {
IgnoreSSL: true,
}
solver := dnfjson.NewSolver("platform:f31", "31", "x86_64", path.Join(dir, "rpmmd"))
// use a fullpath to dnf-json, this allows this test to have an arbitrary
// working directory
rpmMetadata := rpmmd.NewRPMMD(path.Join(dir, "rpmmd"))
_, c, err := rpmMetadata.FetchMetadata([]rpmmd.RepoConfig{repoCfg}, "platform:f31", "x86_64", "31")
solver.SetDNFJSONPath("/usr/libexec/osbuild-composer/dnf-json")
res, err := solver.FetchMetadata([]rpmmd.RepoConfig{repoCfg})
assert.Nilf(t, err, "Failed to fetch checksum: %v", err)
c := res.Checksums
assert.NotEqual(t, "", c["repo"], "The checksum is empty")
}
@ -63,10 +66,7 @@ func TestCrossArchDepsolve(t *testing.T) {
// Set up temporary directory for rpm/dnf cache
dir := t.TempDir()
// use a fullpath to dnf-json, this allows this test to have an arbitrary
// working directory
rpm := rpmmd.NewRPMMD(dir)
baseSolver := dnfjson.NewBaseSolver(dir)
repos, err := rpmmd.LoadRepositories([]string{repoDir}, distroStruct.Name())
require.NoErrorf(t, err, "Failed to LoadRepositories %v", distroStruct.Name())
@ -75,7 +75,7 @@ func TestCrossArchDepsolve(t *testing.T) {
t.Run(archStr, func(t *testing.T) {
arch, err := distroStruct.GetArch(archStr)
require.NoError(t, err)
solver := baseSolver.NewWithConfig(distroStruct.ModulePlatformID(), distroStruct.Releasever(), archStr)
for _, imgTypeStr := range arch.ListImageTypes() {
t.Run(imgTypeStr, func(t *testing.T) {
imgType, err := arch.GetImageType(imgTypeStr)
@ -83,10 +83,10 @@ func TestCrossArchDepsolve(t *testing.T) {
packages := imgType.PackageSets(blueprint.Blueprint{})
_, _, err = rpm.Depsolve(packages["build"], repos[archStr], distroStruct.ModulePlatformID(), archStr, distroStruct.Releasever())
_, err = solver.Depsolve(packages["build"], repos[archStr])
assert.NoError(t, err)
_, _, err = rpm.Depsolve(packages["packages"], repos[archStr], distroStruct.ModulePlatformID(), archStr, distroStruct.Releasever())
_, err = solver.Depsolve(packages["packages"], repos[archStr])
assert.NoError(t, err)
})
}
@ -111,10 +111,7 @@ func TestDepsolvePackageSets(t *testing.T) {
// Set up temporary directory for rpm/dnf cache
dir := t.TempDir()
// use a fullpath to dnf-json, this allows this test to have an arbitrary
// working directory
rpm := rpmmd.NewRPMMD(dir)
solver := dnfjson.NewSolver(distroStruct.ModulePlatformID(), distroStruct.Releasever(), distro.X86_64ArchName, dir)
repos, err := rpmmd.LoadRepositories([]string{repoDir}, distroStruct.Name())
require.NoErrorf(t, err, "Failed to LoadRepositories %v", distroStruct.Name())
@ -150,7 +147,29 @@ func TestDepsolvePackageSets(t *testing.T) {
return expectedPkgSpecsSetNames
}(imagePkgSets, imagePkgSetChains)
gotPackageSpecsSets, err := rpm.DepsolvePackageSets(imagePkgSetChains, imagePkgSets, x86Repos, nil, distroStruct.ModulePlatformID(), x86Arch.Name(), distroStruct.Releasever())
gotPackageSpecsSets := make(map[string]*dnfjson.DepsolveResult, len(imagePkgSets))
// first depsolve package sets that are part of a chain
for specName, setNames := range imagePkgSetChains {
pkgSets := make([]rpmmd.PackageSet, len(setNames))
for idx, pkgSetName := range setNames {
pkgSets[idx] = imagePkgSets[pkgSetName]
delete(imagePkgSets, pkgSetName) // will be depsolved here: remove from map
}
res, err := solver.ChainDepsolve(pkgSets, x86Repos, nil)
if err != nil {
require.Nil(t, err)
}
gotPackageSpecsSets[specName] = res
}
// depsolve the rest of the package sets
for name, pkgSet := range imagePkgSets {
res, err := solver.ChainDepsolve([]rpmmd.PackageSet{pkgSet}, x86Repos, nil)
if err != nil {
require.Nil(t, err)
}
gotPackageSpecsSets[name] = res
}
require.Nil(t, err)
require.EqualValues(t, len(expectedPackageSpecsSetNames), len(gotPackageSpecsSets))
for _, name := range expectedPackageSpecsSetNames {

View file

@ -10,6 +10,7 @@ import (
"path"
"github.com/osbuild/osbuild-composer/internal/distroregistry"
"github.com/osbuild/osbuild-composer/internal/dnfjson"
"github.com/osbuild/osbuild-composer/internal/ostree"
"github.com/osbuild/osbuild-composer/internal/blueprint"
@ -122,24 +123,36 @@ func main() {
panic("os.UserHomeDir(): " + err.Error())
}
rpm_md := rpmmd.NewRPMMD(path.Join(home, ".cache/osbuild-composer/rpmmd"))
packageSets := imageType.PackageSets(composeRequest.Blueprint)
packageSpecSets, err := rpm_md.DepsolvePackageSets(
imageType.PackageSetsChains(),
imageType.PackageSets(composeRequest.Blueprint),
repos,
nil,
d.ModulePlatformID(),
arch.Name(),
d.Releasever(),
)
if err != nil {
panic("Could not depsolve: " + err.Error())
solver := dnfjson.NewSolver(d.ModulePlatformID(), d.Releasever(), arch.Name(), path.Join(home, ".cache/osbuild-composer/rpmmd"))
depsolvedSets := make(map[string][]rpmmd.PackageSpec)
// first depsolve package sets that are part of a chain
for specName, setNames := range imageType.PackageSetsChains() {
pkgSets := make([]rpmmd.PackageSet, len(setNames))
for idx, pkgSetName := range setNames {
pkgSets[idx] = packageSets[pkgSetName]
delete(packageSets, pkgSetName) // will be depsolved here: remove from map
}
res, err := solver.ChainDepsolve(pkgSets, repos, nil)
if err != nil {
panic("Could not depsolve: " + err.Error())
}
depsolvedSets[specName] = res.Dependencies
}
// depsolve the rest of the package sets
for name, pkgSet := range packageSets {
res, err := solver.ChainDepsolve([]rpmmd.PackageSet{pkgSet}, repos, nil)
if err != nil {
panic("Could not depsolve: " + err.Error())
}
depsolvedSets[name] = res.Dependencies
}
var bytes []byte
if rpmmdArg {
bytes, err = json.Marshal(packageSpecSets)
bytes, err = json.Marshal(depsolvedSets)
if err != nil {
panic(err)
}
@ -160,7 +173,7 @@ func main() {
},
},
repos,
packageSpecSets,
depsolvedSets,
seedArg)
if err != nil {
panic(err.Error())

View file

@ -12,24 +12,22 @@ import (
"github.com/osbuild/osbuild-composer/internal/blueprint"
"github.com/osbuild/osbuild-composer/internal/distro"
"github.com/osbuild/osbuild-composer/internal/distro/fedora"
"github.com/osbuild/osbuild-composer/internal/dnfjson"
"github.com/osbuild/osbuild-composer/internal/rpmmd"
"github.com/osbuild/osbuild-composer/internal/store"
"github.com/osbuild/osbuild-composer/internal/target"
)
func getManifest(bp blueprint.Blueprint, t distro.ImageType, a distro.Arch, d distro.Distro, rpm_md rpmmd.RPMMD, repos []rpmmd.RepoConfig) (distro.Manifest, []rpmmd.PackageSpec) {
func getManifest(bp blueprint.Blueprint, t distro.ImageType, a distro.Arch, d distro.Distro, cacheDir string, repos []rpmmd.RepoConfig) (distro.Manifest, []rpmmd.PackageSpec) {
packageSets := t.PackageSets(bp)
pkgSpecSets, err := rpm_md.DepsolvePackageSets(
t.PackageSetsChains(),
packageSets,
repos,
nil,
d.ModulePlatformID(),
a.Name(),
d.Releasever(),
)
if err != nil {
panic("Could not depsolve: " + err.Error())
pkgSpecSets := make(map[string][]rpmmd.PackageSpec)
solver := dnfjson.NewSolver(d.ModulePlatformID(), d.Releasever(), a.Name(), cacheDir)
for name, packages := range packageSets {
res, err := solver.Depsolve(packages, repos)
if err != nil {
panic(err)
}
pkgSpecSets[name] = res.Dependencies
}
manifest, err := t.Manifest(bp.Customizations, distro.ImageOptions{}, repos, pkgSpecSets, 0)
if err != nil {
@ -142,7 +140,7 @@ func main() {
if err != nil {
panic("os.UserHomeDir(): " + err.Error())
}
rpmmd := rpmmd.NewRPMMD(path.Join(homeDir, ".cache/osbuild-composer/rpmmd"))
rpmmdCache := path.Join(homeDir, ".cache/osbuild-composer/rpmmd")
s := store.New(&cwd, a, nil)
if s == nil {
@ -160,7 +158,7 @@ func main() {
if err != nil {
panic(err)
}
manifest, packages := getManifest(bp2, t1, a, d, rpmmd, repos)
manifest, packages := getManifest(bp2, t1, a, d, rpmmdCache, repos)
err = s.PushCompose(id1,
manifest,
t1,
@ -175,7 +173,7 @@ func main() {
if err != nil {
panic(err)
}
manifest, packages = getManifest(bp1, t2, a, d, rpmmd, repos)
manifest, packages = getManifest(bp1, t2, a, d, rpmmdCache, repos)
err = s.PushCompose(id2,
manifest,
t2,

View file

@ -5,6 +5,7 @@ import (
"github.com/sirupsen/logrus"
"github.com/osbuild/osbuild-composer/internal/dnfjson"
"github.com/osbuild/osbuild-composer/internal/rpmmd"
"github.com/osbuild/osbuild-composer/internal/worker"
"github.com/osbuild/osbuild-composer/internal/worker/clienterrors"
@ -18,22 +19,35 @@ type DepsolveJobImpl struct {
// in repos are used for all package sets, whereas the repositories in
// packageSetsRepos are only used for the package set with the same name
// (matching map keys).
func (impl *DepsolveJobImpl) depsolve(packageSetsChains map[string][]string,
packageSets map[string]rpmmd.PackageSet,
repos []rpmmd.RepoConfig,
packageSetsRepos map[string][]rpmmd.RepoConfig,
modulePlatformID, arch, releasever string) (map[string][]rpmmd.PackageSpec, error) {
rpmMD := rpmmd.NewRPMMD(impl.RPMMDCache)
func (impl *DepsolveJobImpl) depsolve(packageSetsChains map[string][]string, packageSets map[string]rpmmd.PackageSet, repos []rpmmd.RepoConfig, packageSetsRepos map[string][]rpmmd.RepoConfig, modulePlatformID, arch, releasever string) (map[string][]rpmmd.PackageSpec, error) {
solver := dnfjson.NewSolver(modulePlatformID, releasever, arch, impl.RPMMDCache)
depsolvedSets := make(map[string][]rpmmd.PackageSpec)
psRepos := make([][]rpmmd.RepoConfig, 0)
return rpmMD.DepsolvePackageSets(
packageSetsChains,
packageSets,
repos,
packageSetsRepos,
modulePlatformID,
arch,
releasever,
)
// first depsolve package sets that are part of a chain
for specName, setNames := range packageSetsChains {
pkgSets := make([]rpmmd.PackageSet, len(setNames))
for idx, pkgSetName := range setNames {
pkgSets[idx] = packageSets[pkgSetName]
psRepos = append(psRepos, packageSetsRepos[pkgSetName]) // will be nil if it doesn't exist
delete(packageSets, pkgSetName) // will be depsolved here: remove from map
}
res, err := solver.ChainDepsolve(pkgSets, repos, psRepos)
if err != nil {
return nil, err
}
depsolvedSets[specName] = res.Dependencies
}
// depsolve the rest of the package sets
for name, pkgSet := range packageSets {
res, err := solver.ChainDepsolve([]rpmmd.PackageSet{pkgSet}, repos, [][]rpmmd.RepoConfig{packageSetsRepos[name]})
if err != nil {
return nil, err
}
depsolvedSets[name] = res.Dependencies
}
return depsolvedSets, nil
}
func (impl *DepsolveJobImpl) Run(job worker.Job) error {

View file

@ -1,6 +1,7 @@
// Package client contains functions for communicating with the API server
// Copyright (C) 2020 by Red Hat, Inc.
//go:build !integration
// +build !integration
package client
@ -11,11 +12,15 @@ import (
"net"
"net/http"
"os"
"os/exec"
"path"
"path/filepath"
"testing"
"github.com/osbuild/osbuild-composer/internal/distro/test_distro"
"github.com/osbuild/osbuild-composer/internal/distroregistry"
"github.com/osbuild/osbuild-composer/internal/dnfjson"
dnfjson_mock "github.com/osbuild/osbuild-composer/internal/mocks/dnfjson"
rpmmd_mock "github.com/osbuild/osbuild-composer/internal/mocks/rpmmd"
"github.com/osbuild/osbuild-composer/internal/reporegistry"
"github.com/osbuild/osbuild-composer/internal/rpmmd"
@ -24,6 +29,20 @@ import (
// Hold test state to share between tests
var testState *TestState
var dnfjsonPath string
func init() {
// compile the mock-dnf-json binary to speed up tests
tmpdir, err := os.MkdirTemp("", "")
if err != nil {
panic(err)
}
dnfjsonPath = filepath.Join(tmpdir, "mock-dnf-json")
cmd := exec.Command("go", "build", "-o", dnfjsonPath, "../../cmd/mock-dnf-json")
if err := cmd.Run(); err != nil {
panic(err)
}
}
func executeTests(m *testing.M) int {
// Setup the mocked server running on a temporary domain socket
@ -45,7 +64,6 @@ func executeTests(m *testing.M) int {
panic(err)
}
fixture := rpmmd_mock.BaseFixture(path.Join(tmpdir, "/jobs"))
rpm := rpmmd_mock.NewRPMMDMock(fixture)
distro1 := test_distro.New()
arch, err := distro1.GetArch(test_distro.TestArchName)
@ -67,8 +85,12 @@ func executeTests(m *testing.M) int {
panic(err)
}
dspath, err := os.MkdirTemp(tmpdir, "")
dnfjsonFixture := dnfjson_mock.Base(dspath)
solver := dnfjson.NewBaseSolver(path.Join(tmpdir, "dnfjson-cache"))
solver.SetDNFJSONPath(dnfjsonPath, dnfjsonFixture)
logger := log.New(os.Stdout, "", 0)
api := weldr.NewTestAPI(rpm, arch, dr, rr, logger, fixture.Store, fixture.Workers, "", nil)
api := weldr.NewTestAPI(solver, arch, dr, rr, logger, fixture.Store, fixture.Workers, "", nil)
server := http.Server{Handler: api}
defer server.Close()

View file

@ -15,6 +15,7 @@ import (
"github.com/osbuild/osbuild-composer/internal/blueprint"
"github.com/osbuild/osbuild-composer/internal/distro"
"github.com/osbuild/osbuild-composer/internal/distroregistry"
"github.com/osbuild/osbuild-composer/internal/dnfjson"
"github.com/osbuild/osbuild-composer/internal/ostree"
"github.com/osbuild/osbuild-composer/internal/rpmmd"
)
@ -139,19 +140,14 @@ func TestDistro_Manifest(t *testing.T, pipelinePath string, prefix string, regis
func getImageTypePkgSpecSets(imageType distro.ImageType, bp blueprint.Blueprint, repos []rpmmd.RepoConfig, cacheDir, dnfJsonPath string) map[string][]rpmmd.PackageSpec {
imgPackageSets := imageType.PackageSets(bp)
rpm_md := rpmmd.NewRPMMD(cacheDir)
imgPackageSpecSets, err := rpm_md.DepsolvePackageSets(
imageType.PackageSetsChains(),
imgPackageSets,
repos,
nil,
imageType.Arch().Distro().ModulePlatformID(),
imageType.Arch().Name(),
imageType.Arch().Distro().Releasever(),
)
if err != nil {
panic("Could not depsolve: " + err.Error())
solver := dnfjson.NewSolver(imageType.Arch().Distro().ModulePlatformID(), imageType.Arch().Distro().Releasever(), imageType.Arch().Name(), cacheDir)
imgPackageSpecSets := make(map[string][]rpmmd.PackageSpec)
for name, packages := range imgPackageSets {
res, err := solver.Depsolve(packages, repos)
if err != nil {
panic("Could not depsolve: " + err.Error())
}
imgPackageSpecSets[name] = res.Dependencies
}
return imgPackageSpecSets

View file

@ -18,6 +18,7 @@ import (
"github.com/osbuild/osbuild-composer/internal/blueprint"
"github.com/osbuild/osbuild-composer/internal/distro"
"github.com/osbuild/osbuild-composer/internal/distroregistry"
"github.com/osbuild/osbuild-composer/internal/dnfjson"
"github.com/osbuild/osbuild-composer/internal/kojiapi/api"
"github.com/osbuild/osbuild-composer/internal/rpmmd"
"github.com/osbuild/osbuild-composer/internal/worker"
@ -25,19 +26,19 @@ import (
// Server represents the state of the koji Server
type Server struct {
logger *log.Logger
workers *worker.Server
rpmMetadata rpmmd.RPMMD
distros *distroregistry.Registry
logger *log.Logger
workers *worker.Server
solver *dnfjson.BaseSolver
distros *distroregistry.Registry
}
// NewServer creates a new koji server
func NewServer(logger *log.Logger, workers *worker.Server, rpmMetadata rpmmd.RPMMD, distros *distroregistry.Registry) *Server {
func NewServer(logger *log.Logger, workers *worker.Server, solver *dnfjson.BaseSolver, distros *distroregistry.Registry) *Server {
s := &Server{
logger: logger,
workers: workers,
rpmMetadata: rpmMetadata,
distros: distros,
logger: logger,
workers: workers,
solver: solver,
distros: distros,
}
return s
@ -122,18 +123,19 @@ func (h *apiHandlers) PostCompose(ctx echo.Context) error {
panic("Could not initialize empty blueprint.")
}
solver := h.server.solver.NewWithConfig(d.ModulePlatformID(), d.Releasever(), arch.Name())
packageSets := imageType.PackageSets(*bp)
packageSpecSets, err := h.server.rpmMetadata.DepsolvePackageSets(
imageType.PackageSetsChains(),
packageSets,
repositories,
nil,
d.ModulePlatformID(),
arch.Name(),
d.Releasever(),
)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Failed to depsolve base base packages for %s/%s/%s: %s", ir.ImageType, ir.Architecture, request.Distribution, err))
packageSpecSets := make(map[string][]rpmmd.PackageSpec)
for specName, setNames := range imageType.PackageSetsChains() {
chain := make([]rpmmd.PackageSet, len(setNames))
for idx, pkgSetName := range setNames {
chain[idx] = packageSets[pkgSetName]
}
res, err := solver.ChainDepsolve(chain, repositories, nil)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Failed to depsolve base base packages for %s/%s/%s: %s", ir.ImageType, ir.Architecture, request.Distribution, err))
}
packageSpecSets[specName] = res.Dependencies
}
manifest, err := imageType.Manifest(nil, distro.ImageOptions{Size: imageType.Size(0)}, repositories, packageSpecSets, manifestSeed)

View file

@ -6,6 +6,9 @@ import (
"fmt"
"net/http"
"net/http/httptest"
"os"
"os/exec"
"path/filepath"
"sync"
"testing"
"time"
@ -14,6 +17,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/osbuild/osbuild-composer/internal/distro/test_distro"
"github.com/osbuild/osbuild-composer/internal/dnfjson"
"github.com/osbuild/osbuild-composer/internal/kojiapi"
"github.com/osbuild/osbuild-composer/internal/kojiapi/api"
distro_mock "github.com/osbuild/osbuild-composer/internal/mocks/distro"
@ -24,16 +28,44 @@ import (
"github.com/osbuild/osbuild-composer/internal/worker/clienterrors"
)
var dnfjsonPath string
func init() {
// compile the mock-dnf-json binary to speed up tests
tmpdir, err := os.MkdirTemp("", "")
if err != nil {
panic(err)
}
dnfjsonPath = filepath.Join(tmpdir, "mock-dnf-json")
cmd := exec.Command("go", "build", "-o", dnfjsonPath, "../../cmd/mock-dnf-json")
if err := cmd.Run(); err != nil {
panic(err)
}
}
func newTestKojiServer(t *testing.T, dir string) (*kojiapi.Server, *worker.Server) {
rpm_fixture := rpmmd_mock.BaseFixture(dir)
rpm := rpmmd_mock.NewRPMMDMock(rpm_fixture)
require.NotNil(t, rpm)
// create tempdir subdirectory for store
dbpath, err := os.MkdirTemp(dir, "")
if err != nil {
panic(err)
}
rpm_fixture := rpmmd_mock.BaseFixture(dbpath)
distros, err := distro_mock.NewDefaultRegistry()
require.NoError(t, err)
require.NotNil(t, distros)
kojiServer := kojiapi.NewServer(nil, rpm_fixture.Workers, rpm, distros)
solver := dnfjson.NewBaseSolver("") // test solver doesn't need a cache dir
// create tempdir subdirectory for solver response file
dspath, err := os.MkdirTemp(dir, "")
if err != nil {
panic(err)
}
respfile := rpm_fixture.ResponseGenerator(dspath)
solver.SetDNFJSONPath(dnfjsonPath, respfile)
kojiServer := kojiapi.NewServer(nil, rpm_fixture.Workers, solver, distros)
require.NotNil(t, kojiServer)
return kojiServer, rpm_fixture.Workers

View file

@ -1,57 +1,14 @@
package rpmmd_mock
import (
"fmt"
"sort"
"time"
"github.com/osbuild/osbuild-composer/internal/jobqueue/fsjobqueue"
"github.com/osbuild/osbuild-composer/internal/worker"
"github.com/osbuild/osbuild-composer/internal/rpmmd"
dnfjson_mock "github.com/osbuild/osbuild-composer/internal/mocks/dnfjson"
"github.com/osbuild/osbuild-composer/internal/store"
"github.com/osbuild/osbuild-composer/internal/worker"
)
type FixtureGenerator func(tmpdir string) Fixture
func generatePackageList() rpmmd.PackageList {
baseTime, err := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
if err != nil {
panic(err)
}
var packageList rpmmd.PackageList
for i := 0; i < 22; i++ {
basePackage := rpmmd.Package{
Name: fmt.Sprintf("package%d", i),
Summary: fmt.Sprintf("pkg%d sum", i),
Description: fmt.Sprintf("pkg%d desc", i),
URL: fmt.Sprintf("https://pkg%d.example.com", i),
Epoch: 0,
Version: fmt.Sprintf("%d.0", i),
Release: fmt.Sprintf("%d.fc30", i),
Arch: "x86_64",
BuildTime: baseTime.AddDate(0, i, 0),
License: "MIT",
}
secondBuild := basePackage
secondBuild.Version = fmt.Sprintf("%d.1", i)
secondBuild.BuildTime = basePackage.BuildTime.AddDate(0, 0, 1)
packageList = append(packageList, basePackage, secondBuild)
}
sort.Slice(packageList, func(i, j int) bool {
return packageList[i].Name < packageList[j].Name
})
return packageList
}
func createBaseWorkersFixture(tmpdir string) *worker.Server {
q, err := fsjobqueue.New(tmpdir)
if err != nil {
@ -60,199 +17,50 @@ func createBaseWorkersFixture(tmpdir string) *worker.Server {
return worker.NewServer(nil, q, worker.Config{BasePath: "/api/worker/v1"})
}
func createBaseDepsolveFixture() []rpmmd.PackageSpec {
return []rpmmd.PackageSpec{
{
Name: "dep-package3",
Epoch: 7,
Version: "3.0.3",
Release: "1.fc30",
Arch: "x86_64",
},
{
Name: "dep-package1",
Epoch: 0,
Version: "1.33",
Release: "2.fc30",
Arch: "x86_64",
},
{
Name: "dep-package2",
Epoch: 0,
Version: "2.9",
Release: "1.fc30",
Arch: "x86_64",
},
}
}
func createBaseDepsolvePackageSetsFixture() map[string][]rpmmd.PackageSpec {
return map[string][]rpmmd.PackageSpec{
"build": {
{
Name: "dep-package3",
Epoch: 7,
Version: "3.0.3",
Release: "1.fc30",
Arch: "x86_64",
},
{
Name: "dep-package1",
Epoch: 0,
Version: "1.33",
Release: "2.fc30",
Arch: "x86_64",
},
{
Name: "dep-package2",
Epoch: 0,
Version: "2.9",
Release: "1.fc30",
Arch: "x86_64",
},
},
"packages": {
{
Name: "dep-package3",
Epoch: 7,
Version: "3.0.3",
Release: "1.fc30",
Arch: "x86_64",
},
{
Name: "dep-package1",
Epoch: 0,
Version: "1.33",
Release: "2.fc30",
Arch: "x86_64",
},
{
Name: "dep-package2",
Epoch: 0,
Version: "2.9",
Release: "1.fc30",
Arch: "x86_64",
},
},
}
}
func BaseFixture(tmpdir string) Fixture {
return Fixture{
fetchPackageList{
generatePackageList(),
map[string]string{"base": "sha256:f34848ca92665c342abd5816c9e3eda0e82180671195362bcd0080544a3bc2ac"},
nil,
},
depsolve{
createBaseDepsolveFixture(),
createBaseDepsolvePackageSetsFixture(),
map[string]string{"base": "sha256:f34848ca92665c342abd5816c9e3eda0e82180671195362bcd0080544a3bc2ac"},
nil,
},
store.FixtureBase(),
createBaseWorkersFixture(tmpdir),
dnfjson_mock.Base,
}
}
func NoComposesFixture(tmpdir string) Fixture {
return Fixture{
fetchPackageList{
generatePackageList(),
map[string]string{"base": "sha256:f34848ca92665c342abd5816c9e3eda0e82180671195362bcd0080544a3bc2ac"},
nil,
},
depsolve{
createBaseDepsolveFixture(),
createBaseDepsolvePackageSetsFixture(),
map[string]string{"base": "sha256:f34848ca92665c342abd5816c9e3eda0e82180671195362bcd0080544a3bc2ac"},
nil,
},
store.FixtureEmpty(),
createBaseWorkersFixture(tmpdir),
dnfjson_mock.Base,
}
}
func NonExistingPackage(tmpdir string) Fixture {
return Fixture{
fetchPackageList{
generatePackageList(),
map[string]string{"base": "sha256:f34848ca92665c342abd5816c9e3eda0e82180671195362bcd0080544a3bc2ac"},
nil,
},
depsolve{
nil,
nil,
nil,
&rpmmd.DNFError{
Kind: "MarkingErrors",
Reason: "Error occurred when marking packages for installation: Problems in request:\nmissing packages: fash",
},
},
store.FixtureBase(),
createBaseWorkersFixture(tmpdir),
dnfjson_mock.NonExistingPackage,
}
}
func BadDepsolve(tmpdir string) Fixture {
return Fixture{
fetchPackageList{
generatePackageList(),
map[string]string{"base": "sha256:f34848ca92665c342abd5816c9e3eda0e82180671195362bcd0080544a3bc2ac"},
nil,
},
depsolve{
nil,
nil,
nil,
&rpmmd.DNFError{
Kind: "DepsolveError",
Reason: "There was a problem depsolving ['go2rpm']: \n Problem: conflicting requests\n - nothing provides askalono-cli needed by go2rpm-1-4.fc31.noarch",
},
},
store.FixtureBase(),
createBaseWorkersFixture(tmpdir),
dnfjson_mock.BadDepsolve,
}
}
func BadFetch(tmpdir string) Fixture {
return Fixture{
fetchPackageList{
ret: nil,
checksums: nil,
err: &rpmmd.DNFError{
Kind: "FetchError",
Reason: "There was a problem when fetching packages.",
},
},
depsolve{
nil,
nil,
nil,
&rpmmd.DNFError{
Kind: "DepsolveError",
Reason: "There was a problem depsolving ['go2rpm']: \n Problem: conflicting requests\n - nothing provides askalono-cli needed by go2rpm-1-4.fc31.noarch",
},
},
store.FixtureBase(),
createBaseWorkersFixture(tmpdir),
dnfjson_mock.BadFetch,
}
}
func OldChangesFixture(tmpdir string) Fixture {
return Fixture{
fetchPackageList{
generatePackageList(),
map[string]string{"base": "sha256:f34848ca92665c342abd5816c9e3eda0e82180671195362bcd0080544a3bc2ac"},
nil,
},
depsolve{
createBaseDepsolveFixture(),
createBaseDepsolvePackageSetsFixture(),
map[string]string{"base": "sha256:f34848ca92665c342abd5816c9e3eda0e82180671195362bcd0080544a3bc2ac"},
nil,
},
store.FixtureOldChanges(),
createBaseWorkersFixture(tmpdir),
dnfjson_mock.Base,
}
}

View file

@ -1,46 +1,13 @@
package rpmmd_mock
import (
"github.com/osbuild/osbuild-composer/internal/rpmmd"
dnfjson_mock "github.com/osbuild/osbuild-composer/internal/mocks/dnfjson"
"github.com/osbuild/osbuild-composer/internal/store"
"github.com/osbuild/osbuild-composer/internal/worker"
)
type fetchPackageList struct {
ret rpmmd.PackageList
checksums map[string]string
err error
}
type depsolve struct {
ret []rpmmd.PackageSpec
retSets map[string][]rpmmd.PackageSpec
checksums map[string]string
err error
}
type Fixture struct {
fetchPackageList
depsolve
*store.Store
Workers *worker.Server
}
type rpmmdMock struct {
Fixture Fixture
}
func NewRPMMDMock(fixture Fixture) rpmmd.RPMMD {
return &rpmmdMock{Fixture: fixture}
}
func (r *rpmmdMock) FetchMetadata(repos []rpmmd.RepoConfig, modulePlatformID, arch, releasever string) (rpmmd.PackageList, map[string]string, error) {
return r.Fixture.fetchPackageList.ret, r.Fixture.fetchPackageList.checksums, r.Fixture.fetchPackageList.err
}
func (r *rpmmdMock) Depsolve(packageSet rpmmd.PackageSet, repos []rpmmd.RepoConfig, modulePlatformID, arch, releasever string) ([]rpmmd.PackageSpec, map[string]string, error) {
return r.Fixture.depsolve.ret, r.Fixture.fetchPackageList.checksums, r.Fixture.depsolve.err
}
func (r *rpmmdMock) DepsolvePackageSets(packageSetsChains map[string][]string, packageSets map[string]rpmmd.PackageSet, repos []rpmmd.RepoConfig, packageSetsRepos map[string][]rpmmd.RepoConfig, modulePlatformID, arch, releasever string) (map[string][]rpmmd.PackageSpec, error) {
return r.Fixture.depsolve.retSets, r.Fixture.depsolve.err
dnfjson_mock.ResponseGenerator
}

View file

@ -33,6 +33,7 @@ import (
"github.com/osbuild/osbuild-composer/internal/common"
"github.com/osbuild/osbuild-composer/internal/distro"
"github.com/osbuild/osbuild-composer/internal/distroregistry"
"github.com/osbuild/osbuild-composer/internal/dnfjson"
"github.com/osbuild/osbuild-composer/internal/jobqueue"
osbuild "github.com/osbuild/osbuild-composer/internal/osbuild2"
"github.com/osbuild/osbuild-composer/internal/ostree"
@ -47,7 +48,7 @@ type API struct {
store *store.Store
workers *worker.Server
rpmmd rpmmd.RPMMD
solver *dnfjson.BaseSolver
archName string
repoRegistry *reporegistry.RepoRegistry
@ -121,7 +122,7 @@ func validDistros(rr *reporegistry.RepoRegistry, dr *distroregistry.Registry, ar
var ValidBlueprintName = regexp.MustCompile(`^[a-zA-Z0-9._-]+$`)
// NewTestAPI is used for the test framework, sets up a single distro
func NewTestAPI(rpm rpmmd.RPMMD, arch distro.Arch, dr *distroregistry.Registry,
func NewTestAPI(solver *dnfjson.BaseSolver, arch distro.Arch, dr *distroregistry.Registry,
rr *reporegistry.RepoRegistry, logger *log.Logger,
store *store.Store, workers *worker.Server, compatOutputDir string,
distrosImageTypeDenylist map[string][]string) *API {
@ -131,7 +132,7 @@ func NewTestAPI(rpm rpmmd.RPMMD, arch distro.Arch, dr *distroregistry.Registry,
api := &API{
store: store,
workers: workers,
rpmmd: rpm,
solver: solver,
archName: arch.Name(),
repoRegistry: rr,
logger: logger,
@ -144,7 +145,7 @@ func NewTestAPI(rpm rpmmd.RPMMD, arch distro.Arch, dr *distroregistry.Registry,
return setupRouter(api)
}
func New(repoPaths []string, stateDir string, rpm rpmmd.RPMMD, dr *distroregistry.Registry,
func New(repoPaths []string, stateDir string, solver *dnfjson.BaseSolver, dr *distroregistry.Registry,
logger *log.Logger, workers *worker.Server, distrosImageTypeDenylist map[string][]string) (*API, error) {
if logger == nil {
logger = log.New(os.Stdout, "", 0)
@ -188,7 +189,7 @@ func New(repoPaths []string, stateDir string, rpm rpmmd.RPMMD, dr *distroregistr
api := &API{
store: store,
workers: workers,
rpmmd: rpm,
solver: solver,
archName: archName,
repoRegistry: rr,
logger: logger,
@ -1257,8 +1258,10 @@ func (api *API) modulesInfoHandler(writer http.ResponseWriter, request *http.Req
return
}
solver := api.solver.NewWithConfig(d.ModulePlatformID(), d.Releasever(), api.archName)
for i := range packageInfos {
err := packageInfos[i].FillDependencies(api.rpmmd, repos, d.ModulePlatformID(), api.archName, d.Releasever())
pkgName := packageInfos[i].Name
solved, err := solver.Depsolve(rpmmd.PackageSet{Include: []string{pkgName}}, repos)
if err != nil {
errors := responseError{
ID: errorId,
@ -1267,6 +1270,7 @@ func (api *API) modulesInfoHandler(writer http.ResponseWriter, request *http.Req
statusResponseError(writer, http.StatusBadRequest, errors)
return
}
packageInfos[i].Dependencies = solved.Dependencies
}
}
@ -1334,11 +1338,11 @@ func (api *API) projectsDepsolveHandler(writer http.ResponseWriter, request *htt
return
}
packages, _, err := api.rpmmd.Depsolve(rpmmd.PackageSet{Include: names},
solver := api.solver.NewWithConfig(d.ModulePlatformID(), d.Releasever(), api.archName)
deps, err := solver.Depsolve(
rpmmd.PackageSet{Include: names},
repos,
d.ModulePlatformID(),
api.archName,
d.Releasever())
)
if err != nil {
errors := responseError{
ID: "ProjectsError",
@ -1347,9 +1351,8 @@ func (api *API) projectsDepsolveHandler(writer http.ResponseWriter, request *htt
statusResponseError(writer, http.StatusBadRequest, errors)
return
}
err = json.NewEncoder(writer).Encode(reply{
Projects: packages,
Projects: deps.Dependencies,
})
common.PanicOnError(err)
}
@ -2142,23 +2145,37 @@ func (api *API) depsolveBlueprintForImageType(bp blueprint.Blueprint, imageType
}
packageSets := imageType.PackageSets(bp)
depsolvedSets := make(map[string][]rpmmd.PackageSpec)
platformID := imageType.Arch().Distro().ModulePlatformID()
releasever := imageType.Arch().Distro().Releasever()
solver := api.solver.NewWithConfig(platformID, releasever, api.archName)
psRepos := make([][]rpmmd.RepoConfig, 0)
packageSpecSets, err := api.rpmmd.DepsolvePackageSets(
imageType.PackageSetsChains(),
packageSets,
imageTypeRepos,
packageSetsRepos,
platformID,
api.archName,
releasever,
)
if err != nil {
return nil, err
// first depsolve package sets that are part of a chain
for specName, setNames := range imageType.PackageSetsChains() {
pkgSets := make([]rpmmd.PackageSet, len(setNames))
for idx, pkgSetName := range setNames {
pkgSets[idx] = packageSets[pkgSetName]
psRepos = append(psRepos, packageSetsRepos[pkgSetName]) // will be nil if it doesn't exist
delete(packageSets, pkgSetName) // will be depsolved here: remove from map
}
res, err := solver.ChainDepsolve(pkgSets, imageTypeRepos, psRepos)
if err != nil {
return nil, err
}
depsolvedSets[specName] = res.Dependencies
}
return packageSpecSets, nil
// depsolve the rest of the package sets
for name, pkgSet := range packageSets {
res, err := solver.ChainDepsolve([]rpmmd.PackageSet{pkgSet}, imageTypeRepos, [][]rpmmd.RepoConfig{packageSetsRepos[name]})
if err != nil {
return nil, err
}
depsolvedSets[name] = res.Dependencies
}
return depsolvedSets, nil
}
// Schedule new compose by first translating the appropriate blueprint into a pipeline and then
@ -3126,8 +3143,12 @@ func (api *API) fetchPackageList(distroName string) (rpmmd.PackageList, error) {
return nil, err
}
packages, _, err := api.rpmmd.FetchMetadata(repos, d.ModulePlatformID(), api.archName, d.Releasever())
return packages, err
solver := api.solver.NewWithConfig(d.ModulePlatformID(), d.Releasever(), api.archName)
packages, err := solver.FetchMetadata(repos)
if err != nil {
return nil, err
}
return packages.Packages, nil
}
// Returns only user-defined repositories, which should be used only for
@ -3186,12 +3207,14 @@ func (api *API) depsolveBlueprint(bp blueprint.Blueprint) ([]rpmmd.PackageSpec,
return nil, err
}
packages, _, err := api.rpmmd.Depsolve(rpmmd.PackageSet{Include: bp.GetPackages()}, repos, d.ModulePlatformID(), api.archName, d.Releasever())
solver := api.solver.NewWithConfig(d.ModulePlatformID(), d.Releasever(), api.archName)
solved, err := solver.Depsolve(rpmmd.PackageSet{Include: bp.GetPackages()}, repos)
if err != nil {
return nil, err
}
return packages, err
packages := solved.Dependencies
return packages, nil
}
func (api *API) uploadsScheduleHandler(writer http.ResponseWriter, request *http.Request, params httprouter.Params) {

View file

@ -11,6 +11,8 @@ import (
"net/http"
"net/http/httptest"
"os"
"os/exec"
"path/filepath"
"strconv"
"testing"
"time"
@ -20,6 +22,8 @@ import (
"github.com/osbuild/osbuild-composer/internal/distro"
"github.com/osbuild/osbuild-composer/internal/distro/test_distro"
"github.com/osbuild/osbuild-composer/internal/distroregistry"
"github.com/osbuild/osbuild-composer/internal/dnfjson"
dnfjson_mock "github.com/osbuild/osbuild-composer/internal/mocks/dnfjson"
rpmmd_mock "github.com/osbuild/osbuild-composer/internal/mocks/rpmmd"
"github.com/osbuild/osbuild-composer/internal/ostree/mock_ostree_repo"
"github.com/osbuild/osbuild-composer/internal/reporegistry"
@ -33,9 +37,29 @@ import (
"github.com/stretchr/testify/require"
)
var dnfjsonPath string
func init() {
// compile the mock-dnf-json binary to speed up tests
tmpdir, err := os.MkdirTemp("", "")
if err != nil {
panic(err)
}
dnfjsonPath = filepath.Join(tmpdir, "mock-dnf-json")
cmd := exec.Command("go", "build", "-o", dnfjsonPath, "../../cmd/mock-dnf-json")
if err := cmd.Run(); err != nil {
panic(err)
}
}
func createWeldrAPI(tempdir string, fixtureGenerator rpmmd_mock.FixtureGenerator) (*API, *store.Store) {
fixture := fixtureGenerator(tempdir)
rpm := rpmmd_mock.NewRPMMDMock(fixture)
// create tempdir subdirectory for store
dbpath, err := os.MkdirTemp(tempdir, "")
if err != nil {
panic(err)
}
fixture := fixtureGenerator(dbpath)
rr := reporegistry.NewFromDistrosRepoConfigs(rpmmd.DistrosRepoConfigs{
test_distro.TestDistroName: {
@ -62,15 +86,29 @@ func createWeldrAPI(tempdir string, fixtureGenerator rpmmd_mock.FixtureGenerator
panic(err)
}
return NewTestAPI(rpm, arch, dr, rr, nil, fixture.Store, fixture.Workers, "", nil), fixture.Store
solver := dnfjson.NewBaseSolver("") // test solver doesn't need a cache dir
// create tempdir subdirectory for solver response file
dspath, err := os.MkdirTemp(tempdir, "")
if err != nil {
panic(err)
}
respfile := fixture.ResponseGenerator(dspath)
solver.SetDNFJSONPath(dnfjsonPath, respfile)
testApi := NewTestAPI(solver, arch, dr, rr, nil, fixture.Store, fixture.Workers, "", nil)
return testApi, fixture.Store
}
// createWeldrAPI2 is an alternative function to createWeldrAPI, using different test architecture
// with more than a single image type
func createWeldrAPI2(tempdir string, fixtureGenerator rpmmd_mock.FixtureGenerator,
distroImageTypeDenylist map[string][]string) (*API, *store.Store) {
fixture := fixtureGenerator(tempdir)
rpm := rpmmd_mock.NewRPMMDMock(fixture)
func createWeldrAPI2(tempdir string, fixtureGenerator rpmmd_mock.FixtureGenerator, distroImageTypeDenylist map[string][]string) (*API, *store.Store) {
// create tempdir subdirectory for store
dbpath, err := os.MkdirTemp(tempdir, "")
if err != nil {
panic(err)
}
fixture := fixtureGenerator(dbpath)
rr := reporegistry.NewFromDistrosRepoConfigs(rpmmd.DistrosRepoConfigs{
test_distro.TestDistroName: {
@ -97,7 +135,16 @@ func createWeldrAPI2(tempdir string, fixtureGenerator rpmmd_mock.FixtureGenerato
panic(err)
}
return NewTestAPI(rpm, arch, dr, rr, nil, fixture.Store, fixture.Workers, "", distroImageTypeDenylist), fixture.Store
// create tempdir subdirectory for solver response file
dspath, err := os.MkdirTemp(tempdir, "")
if err != nil {
panic(err)
}
solver := dnfjson.NewBaseSolver("")
respfile := fixture.ResponseGenerator(dspath)
solver.SetDNFJSONPath(dnfjsonPath, respfile)
return NewTestAPI(solver, arch, dr, rr, nil, fixture.Store, fixture.Workers, "", distroImageTypeDenylist), fixture.Store
}
func TestBasic(t *testing.T) {
@ -625,14 +672,6 @@ func TestCompose(t *testing.T) {
manifest, err := imgType.Manifest(nil, distro.ImageOptions{}, nil, nil, 0)
require.NoError(t, err)
tempdir := t.TempDir()
fixtureGenerator := rpmmd_mock.NoComposesFixture
fixture := fixtureGenerator(tempdir)
rpmMD := rpmmd_mock.NewRPMMDMock(fixture)
packageSpecSets, err := rpmMD.DepsolvePackageSets(nil, nil, nil, nil, "", "", "")
require.NoError(t, err)
expectedComposeLocal := &store.Compose{
Blueprint: &blueprint.Blueprint{
Name: "test",
@ -647,7 +686,7 @@ func TestCompose(t *testing.T) {
ImageType: imgType,
Manifest: manifest,
},
Packages: packageSpecSets["packages"],
Packages: dnfjson_mock.BaseDeps(),
}
expectedComposeLocalAndAws := &store.Compose{
Blueprint: &blueprint.Blueprint{
@ -678,7 +717,7 @@ func TestCompose(t *testing.T) {
},
},
},
Packages: packageSpecSets["packages"],
Packages: dnfjson_mock.BaseDeps(),
}
expectedComposeOSTree := &store.Compose{
Blueprint: &blueprint.Blueprint{
@ -694,7 +733,7 @@ func TestCompose(t *testing.T) {
ImageType: imgType,
Manifest: manifest,
},
Packages: packageSpecSets["packages"],
Packages: dnfjson_mock.BaseDeps(),
}
// For 2nd distribution
@ -720,7 +759,7 @@ func TestCompose(t *testing.T) {
ImageType: imgType2,
Manifest: manifest2,
},
Packages: packageSpecSets["packages"],
Packages: dnfjson_mock.BaseDeps(),
}
// create two ostree repos, one to serve the default test_distro ref (for fallback tests) and one to serve a custom ref
@ -891,8 +930,9 @@ func TestCompose(t *testing.T) {
},
}
tempdir := t.TempDir()
for _, c := range cases {
api, s := createWeldrAPI(tempdir, fixtureGenerator)
api, s := createWeldrAPI(tempdir, rpmmd_mock.NoComposesFixture)
test.TestRoute(t, api, c.External, c.Method, c.Path, c.Body, c.ExpectedStatus, c.ExpectedJSON, c.IgnoreFields...)
if c.ExpectedStatus != http.StatusOK {
@ -1431,10 +1471,10 @@ func TestProjectsDepsolve(t *testing.T) {
ExpectedStatus int
ExpectedJSON string
}{
{rpmmd_mock.NonExistingPackage, "/api/v0/projects/depsolve/fash", http.StatusBadRequest, `{"status":false,"errors":[{"id":"ProjectsError","msg":"BadRequest: DNF error occured: MarkingErrors: Error occurred when marking packages for installation: Problems in request:\nmissing packages: fash"}]}`},
{rpmmd_mock.BaseFixture, "/api/v0/projects/depsolve/fish", http.StatusOK, `{"projects":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]}`},
{rpmmd_mock.BaseFixture, "/api/v0/projects/depsolve/fish?distro=test-distro-2", http.StatusOK, `{"projects":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]}`},
{rpmmd_mock.BadDepsolve, "/api/v0/projects/depsolve/go2rpm", http.StatusBadRequest, `{"status":false,"errors":[{"id":"ProjectsError","msg":"BadRequest: DNF error occured: DepsolveError: There was a problem depsolving ['go2rpm']: \n Problem: conflicting requests\n - nothing provides askalono-cli needed by go2rpm-1-4.fc31.noarch"}]}`},
{rpmmd_mock.NonExistingPackage, "/api/v0/projects/depsolve/fash", http.StatusBadRequest, `{"status":false,"errors":[{"id":"ProjectsError","msg":"BadRequest: DNF error occurred: MarkingErrors: Error occurred when marking packages for installation: Problems in request:\nmissing packages: fash"}]}`},
{rpmmd_mock.BaseFixture, "/api/v0/projects/depsolve/fish", http.StatusOK, `{"projects":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64","check_gpg":true},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64","check_gpg":true},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64","check_gpg":true}]}`},
{rpmmd_mock.BaseFixture, "/api/v0/projects/depsolve/fish?distro=test-distro-2", http.StatusOK, `{"projects":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64","check_gpg":true},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64","check_gpg":true},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64","check_gpg":true}]}`},
{rpmmd_mock.BadDepsolve, "/api/v0/projects/depsolve/go2rpm", http.StatusBadRequest, `{"status":false,"errors":[{"id":"ProjectsError","msg":"BadRequest: DNF error occurred: DepsolveError: There was a problem depsolving ['go2rpm']: \n Problem: conflicting requests\n - nothing provides askalono-cli needed by go2rpm-1-4.fc31.noarch"}]}`},
{rpmmd_mock.BaseFixture, "/api/v0/projects/depsolve/fish?distro=fedora-1", http.StatusBadRequest, `{"status":false,"errors":[{"id":"DistroError","msg":"Invalid distro: fedora-1"}]}`},
}
@ -1458,7 +1498,7 @@ func TestProjectsInfo(t *testing.T) {
{rpmmd_mock.BaseFixture, "/api/v0/projects/info/nonexistingpkg", http.StatusBadRequest, `{"status":false,"errors":[{"id":"UnknownProject","msg":"No packages have been found."}]}`},
{rpmmd_mock.BaseFixture, "/api/v0/projects/info/*", http.StatusOK, projectsInfoResponse},
{rpmmd_mock.BaseFixture, "/api/v0/projects/info/package2*,package16", http.StatusOK, projectsInfoFilteredResponse},
{rpmmd_mock.BadFetch, "/api/v0/projects/info/package2*,package16", http.StatusBadRequest, `{"status":false,"errors":[{"id":"ModulesError","msg":"msg: DNF error occured: FetchError: There was a problem when fetching packages."}]}`},
{rpmmd_mock.BadFetch, "/api/v0/projects/info/package2*,package16", http.StatusBadRequest, `{"status":false,"errors":[{"id":"ModulesError","msg":"msg: DNF error occurred: FetchError: There was a problem when fetching packages."}]}`},
{rpmmd_mock.BaseFixture, "/api/v0/projects/info/package16?distro=test-distro-2", http.StatusOK, projectsInfoPackage16Response},
{rpmmd_mock.BaseFixture, "/api/v0/projects/info/package16?distro=fedora-1", http.StatusBadRequest, `{"status":false,"errors":[{"id":"DistroError","msg":"Invalid distro: fedora-1"}]}`},
}
@ -1481,10 +1521,10 @@ func TestModulesInfo(t *testing.T) {
{rpmmd_mock.BaseFixture, "/api/v0/modules/info", http.StatusBadRequest, `{"status":false,"errors":[{"id":"UnknownModule","msg":"No packages specified."}]}`},
{rpmmd_mock.BaseFixture, "/api/v0/modules/info/", http.StatusBadRequest, `{"status":false,"errors":[{"id":"UnknownModule","msg":"No packages specified."}]}`},
{rpmmd_mock.BaseFixture, "/api/v0/modules/info/nonexistingpkg", http.StatusBadRequest, `{"status":false,"errors":[{"id":"UnknownModule","msg":"No packages have been found."}]}`},
{rpmmd_mock.BadDepsolve, "/api/v0/modules/info/package1", http.StatusBadRequest, `{"status":false,"errors":[{"id":"ModulesError","msg":"Cannot depsolve package package1: DNF error occured: DepsolveError: There was a problem depsolving ['go2rpm']: \n Problem: conflicting requests\n - nothing provides askalono-cli needed by go2rpm-1-4.fc31.noarch"}]}`},
{rpmmd_mock.BadDepsolve, "/api/v0/modules/info/package1", http.StatusBadRequest, `{"status":false,"errors":[{"id":"ModulesError","msg":"Cannot depsolve package package1: DNF error occurred: DepsolveError: There was a problem depsolving ['go2rpm']: \n Problem: conflicting requests\n - nothing provides askalono-cli needed by go2rpm-1-4.fc31.noarch"}]}`},
{rpmmd_mock.BaseFixture, "/api/v0/modules/info/package2*,package16", http.StatusOK, modulesInfoFilteredResponse},
{rpmmd_mock.BaseFixture, "/api/v0/modules/info/*", http.StatusOK, modulesInfoResponse},
{rpmmd_mock.BadFetch, "/api/v0/modules/info/package2*,package16", http.StatusBadRequest, `{"status":false,"errors":[{"id":"ModulesError","msg":"msg: DNF error occured: FetchError: There was a problem when fetching packages."}]}`},
{rpmmd_mock.BadFetch, "/api/v0/modules/info/package2*,package16", http.StatusBadRequest, `{"status":false,"errors":[{"id":"ModulesError","msg":"msg: DNF error occurred: FetchError: There was a problem when fetching packages."}]}`},
{rpmmd_mock.BaseFixture, "/api/v1/modules/info/package2*,package16", http.StatusOK, modulesInfoFilteredResponse},
{rpmmd_mock.BaseFixture, "/api/v1/modules/info/package16?distro=test-distro-2", http.StatusOK, modulesInfoPackage16Response},
{rpmmd_mock.BaseFixture, "/api/v1/modules/info/package16?distro=fedora-1", http.StatusBadRequest, `{"status":false,"errors":[{"id":"DistroError","msg":"Invalid distro: fedora-1"}]}`},
@ -1507,7 +1547,7 @@ func TestProjectsList(t *testing.T) {
}{
{rpmmd_mock.BaseFixture, "/api/v0/projects/list", http.StatusOK, projectsListResponse},
{rpmmd_mock.BaseFixture, "/api/v0/projects/list/", http.StatusOK, projectsListResponse},
{rpmmd_mock.BadFetch, "/api/v0/projects/list/", http.StatusBadRequest, `{"status":false,"errors":[{"id":"ProjectsError","msg":"msg: DNF error occured: FetchError: There was a problem when fetching packages."}]}`},
{rpmmd_mock.BadFetch, "/api/v0/projects/list/", http.StatusBadRequest, `{"status":false,"errors":[{"id":"ProjectsError","msg":"msg: DNF error occurred: FetchError: There was a problem when fetching packages."}]}`},
{rpmmd_mock.BaseFixture, "/api/v0/projects/list?offset=1&limit=1", http.StatusOK, projectsList1Response},
{rpmmd_mock.BaseFixture, "/api/v0/projects/list?distro=test-distro-2&offset=1&limit=1", http.StatusOK, projectsList1Response},
{rpmmd_mock.BaseFixture, "/api/v0/projects/list?distro=fedora-1&offset=1&limit=1", http.StatusBadRequest, `{"status":false,"errors":[{"id":"DistroError","msg":"Invalid distro: fedora-1"}]}`},
@ -1532,7 +1572,7 @@ func TestModulesList(t *testing.T) {
{rpmmd_mock.BaseFixture, "/api/v0/modules/list/", http.StatusOK, modulesListResponse},
{rpmmd_mock.BaseFixture, "/api/v0/modules/list/nonexistingpkg", http.StatusBadRequest, `{"status":false,"errors":[{"id":"UnknownModule","msg":"No packages have been found."}]}`},
{rpmmd_mock.BaseFixture, "/api/v0/modules/list/package2*,package16", http.StatusOK, modulesListFilteredResponse},
{rpmmd_mock.BadFetch, "/api/v0/modules/list/package2*,package16", http.StatusBadRequest, `{"status":false,"errors":[{"id":"ModulesError","msg":"msg: DNF error occured: FetchError: There was a problem when fetching packages."}]}`},
{rpmmd_mock.BadFetch, "/api/v0/modules/list/package2*,package16", http.StatusBadRequest, `{"status":false,"errors":[{"id":"ModulesError","msg":"msg: DNF error occurred: FetchError: There was a problem when fetching packages."}]}`},
{rpmmd_mock.BaseFixture, "/api/v0/modules/list/package2*,package16?offset=1&limit=1", http.StatusOK, `{"total":4,"offset":1,"limit":1,"modules":[{"name":"package2","group_type":"rpm"}]}`},
{rpmmd_mock.BaseFixture, "/api/v0/modules/list/*", http.StatusOK, modulesListResponse},
{rpmmd_mock.BaseFixture, "/api/v0/modules/list/package2*,package16?distro=test-distro-2&offset=1&limit=1", http.StatusOK, `{"total":4,"offset":1,"limit":1,"modules":[{"name":"package2","group_type":"rpm"}]}`},
@ -1635,14 +1675,6 @@ func TestComposePOST_ImageTypeDenylist(t *testing.T) {
manifest, err := imgType.Manifest(nil, distro.ImageOptions{}, nil, nil, 0)
require.NoError(t, err)
tempdir := t.TempDir()
fixtureGenerator := rpmmd_mock.NoComposesFixture
fixture := fixtureGenerator(tempdir)
rpmMD := rpmmd_mock.NewRPMMDMock(fixture)
packageSpecSets, err := rpmMD.DepsolvePackageSets(nil, nil, nil, nil, "", "", "")
require.NoError(t, err)
expectedComposeLocal := &store.Compose{
Blueprint: &blueprint.Blueprint{
Name: "test",
@ -1657,7 +1689,7 @@ func TestComposePOST_ImageTypeDenylist(t *testing.T) {
ImageType: imgType,
Manifest: manifest,
},
Packages: packageSpecSets["packages"],
Packages: dnfjson_mock.BaseDeps(),
}
expectedComposeLocal2 := &store.Compose{
@ -1674,7 +1706,7 @@ func TestComposePOST_ImageTypeDenylist(t *testing.T) {
ImageType: imgType2,
Manifest: manifest,
},
Packages: packageSpecSets["packages"],
Packages: dnfjson_mock.BaseDeps(),
}
var cases = []struct {
@ -1785,8 +1817,10 @@ func TestComposePOST_ImageTypeDenylist(t *testing.T) {
},
}
tempdir := t.TempDir()
for _, c := range cases {
api, s := createWeldrAPI2(tempdir, fixtureGenerator, c.imageTypeDenylist)
api, s := createWeldrAPI2(tempdir, rpmmd_mock.NoComposesFixture, c.imageTypeDenylist)
test.TestRoute(t, api, true, "POST", c.Path, c.Body, c.ExpectedStatus, c.ExpectedJSON, c.IgnoreFields...)
if c.ExpectedStatus != http.StatusOK {

File diff suppressed because it is too large Load diff