-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
osbuild: add org.osbuild.librepo source wrapper
This commit adds a wrapper for the org.osbuild.librepo source to download RPMs.
- Loading branch information
Showing
2 changed files
with
364 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package osbuild | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/osbuild/images/pkg/rpmmd" | ||
) | ||
|
||
// LibrepoSource wraps the org.osbuild.librepo osbuild source | ||
type LibrepoSource struct { | ||
Items map[string]*LibrepoSourceItem `json:"items"` | ||
Options *LibrepoSourceOptions `json:"options"` | ||
} | ||
|
||
func NewLibrepoSource() *LibrepoSource { | ||
return &LibrepoSource{ | ||
Items: make(map[string]*LibrepoSourceItem), | ||
Options: &LibrepoSourceOptions{ | ||
Mirrors: make(map[string]*LibrepoSourceMirror), | ||
}, | ||
} | ||
} | ||
|
||
// AddPackage adds the given *depsolved* pkg to the downloading. It | ||
// needs the *depsovled* repoConfig so that the repoID of the two can | ||
// be matched up | ||
func (source *LibrepoSource) AddPackage(pkg rpmmd.PackageSpec, repos []rpmmd.RepoConfig) error { | ||
pkgRepo, err := findRepoById(repos, pkg.RepoID) | ||
if err != nil { | ||
return fmt.Errorf("cannot find repo-id for pkg %v: %v", pkg.Name, err) | ||
} | ||
if _, ok := source.Options.Mirrors[pkgRepo.Id]; !ok { | ||
mirror, err := mirrorFromRepo(pkgRepo) | ||
if err != nil { | ||
return err | ||
} | ||
source.Options.Mirrors[pkgRepo.Id] = mirror | ||
} | ||
mirror := source.Options.Mirrors[pkgRepo.Id] | ||
if pkg.IgnoreSSL { | ||
mirror.Insecure = true | ||
} | ||
// this should never happen but we should still check to avoid | ||
// potential security issues | ||
if mirror.Insecure && !pkg.IgnoreSSL { | ||
return fmt.Errorf("inconsistent SSL configuration: package %v requires SSL but mirror %v is configured to ignore SSL", pkg.Name, mirror.URL) | ||
} | ||
if pkg.Secrets == "org.osbuild.rhsm" { | ||
mirror.Secrets = &URLSecrets{ | ||
Name: "org.osbuild.rhsm", | ||
} | ||
} else if pkg.Secrets == "org.osbuild.mtls" { | ||
mirror.Secrets = &URLSecrets{ | ||
Name: "org.osbuild.mtls", | ||
} | ||
} | ||
|
||
item := &LibrepoSourceItem{ | ||
Path: pkg.Path, | ||
MirrorID: pkgRepo.Id, | ||
} | ||
source.Items[pkg.Checksum] = item | ||
return nil | ||
} | ||
|
||
func (LibrepoSource) isSource() {} | ||
|
||
type LibrepoSourceItem struct { | ||
Path string `json:"path"` | ||
MirrorID string `json:"mirror"` | ||
} | ||
|
||
func findRepoById(repos []rpmmd.RepoConfig, repoID string) (*rpmmd.RepoConfig, error) { | ||
type info struct { | ||
ID string | ||
Name string | ||
} | ||
var repoInfo []info | ||
for _, repo := range repos { | ||
repoInfo = append(repoInfo, info{repo.Id, repo.Name}) | ||
if repo.Id == repoID { | ||
return &repo, nil | ||
} | ||
} | ||
|
||
return nil, fmt.Errorf("cannot find repo-id %v in %+v", repoID, repoInfo) | ||
} | ||
|
||
func mirrorFromRepo(repo *rpmmd.RepoConfig) (*LibrepoSourceMirror, error) { | ||
switch { | ||
case repo.Metalink != "": | ||
return &LibrepoSourceMirror{ | ||
URL: repo.Metalink, | ||
Type: "metalink", | ||
}, nil | ||
case repo.MirrorList != "": | ||
return &LibrepoSourceMirror{ | ||
URL: repo.MirrorList, | ||
Type: "mirrorlist", | ||
}, nil | ||
case len(repo.BaseURLs) > 0: | ||
return &LibrepoSourceMirror{ | ||
// XXX: should we pick a random one instead? | ||
URL: repo.BaseURLs[0], | ||
Type: "baseurl", | ||
}, nil | ||
} | ||
|
||
return nil, fmt.Errorf("cannot find metalink, mirrorlist or baseurl for %+v", repo) | ||
} | ||
|
||
// librepoSourceOptions are the JSON options for source org.osbuild.librepo | ||
type LibrepoSourceOptions struct { | ||
Mirrors map[string]*LibrepoSourceMirror `json:"mirrors"` | ||
} | ||
|
||
type LibrepoSourceMirror struct { | ||
URL string `json:"url"` | ||
Type string `json:"type"` | ||
|
||
Insecure bool `json:"insecure,omitempty"` | ||
Secrets *URLSecrets `json:"secrets,omitempty"` | ||
|
||
MaxParallels *int `json:"max-parallels,omitempty"` | ||
FastestMirror bool `json:"fastest-mirror,omitempty"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
package osbuild_test | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
|
||
"github.com/osbuild/images/internal/common" | ||
"github.com/osbuild/images/pkg/osbuild" | ||
"github.com/osbuild/images/pkg/rpmmd" | ||
) | ||
|
||
var ( | ||
opensslPkg = rpmmd.PackageSpec{ | ||
Name: "openssl-libs", | ||
Epoch: 1, | ||
Version: "3.0.1", | ||
Release: "5.el9", | ||
Arch: "x86_64", | ||
RemoteLocation: "https://example.com/repo/Packages/openssl-libs-3.0.1-5.el9.x86_64.rpm", | ||
Checksum: "sha256:fcf2515ec9115551c99d552da721803ecbca23b7ae5a974309975000e8bef666", | ||
Path: "Packages/openssl-libs-3.0.1-5.el9.x86_64.rpm", | ||
RepoID: "repo_id_metalink", | ||
} | ||
|
||
pamPkg = rpmmd.PackageSpec{ | ||
Name: "pam", | ||
Epoch: 0, | ||
Version: "1.5.1", | ||
Release: "9.el9", | ||
Arch: "x86_64", | ||
RemoteLocation: "https://example.com/repo/Packages/pam-1.5.1-9.el9.x86_64.rpm", | ||
Checksum: "sha256:e64caedce811645ecdd78e7b4ae83c189aa884ff1ba6445374f39186c588c52c", | ||
Path: "Packages/pam-1.5.1-9.el9.x86_64.rpm", | ||
RepoID: "repo_id_mirrorlist", | ||
} | ||
|
||
dbusPkg = rpmmd.PackageSpec{ | ||
Name: "dbus", | ||
Epoch: 1, | ||
Version: "1.12.20", | ||
Release: "5.el9", | ||
Arch: "x86_64", | ||
RemoteLocation: "https://example.com/repo/Packages/dbus-1.12.20-5.el9.x86_64.rpm", | ||
Checksum: "sha256:bb85bd28cc162e98da53b756b988ffd9350f4dbcc186f4c6962ae047e27f83d3", | ||
Path: "Packages/dbus-1.12.20-5.el9.x86_64.rpm", | ||
RepoID: "repo_id_baseurls", | ||
} | ||
) | ||
|
||
var fakeRepos = []rpmmd.RepoConfig{ | ||
{ | ||
Id: "repo_id_metalink", | ||
Name: "repo1", | ||
Metalink: "http://example.com/metalink", | ||
}, | ||
{ | ||
Id: "repo_id_mirrorlist", | ||
Name: "repo1", | ||
MirrorList: "http://example.com/mirrorlist", | ||
}, | ||
{ | ||
Id: "repo_id_baseurls", | ||
Name: "repo1", | ||
BaseURLs: []string{"http://example.com/baseurl1"}, | ||
}, | ||
} | ||
|
||
func TestLibrepoAddPackage(t *testing.T) { | ||
sources := osbuild.NewLibrepoSource() | ||
err := sources.AddPackage(opensslPkg, fakeRepos) | ||
assert.NoError(t, err) | ||
err = sources.AddPackage(pamPkg, fakeRepos) | ||
assert.NoError(t, err) | ||
err = sources.AddPackage(dbusPkg, fakeRepos) | ||
assert.NoError(t, err) | ||
|
||
expectedJSON := `{ | ||
"items": { | ||
"sha256:bb85bd28cc162e98da53b756b988ffd9350f4dbcc186f4c6962ae047e27f83d3": { | ||
"path": "Packages/dbus-1.12.20-5.el9.x86_64.rpm", | ||
"mirror": "repo_id_baseurls" | ||
}, | ||
"sha256:e64caedce811645ecdd78e7b4ae83c189aa884ff1ba6445374f39186c588c52c": { | ||
"path": "Packages/pam-1.5.1-9.el9.x86_64.rpm", | ||
"mirror": "repo_id_mirrorlist" | ||
}, | ||
"sha256:fcf2515ec9115551c99d552da721803ecbca23b7ae5a974309975000e8bef666": { | ||
"path": "Packages/openssl-libs-3.0.1-5.el9.x86_64.rpm", | ||
"mirror": "repo_id_metalink" | ||
} | ||
}, | ||
"options": { | ||
"mirrors": { | ||
"repo_id_baseurls": { | ||
"url": "http://example.com/baseurl1", | ||
"type": "baseurl" | ||
}, | ||
"repo_id_metalink": { | ||
"url": "http://example.com/metalink", | ||
"type": "metalink" | ||
}, | ||
"repo_id_mirrorlist": { | ||
"url": "http://example.com/mirrorlist", | ||
"type": "mirrorlist" | ||
} | ||
} | ||
} | ||
}` | ||
b, err := json.MarshalIndent(sources, "", " ") | ||
assert.NoError(t, err) | ||
assert.Equal(t, expectedJSON, string(b)) | ||
} | ||
|
||
func TestLibrepoInsecure(t *testing.T) { | ||
pkg := opensslPkg | ||
pkg.IgnoreSSL = true | ||
|
||
sources := osbuild.NewLibrepoSource() | ||
err := sources.AddPackage(pkg, fakeRepos) | ||
assert.NoError(t, err) | ||
|
||
expectedJSON := `{ | ||
"items": { | ||
"sha256:fcf2515ec9115551c99d552da721803ecbca23b7ae5a974309975000e8bef666": { | ||
"path": "Packages/openssl-libs-3.0.1-5.el9.x86_64.rpm", | ||
"mirror": "repo_id_metalink" | ||
} | ||
}, | ||
"options": { | ||
"mirrors": { | ||
"repo_id_metalink": { | ||
"url": "http://example.com/metalink", | ||
"type": "metalink", | ||
"insecure": true | ||
} | ||
} | ||
} | ||
}` | ||
b, err := json.MarshalIndent(sources, "", " ") | ||
assert.NoError(t, err) | ||
assert.Equal(t, expectedJSON, string(b)) | ||
} | ||
|
||
func TestLibrepoSecrets(t *testing.T) { | ||
for _, secret := range []string{"org.osbuild.rhsm", "org.osbuild.mtls"} { | ||
pkg := opensslPkg | ||
pkg.Secrets = secret | ||
|
||
sources := osbuild.NewLibrepoSource() | ||
err := sources.AddPackage(pkg, fakeRepos) | ||
assert.NoError(t, err) | ||
|
||
expectedJSON := fmt.Sprintf(`{ | ||
"items": { | ||
"sha256:fcf2515ec9115551c99d552da721803ecbca23b7ae5a974309975000e8bef666": { | ||
"path": "Packages/openssl-libs-3.0.1-5.el9.x86_64.rpm", | ||
"mirror": "repo_id_metalink" | ||
} | ||
}, | ||
"options": { | ||
"mirrors": { | ||
"repo_id_metalink": { | ||
"url": "http://example.com/metalink", | ||
"type": "metalink", | ||
"secrets": { | ||
"name": "%s" | ||
} | ||
} | ||
} | ||
} | ||
}`, secret) | ||
b, err := json.MarshalIndent(sources, "", " ") | ||
assert.NoError(t, err) | ||
assert.Equal(t, expectedJSON, string(b)) | ||
} | ||
} | ||
|
||
func TestLibrepoJsonMinimal(t *testing.T) { | ||
expectedJSON := `{ | ||
"url": "http://example.com", | ||
"type": "metalink" | ||
}` | ||
sourceMirror := osbuild.LibrepoSourceMirror{ | ||
URL: "http://example.com", | ||
Type: "metalink", | ||
} | ||
b, err := json.MarshalIndent(sourceMirror, "", " ") | ||
assert.NoError(t, err) | ||
assert.Equal(t, expectedJSON, string(b)) | ||
} | ||
|
||
func TestLibrepoJsonFull(t *testing.T) { | ||
expectedJSON := `{ | ||
"url": "http://example.com", | ||
"type": "metalink", | ||
"insecure": true, | ||
"secrets": { | ||
"name": "org.osbuild.mtls" | ||
}, | ||
"max-parallels": 10, | ||
"fastest-mirror": true | ||
}` | ||
sourceMirror := osbuild.LibrepoSourceMirror{ | ||
URL: "http://example.com", | ||
Type: "metalink", | ||
Insecure: true, | ||
Secrets: &osbuild.URLSecrets{Name: "org.osbuild.mtls"}, | ||
MaxParallels: common.ToPtr(10), | ||
FastestMirror: true, | ||
} | ||
b, err := json.MarshalIndent(sourceMirror, "", " ") | ||
assert.NoError(t, err) | ||
assert.Equal(t, expectedJSON, string(b)) | ||
} | ||
|
||
func TestLibrepoRepoIdNotFound(t *testing.T) { | ||
pkg := opensslPkg | ||
pkg.RepoID = "invalid_repo_id" | ||
|
||
sources := osbuild.NewLibrepoSource() | ||
err := sources.AddPackage(pkg, fakeRepos) | ||
assert.EqualError(t, err, `cannot find repo-id for pkg openssl-libs: cannot find repo-id invalid_repo_id in [{ID:repo_id_metalink Name:repo1} {ID:repo_id_mirrorlist Name:repo1} {ID:repo_id_baseurls Name:repo1}]`) | ||
} | ||
|
||
func TestLibrepoInconsistentSSLConfiguration(t *testing.T) { | ||
pkg := opensslPkg | ||
pkg.IgnoreSSL = true | ||
|
||
sources := osbuild.NewLibrepoSource() | ||
err := sources.AddPackage(pkg, fakeRepos) | ||
assert.NoError(t, err) | ||
pkg.IgnoreSSL = false | ||
err = sources.AddPackage(pkg, fakeRepos) | ||
assert.EqualError(t, err, `inconsistent SSL configuration: package openssl-libs requires SSL but mirror http://example.com/metalink is configured to ignore SSL`) | ||
} |