From 248f0a6d5590d0758e6818f0ae523758b5dfedd1 Mon Sep 17 00:00:00 2001 From: "Brian C. Lane" Date: Thu, 30 Jan 2020 14:01:21 -0800 Subject: [PATCH] Fix module support in blueprints/freeze This adds support for the modules field. It moves the version replacement into a separate function, setPkgEVRA, and adds tests for the new function as well as for blueprints with packages in both the packages and modules lists. --- internal/weldr/api.go | 42 ++++++++++++++++++++---- internal/weldr/api_test.go | 65 ++++++++++++++++++++++++++++++++++---- 2 files changed, 94 insertions(+), 13 deletions(-) diff --git a/internal/weldr/api.go b/internal/weldr/api.go index 2d95f0eb6..8351814f3 100644 --- a/internal/weldr/api.go +++ b/internal/weldr/api.go @@ -847,6 +847,25 @@ func (api *API) blueprintsDepsolveHandler(writer http.ResponseWriter, request *h }) } +// setPkgEVRA replaces the version globs in the blueprint with their EVRA values from the dependencies +// +// The dependencies must be pre-sorted for this function to work properly +// It will return an error if it cannot find a package in the dependencies +func setPkgEVRA(dependencies []rpmmd.PackageSpec, packages []blueprint.Package) error { + for pkgIndex, pkg := range packages { + i := sort.Search(len(dependencies), func(i int) bool { + return dependencies[i].Name >= pkg.Name + }) + if i < len(dependencies) && dependencies[i].Name == pkg.Name { + packages[pkgIndex].Version = dependencies[i].Version + "-" + dependencies[i].Release + "." + dependencies[i].Arch + } else { + // Packages should not be missing from the depsolve results + return fmt.Errorf("%s missing from depsolve results", pkg.Name) + } + } + return nil +} + func (api *API) blueprintsFreezeHandler(writer http.ResponseWriter, request *http.Request, params httprouter.Params) { if !verifyRequestVersion(writer, params, 0) { return @@ -903,16 +922,25 @@ func (api *API) blueprintsFreezeHandler(writer http.ResponseWriter, request *htt return dependencies[i].Name < dependencies[j].Name }) - for pkgIndex, pkg := range blueprint.Packages { - // sort.Search requires the input to be sorted - i := sort.Search(len(dependencies), func(i int) bool { - return dependencies[i].Name >= pkg.Name - }) - if i < len(dependencies) && dependencies[i].Name == pkg.Name { - blueprint.Packages[pkgIndex].Version = dependencies[i].Version + "-" + dependencies[i].Release + "." + dependencies[i].Arch + err = setPkgEVRA(dependencies, blueprint.Packages) + if err != nil { + rerr := responseError{ + ID: "BlueprintsError", + Msg: fmt.Sprintf("%s: %s", name, err.Error()), } + errors = append(errors, rerr) + break } + err = setPkgEVRA(dependencies, blueprint.Modules) + if err != nil { + rerr := responseError{ + ID: "BlueprintsError", + Msg: fmt.Sprintf("%s: %s", name, err.Error()), + } + errors = append(errors, rerr) + break + } blueprints = append(blueprints, blueprintFrozen{*blueprint}) } diff --git a/internal/weldr/api_test.go b/internal/weldr/api_test.go index d4ca34bd4..13d030263 100644 --- a/internal/weldr/api_test.go +++ b/internal/weldr/api_test.go @@ -1,4 +1,4 @@ -package weldr_test +package weldr import ( "archive/tar" @@ -18,21 +18,21 @@ import ( "github.com/osbuild/osbuild-composer/internal/blueprint" test_distro "github.com/osbuild/osbuild-composer/internal/distro/fedoratest" rpmmd_mock "github.com/osbuild/osbuild-composer/internal/mocks/rpmmd" + "github.com/osbuild/osbuild-composer/internal/rpmmd" "github.com/osbuild/osbuild-composer/internal/store" "github.com/osbuild/osbuild-composer/internal/test" - "github.com/osbuild/osbuild-composer/internal/weldr" "github.com/BurntSushi/toml" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" ) -func createWeldrAPI(fixtureGenerator rpmmd_mock.FixtureGenerator) (*weldr.API, *store.Store) { +func createWeldrAPI(fixtureGenerator rpmmd_mock.FixtureGenerator) (*API, *store.Store) { fixture := fixtureGenerator() rpm := rpmmd_mock.NewRPMMDMock(fixture) d := test_distro.New() - return weldr.New(rpm, "x86_64", d, nil, fixture.Store), fixture.Store + return New(rpm, "x86_64", d, nil, fixture.Store), fixture.Store } func TestBasic(t *testing.T) { @@ -202,6 +202,59 @@ func TestBlueprintsInfoToml(t *testing.T) { } } +func TestSetPkgEVRA(t *testing.T) { + + // Sorted list of dependencies + deps := []rpmmd.PackageSpec{ + { + 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", + }, + { + Name: "dep-package3", + Epoch: 0, + Version: "3.0.3", + Release: "1.fc30", + Arch: "x86_64", + }, + } + pkgs := []blueprint.Package{ + {Name: "dep-package1", Version: "*"}, + {Name: "dep-package2", Version: "*"}, + } + // Replace globs with dependencies + err := setPkgEVRA(deps, pkgs) + if err != nil { + t.Fatalf("setPkgEVRA failed: %s", err.Error()) + } + if pkgs[0].Version != "1.33-2.fc30.x86_64" { + t.Fatalf("setPkgEVRA Unexpected pkg version") + } + if pkgs[1].Version != "2.9-1.fc30.x86_64" { + t.Fatalf("setPkgEVRA Unexpected pkg version") + } + + // Test that a missing package in deps returns an error + pkgs = []blueprint.Package{ + {Name: "dep-package1", Version: "*"}, + {Name: "dep-package0", Version: "*"}, + } + err = setPkgEVRA(deps, pkgs) + if err == nil || err.Error() != "dep-package0 missing from depsolve results" { + t.Fatalf("setPkgEVRA missing package failed to return error") + } +} + func TestBlueprintsFreeze(t *testing.T) { var cases = []struct { Fixture rpmmd_mock.FixtureGenerator @@ -209,12 +262,12 @@ func TestBlueprintsFreeze(t *testing.T) { ExpectedStatus int ExpectedJSON string }{ - {rpmmd_mock.BaseFixture, "/api/v0/blueprints/freeze/test", http.StatusOK, `{"blueprints":[{"blueprint":{"name":"test","description":"Test","version":"0.0.1","packages":[{"name":"dep-package1","version":"1.33-2.fc30.x86_64"},{"name":"dep-package3","version":"3.0.3-1.fc30.x86_64"}],"modules":[],"groups":[]}}],"errors":[]}`}, + {rpmmd_mock.BaseFixture, "/api/v0/blueprints/freeze/test", http.StatusOK, `{"blueprints":[{"blueprint":{"name":"test","description":"Test","version":"0.0.1","packages":[{"name":"dep-package1","version":"1.33-2.fc30.x86_64"},{"name":"dep-package3","version":"3.0.3-1.fc30.x86_64"}],"modules":[{"name":"dep-package2","version":"2.9-1.fc30.x86_64"}],"groups":[]}}],"errors":[]}`}, } for _, c := range cases { api, _ := createWeldrAPI(c.Fixture) - test.SendHTTP(api, false, "POST", "/api/v0/blueprints/new", `{"name":"test","description":"Test","packages":[{"name":"dep-package1","version":"*"},{"name":"dep-package3","version":"*"}],"version":"0.0.0"}`) + test.SendHTTP(api, false, "POST", "/api/v0/blueprints/new", `{"name":"test","description":"Test","packages":[{"name":"dep-package1","version":"*"},{"name":"dep-package3","version":"*"}], "modules":[{"name":"dep-package2","version":"*"}],"version":"0.0.0"}`) test.TestRoute(t, api, false, "GET", c.Path, ``, c.ExpectedStatus, c.ExpectedJSON) test.SendHTTP(api, false, "DELETE", "/api/v0/blueprints/delete/test", ``) }