Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

osbuild: add org.osbuild.librepo source wrapper #1141

Merged
merged 1 commit into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions pkg/osbuild/librepo_source.go
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"`
}
238 changes: 238 additions & 0 deletions pkg/osbuild/librepo_source_test.go
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`)
}
Loading