dnf-json: make independent from the host
We must avoid depending on the host's state in any way. This achieves isolation in the following ways: - rather than the default config file /dev/null is used - rather than sharing the host persistent state dir a temporary one is used and thrown away for each call - the module_platform_id is set explicitly per supported distro, rather than taken from /etc/os-release. Optionally, the cache directory can be configured, as we may want to keep this separate from the host, if for no other reason than accounting. However, the cache appears to be well-behaved, so we can keep sharing it between calls (or even with the host). This speeds up things considerably, so this is definitely what we want. Signed-off-by: Tom Gundersen <teg@jklm.no>
This commit is contained in:
parent
b6d9268810
commit
cdd1912e78
5 changed files with 81 additions and 68 deletions
|
|
@ -81,7 +81,7 @@ func main() {
|
|||
}
|
||||
|
||||
rpmmd := rpmmd.NewRPMMD()
|
||||
_, checksums, err := rpmmd.Depsolve(packages, nil, d.Repositories(archArg), true)
|
||||
_, checksums, err := rpmmd.Depsolve(packages, nil, d.Repositories(archArg), d.ModulePlatformID(), true)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
|
|
|
|||
107
dnf-json
107
dnf-json
|
|
@ -7,6 +7,7 @@ import hawkey
|
|||
import json
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
DNF_ERROR_EXIT_CODE = 10
|
||||
|
||||
|
|
@ -37,8 +38,13 @@ def dnfrepo(desc, parent_conf=None):
|
|||
return repo
|
||||
|
||||
|
||||
def create_base(repos, clean=False):
|
||||
def create_base(repos, module_platform_id, persistdir, cachedir, clean=False):
|
||||
base = dnf.Base()
|
||||
base.conf.module_platform_id = module_platform_id
|
||||
base.conf.config_file_path = "/dev/null"
|
||||
base.conf.persistdir = persistdir
|
||||
if cachedir:
|
||||
base.conf.cachedir = cachedir
|
||||
|
||||
if clean:
|
||||
shutil.rmtree(base.conf.cachedir, ignore_errors=True)
|
||||
|
|
@ -81,55 +87,60 @@ def repo_checksums(base):
|
|||
|
||||
call = json.load(sys.stdin)
|
||||
command = call["command"]
|
||||
arguments = call.get("arguments", {})
|
||||
arguments = call["arguments"]
|
||||
repos = arguments.get("repos", {})
|
||||
clean = arguments.get("clean", False)
|
||||
cachedir = arguments.get("cachedir", None)
|
||||
module_platform_id = arguments["module_platform_id"]
|
||||
|
||||
if command == "dump":
|
||||
base = create_base(arguments.get("repos", {}), arguments.get("clean", False))
|
||||
packages = []
|
||||
for package in base.sack.query().available():
|
||||
packages.append({
|
||||
"name": package.name,
|
||||
"summary": package.summary,
|
||||
"description": package.description,
|
||||
"url": package.url,
|
||||
"epoch": package.epoch,
|
||||
"version": package.version,
|
||||
"release": package.release,
|
||||
"arch": package.arch,
|
||||
"buildtime": timestamp_to_rfc3339(package.buildtime),
|
||||
"license": package.license
|
||||
})
|
||||
json.dump({
|
||||
"checksums": repo_checksums(base),
|
||||
"packages": packages
|
||||
}, sys.stdout)
|
||||
with tempfile.TemporaryDirectory() as persistdir:
|
||||
base = create_base(repos, module_platform_id, persistdir, cachedir, clean)
|
||||
|
||||
elif command == "depsolve":
|
||||
base = create_base(arguments.get("repos", {}), arguments.get("clean", False))
|
||||
errors = []
|
||||
if command == "dump":
|
||||
packages = []
|
||||
for package in base.sack.query().available():
|
||||
packages.append({
|
||||
"name": package.name,
|
||||
"summary": package.summary,
|
||||
"description": package.description,
|
||||
"url": package.url,
|
||||
"epoch": package.epoch,
|
||||
"version": package.version,
|
||||
"release": package.release,
|
||||
"arch": package.arch,
|
||||
"buildtime": timestamp_to_rfc3339(package.buildtime),
|
||||
"license": package.license
|
||||
})
|
||||
json.dump({
|
||||
"checksums": repo_checksums(base),
|
||||
"packages": packages
|
||||
}, sys.stdout)
|
||||
|
||||
try:
|
||||
base.install_specs(arguments["package-specs"], exclude=arguments.get("exclude-specs", []))
|
||||
except dnf.exceptions.MarkingErrors as e:
|
||||
exit_with_dnf_error("MarkingErrors", f"Error occurred when marking packages for installation: {e}")
|
||||
elif command == "depsolve":
|
||||
errors = []
|
||||
|
||||
try:
|
||||
base.resolve()
|
||||
except dnf.exceptions.DepsolveError as e:
|
||||
exit_with_dnf_error("DepsolveError", f"There was a problem depsolving {arguments['package-specs']}: {e}")
|
||||
try:
|
||||
base.install_specs(arguments["package-specs"], exclude=arguments.get("exclude-specs", []))
|
||||
except dnf.exceptions.MarkingErrors as e:
|
||||
exit_with_dnf_error("MarkingErrors", f"Error occurred when marking packages for installation: {e}")
|
||||
|
||||
dependencies = []
|
||||
for package in base.transaction.install_set:
|
||||
dependencies.append({
|
||||
"name": package.name,
|
||||
"epoch": package.epoch,
|
||||
"version": package.version,
|
||||
"release": package.release,
|
||||
"arch": package.arch,
|
||||
"remote_location": package.remote_location(),
|
||||
"checksum": f"{hawkey.chksum_name(package.chksum[0])}:{package.chksum[1].hex()}",
|
||||
})
|
||||
json.dump({
|
||||
"checksums": repo_checksums(base),
|
||||
"dependencies": dependencies
|
||||
}, sys.stdout)
|
||||
try:
|
||||
base.resolve()
|
||||
except dnf.exceptions.DepsolveError as e:
|
||||
exit_with_dnf_error("DepsolveError", f"There was a problem depsolving {arguments['package-specs']}: {e}")
|
||||
|
||||
dependencies = []
|
||||
for package in base.transaction.install_set:
|
||||
dependencies.append({
|
||||
"name": package.name,
|
||||
"epoch": package.epoch,
|
||||
"version": package.version,
|
||||
"release": package.release,
|
||||
"arch": package.arch,
|
||||
"remote_location": package.remote_location(),
|
||||
"checksum": f"{hawkey.chksum_name(package.chksum[0])}:{package.chksum[1].hex()}",
|
||||
})
|
||||
json.dump({
|
||||
"checksums": repo_checksums(base),
|
||||
"dependencies": dependencies
|
||||
}, sys.stdout)
|
||||
|
|
|
|||
|
|
@ -30,10 +30,10 @@ func NewRPMMDMock(fixture Fixture) rpmmd.RPMMD {
|
|||
return &rpmmdMock{Fixture: fixture}
|
||||
}
|
||||
|
||||
func (r *rpmmdMock) FetchPackageList(repos []rpmmd.RepoConfig) (rpmmd.PackageList, map[string]string, error) {
|
||||
func (r *rpmmdMock) FetchPackageList(repos []rpmmd.RepoConfig, modulePlatformID string) (rpmmd.PackageList, map[string]string, error) {
|
||||
return r.Fixture.fetchPackageList.ret, r.Fixture.fetchPackageList.checksums, r.Fixture.fetchPackageList.err
|
||||
}
|
||||
|
||||
func (r *rpmmdMock) Depsolve(specs, excludeSpecs []string, repos []rpmmd.RepoConfig, clean bool) ([]rpmmd.PackageSpec, map[string]string, error) {
|
||||
func (r *rpmmdMock) Depsolve(specs, excludeSpecs []string, repos []rpmmd.RepoConfig, modulePlatformID string, clean bool) ([]rpmmd.PackageSpec, map[string]string, error) {
|
||||
return r.Fixture.depsolve.ret, r.Fixture.fetchPackageList.checksums, r.Fixture.depsolve.err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,8 +95,8 @@ type PackageInfo struct {
|
|||
}
|
||||
|
||||
type RPMMD interface {
|
||||
FetchPackageList(repos []RepoConfig) (PackageList, map[string]string, error)
|
||||
Depsolve(specs, excludeSpecs []string, repos []RepoConfig, clean bool) ([]PackageSpec, map[string]string, error)
|
||||
FetchPackageList(repos []RepoConfig, modulePlatformID string) (PackageList, map[string]string, error)
|
||||
Depsolve(specs, excludeSpecs []string, repos []RepoConfig, modulePlatformID string, clean bool) ([]PackageSpec, map[string]string, error)
|
||||
}
|
||||
|
||||
type DNFError struct {
|
||||
|
|
@ -199,10 +199,11 @@ func NewRPMMD() RPMMD {
|
|||
return &rpmmdImpl{}
|
||||
}
|
||||
|
||||
func (*rpmmdImpl) FetchPackageList(repos []RepoConfig) (PackageList, map[string]string, error) {
|
||||
func (*rpmmdImpl) FetchPackageList(repos []RepoConfig, modulePlatformID string) (PackageList, map[string]string, error) {
|
||||
var arguments = struct {
|
||||
Repos []RepoConfig `json:"repos"`
|
||||
}{repos}
|
||||
Repos []RepoConfig `json:"repos"`
|
||||
ModulePlatformID string `json:"module_platform_id"`
|
||||
}{repos, modulePlatformID}
|
||||
var reply struct {
|
||||
Checksums map[string]string `json:"checksums"`
|
||||
Packages PackageList `json:"packages"`
|
||||
|
|
@ -214,13 +215,14 @@ func (*rpmmdImpl) FetchPackageList(repos []RepoConfig) (PackageList, map[string]
|
|||
return reply.Packages, reply.Checksums, err
|
||||
}
|
||||
|
||||
func (*rpmmdImpl) Depsolve(specs, excludeSpecs []string, repos []RepoConfig, clean bool) ([]PackageSpec, map[string]string, error) {
|
||||
func (*rpmmdImpl) Depsolve(specs, excludeSpecs []string, repos []RepoConfig, modulePlatformID string, clean bool) ([]PackageSpec, map[string]string, error) {
|
||||
var arguments = struct {
|
||||
PackageSpecs []string `json:"package-specs"`
|
||||
ExcludSpecs []string `json:"exclude-specs"`
|
||||
Repos []RepoConfig `json:"repos"`
|
||||
Clean bool `json:"clean,omitempty"`
|
||||
}{specs, excludeSpecs, repos, clean}
|
||||
PackageSpecs []string `json:"package-specs"`
|
||||
ExcludSpecs []string `json:"exclude-specs"`
|
||||
Repos []RepoConfig `json:"repos"`
|
||||
ModulePlatformID string `json:"module_platform_id"`
|
||||
Clean bool `json:"clean,omitempty"`
|
||||
}{specs, excludeSpecs, repos, modulePlatformID, clean}
|
||||
var reply struct {
|
||||
Checksums map[string]string `json:"checksums"`
|
||||
Dependencies []PackageSpec `json:"dependencies"`
|
||||
|
|
@ -281,7 +283,7 @@ func (packages PackageList) ToPackageInfos() []PackageInfo {
|
|||
return results
|
||||
}
|
||||
|
||||
func (pkg *PackageInfo) FillDependencies(rpmmd RPMMD, repos []RepoConfig) (err error) {
|
||||
pkg.Dependencies, _, err = rpmmd.Depsolve([]string{pkg.Name}, nil, repos, false)
|
||||
func (pkg *PackageInfo) FillDependencies(rpmmd RPMMD, repos []RepoConfig, modulePlatformID string) (err error) {
|
||||
pkg.Dependencies, _, err = rpmmd.Depsolve([]string{pkg.Name}, nil, repos, modulePlatformID, false)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -615,7 +615,7 @@ func (api *API) modulesInfoHandler(writer http.ResponseWriter, request *http.Req
|
|||
|
||||
if modulesRequested {
|
||||
for i, _ := range packageInfos {
|
||||
err := packageInfos[i].FillDependencies(api.rpmmd, api.distro.Repositories(api.arch))
|
||||
err := packageInfos[i].FillDependencies(api.rpmmd, api.distro.Repositories(api.arch), api.distro.ModulePlatformID())
|
||||
if err != nil {
|
||||
errors := responseError{
|
||||
ID: errorId,
|
||||
|
|
@ -645,7 +645,7 @@ func (api *API) projectsDepsolveHandler(writer http.ResponseWriter, request *htt
|
|||
|
||||
names := strings.Split(params.ByName("projects"), ",")
|
||||
|
||||
packages, _, err := api.rpmmd.Depsolve(names, nil, api.distro.Repositories(api.arch), false)
|
||||
packages, _, err := api.rpmmd.Depsolve(names, nil, api.distro.Repositories(api.arch), api.distro.ModulePlatformID(), false)
|
||||
|
||||
if err != nil {
|
||||
errors := responseError{
|
||||
|
|
@ -1792,7 +1792,7 @@ func (api *API) fetchPackageList() (rpmmd.PackageList, error) {
|
|||
repos = append(repos, source.RepoConfig())
|
||||
}
|
||||
|
||||
packages, _, err := api.rpmmd.FetchPackageList(repos)
|
||||
packages, _, err := api.rpmmd.FetchPackageList(repos, api.distro.ModulePlatformID())
|
||||
return packages, err
|
||||
}
|
||||
|
||||
|
|
@ -1817,7 +1817,7 @@ func (api *API) depsolveBlueprint(bp *blueprint.Blueprint, clean bool) ([]rpmmd.
|
|||
repos = append(repos, source.RepoConfig())
|
||||
}
|
||||
|
||||
return api.rpmmd.Depsolve(specs, nil, repos, clean)
|
||||
return api.rpmmd.Depsolve(specs, nil, repos, api.distro.ModulePlatformID(), clean)
|
||||
}
|
||||
|
||||
func (api *API) uploadsScheduleHandler(writer http.ResponseWriter, request *http.Request, params httprouter.Params) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue