internal/weldr: specify architecture of compose

This is useful in environments with multi-arch remote workers.
Defaults to the host architecture.
This commit is contained in:
Sanne Raymaekers 2023-10-10 12:53:18 +02:00
parent e24772dc57
commit c6aa7d88d2
10 changed files with 188 additions and 73 deletions

2
go.mod
View file

@ -14,7 +14,7 @@ require (
github.com/BurntSushi/toml v1.3.2
github.com/aws/aws-sdk-go v1.48.13
github.com/coreos/go-semver v0.3.1
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
github.com/deepmap/oapi-codegen v1.8.2
github.com/getkin/kin-openapi v0.93.0
github.com/gobwas/glob v0.2.3

3
go.sum
View file

@ -89,8 +89,9 @@ github.com/containers/storage v1.51.0/go.mod h1:ybl8a3j1PPtpyaEi/5A6TOFs+5TrEyOb
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=

View file

@ -22,6 +22,7 @@ type Blueprint struct {
Containers []Container `json:"containers,omitempty" toml:"containers,omitempty"`
Customizations *Customizations `json:"customizations,omitempty" toml:"customizations"`
Distro string `json:"distro" toml:"distro"`
Arch string `json:"architecture,omitempty" toml:"architecture,omitempty"`
}
type Change struct {

View file

@ -71,6 +71,10 @@ func executeTests(m *testing.M) int {
panic(err)
}
distro2 := test_distro.NewTestDistro("test-distro-2", "platform:test-2", "2")
_, err = fixture.Workers.RegisterWorker(arch.Name())
if err != nil {
panic(err)
}
rr := reporegistry.NewFromDistrosRepoConfigs(rpmmd.DistrosRepoConfigs{
test_distro.TestDistroName: {

View file

@ -79,6 +79,7 @@ func FixtureBase() *Store {
}}
s.blueprints[bName] = b
s.composes = map[uuid.UUID]Compose{
uuid.MustParse("30000000-0000-0000-0000-000000000000"): {
Blueprint: &b,
@ -309,6 +310,18 @@ func FixtureEmpty() *Store {
b3.Distro = "fedora-1"
s.blueprints[b3.Name] = b3
// Bad arch blueprint
b4 := b
b4.Name = "test-badarch"
b4.Arch = "badarch"
s.blueprints[b4.Name] = b4
// Cross arch blueprint
b5 := b
b5.Name = "test-crossarch"
b5.Arch = test_distro.TestArch2Name
s.blueprints[b5.Name] = b5
return s
}

View file

@ -52,7 +52,7 @@ type API struct {
workers *worker.Server
solver *dnfjson.BaseSolver
archName string
hostArch string
repoRegistry *reporegistry.RepoRegistry
logger *log.Logger
@ -63,7 +63,6 @@ type API struct {
hostDistroName string // Name of the host distro
distroRegistry *distroregistry.Registry // Available distros
distros []string // Supported distro names
// List of ImageType names, which should not be exposed by the API
distrosImageTypeDenylist map[string][]string
@ -97,7 +96,7 @@ func (cs ComposeState) ToString() string {
// systemRepoNames returns a list of the system repos
// NOTE: The system repos have no concept of id vs. name so the id is returned
func (api *API) systemRepoNames() (names []string) {
repos, err := api.repoRegistry.ReposByArchName(api.hostDistroName, api.archName, false)
repos, err := api.repoRegistry.ReposByArchName(api.hostDistroName, api.hostArch, false)
if err == nil {
for _, repo := range repos {
names = append(names, repo.Name)
@ -107,14 +106,16 @@ func (api *API) systemRepoNames() (names []string) {
}
// validDistros returns a list of distributions that also have repositories
func validDistros(rr *reporegistry.RepoRegistry, dr *distroregistry.Registry, arch string, logger *log.Logger) []string {
func (api *API) validDistros(arch string) []string {
distros := []string{}
for _, d := range dr.List() {
_, found := rr.DistroHasRepos(d, arch)
for _, d := range api.distroRegistry.List() {
_, found := api.repoRegistry.DistroHasRepos(d, arch)
if found {
distros = append(distros, d)
} else {
logger.Printf("Distro %s has no repositories, skipping.", d)
if api.logger != nil {
api.logger.Printf("Distro %s has no repositories, skipping.", d)
}
}
}
@ -136,13 +137,12 @@ func NewTestAPI(solver *dnfjson.BaseSolver, arch distro.Arch, dr *distroregistry
store: store,
workers: workers,
solver: solver,
archName: arch.Name(),
hostArch: arch.Name(),
repoRegistry: rr,
logger: logger,
compatOutputDir: compatOutputDir,
hostDistroName: hostDistro.Name(),
distroRegistry: dr,
distros: validDistros(rr, dr, arch.Name(), logger),
distrosImageTypeDenylist: distrosImageTypeDenylist,
}
return setupRouter(api)
@ -158,7 +158,7 @@ func New(repoPaths []string, stateDir string, solver *dnfjson.BaseSolver, dr *di
if err != nil {
return nil, fmt.Errorf("failed to read host distro information")
}
archName := common.CurrentArch()
hostArch := common.CurrentArch()
rr, err := reporegistry.New(repoPaths)
if err != nil {
@ -170,13 +170,13 @@ func New(repoPaths []string, stateDir string, solver *dnfjson.BaseSolver, dr *di
// get canonical distro name if the host distro is supported
hostDistroName = hostDistro.Name()
_, err = hostDistro.GetArch(archName)
_, err = hostDistro.GetArch(hostArch)
if err != nil {
return nil, fmt.Errorf("Host distro does not support host architecture: %v", err)
}
// Check if repositories for the host distro and arch were loaded
_, err = rr.ReposByArchName(hostDistroName, archName, false)
_, err = rr.ReposByArchName(hostDistroName, hostArch, false)
if err != nil {
log.Printf("loaded repository definitions don't contain any for the host distro/arch: %v", err)
}
@ -192,13 +192,12 @@ func New(repoPaths []string, stateDir string, solver *dnfjson.BaseSolver, dr *di
store: store,
workers: workers,
solver: solver,
archName: archName,
hostArch: hostArch,
repoRegistry: rr,
logger: logger,
compatOutputDir: compatOutputDir,
hostDistroName: hostDistroName,
distroRegistry: dr,
distros: validDistros(rr, dr, archName, logger),
distrosImageTypeDenylist: distrosImageTypeDenylist,
}
return setupRouter(api), nil
@ -308,22 +307,22 @@ func (api *API) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
// metadata.
func (api *API) PreloadMetadata() {
log.Printf("Starting metadata preload goroutines")
for _, distro := range api.distros {
for _, distro := range api.validDistros(api.hostArch) {
go func(distro string) {
startTime := time.Now()
d := api.getDistro(distro)
d := api.getDistro(distro, api.hostArch)
if d == nil {
log.Printf("GetDistro - unknown distribution: %s", distro)
return
}
repos, err := api.allRepositories(distro)
repos, err := api.allRepositories(distro, api.hostArch)
if err != nil {
log.Printf("Error getting repositories for distro %s: %s", distro, err)
return
}
solver := api.solver.NewWithConfig(d.ModulePlatformID(), d.Releasever(), api.archName, d.Name())
solver := api.solver.NewWithConfig(d.ModulePlatformID(), d.Releasever(), api.hostArch, d.Name())
_, err = solver.Depsolve([]rpmmd.PackageSet{{Include: []string{"filesystem"}, Repositories: repos}})
if err != nil {
log.Printf("Problem preloading distro metadata for %s: %s", distro, err)
@ -469,7 +468,7 @@ func (api *API) isImageTypeAllowed(distroName, imageType string) (bool, error) {
// getImageType returns the ImageType for the selected distro
// This is necessary because different distros support different image types, and the image
// type may have a different package set than other distros.
func (api *API) getImageType(distroName, imageType string) (distro.ImageType, error) {
func (api *API) getImageType(distroName, imageType, archName string) (distro.ImageType, error) {
imgAllowed, err := api.isImageTypeAllowed(distroName, imageType)
if err != nil {
return nil, fmt.Errorf("error while checking if image type is allowed: %v", err)
@ -478,20 +477,21 @@ func (api *API) getImageType(distroName, imageType string) (distro.ImageType, er
return nil, fmt.Errorf("image type %q for distro %q is denied by configuration", imageType, distroName)
}
distro := api.getDistro(distroName)
distro := api.getDistro(distroName, archName)
if distro == nil {
return nil, fmt.Errorf("GetDistro - unknown distribution: %s", distroName)
}
arch, err := distro.GetArch(api.archName)
arch, err := distro.GetArch(archName)
if err != nil {
return nil, err
}
return arch.GetImageType(imageType)
}
func (api *API) parseDistro(query url.Values) (string, error) {
func (api *API) parseDistro(query url.Values, arch string) (string, error) {
if distro := query.Get("distro"); distro != "" {
if common.IsStringInSortedSlice(api.distros, distro) {
if common.IsStringInSortedSlice(api.validDistros(arch), distro) {
return distro, nil
}
return "", errors_package.New("Invalid distro: " + distro)
@ -501,8 +501,8 @@ func (api *API) parseDistro(query url.Values) (string, error) {
// getDistro returns the named distro or nil
// It excludes unsupported distros by first checking the api.distros list
func (api *API) getDistro(name string) distro.Distro {
if !common.IsStringInSortedSlice(api.distros, name) {
func (api *API) getDistro(name, arch string) distro.Distro {
if !common.IsStringInSortedSlice(api.validDistros(arch), name) {
return nil
}
return api.distroRegistry.GetDistro(name)
@ -664,7 +664,7 @@ func (api *API) getSourceConfigs(params httprouter.Params) (map[string]store.Sou
sources := map[string]store.SourceConfig{}
errors := []responseError{}
repos, err := api.repoRegistry.ReposByArchName(api.hostDistroName, api.archName, false)
repos, err := api.repoRegistry.ReposByArchName(api.hostDistroName, api.hostArch, false)
if err != nil {
error := responseError{
ID: "InternalError",
@ -939,7 +939,7 @@ func (api *API) sourceNewHandler(writer http.ResponseWriter, request *http.Reque
// If there is a list of distros, check to make sure they are valid
invalid := []string{}
for _, d := range source.SourceConfig().Distros {
if !common.IsStringInSortedSlice(api.distros, d) {
if !common.IsStringInSortedSlice(api.validDistros(api.hostArch), d) {
invalid = append(invalid, d)
}
}
@ -1050,9 +1050,14 @@ func (api *API) modulesListHandler(writer http.ResponseWriter, request *http.Req
modulesParam := params.ByName("modules")
archName := api.hostArch
if queryArch := request.URL.Query().Get("arch"); queryArch != "" {
archName = queryArch
}
// Optional distro parameter
// If it is empty it will return api.hostDistroName
distroName, err := api.parseDistro(request.URL.Query())
distroName, err := api.parseDistro(request.URL.Query(), archName)
if err != nil {
errors := responseError{
ID: "DistroError",
@ -1071,7 +1076,7 @@ func (api *API) modulesListHandler(writer http.ResponseWriter, request *http.Req
names = strings.Split(modulesParam, ",")
}
packages, err := api.fetchPackageList(distroName, names)
packages, err := api.fetchPackageList(distroName, archName, names)
if err != nil {
errors := responseError{
@ -1133,9 +1138,14 @@ func (api *API) projectsListHandler(writer http.ResponseWriter, request *http.Re
return
}
archName := api.hostArch
if queryArch := request.URL.Query().Get("arch"); queryArch != "" {
archName = queryArch
}
// Optional distro parameter
// If it is empty it will return api.hostDistroName
distroName, err := api.parseDistro(request.URL.Query())
distroName, err := api.parseDistro(request.URL.Query(), archName)
if err != nil {
errors := responseError{
ID: "DistroError",
@ -1144,7 +1154,7 @@ func (api *API) projectsListHandler(writer http.ResponseWriter, request *http.Re
statusResponseError(writer, http.StatusBadRequest, errors)
return
}
availablePackages, err := api.fetchPackageList(distroName, []string{})
availablePackages, err := api.fetchPackageList(distroName, archName, []string{})
if err != nil {
errors := responseError{
@ -1216,9 +1226,14 @@ func (api *API) modulesInfoHandler(writer http.ResponseWriter, request *http.Req
names := strings.Split(modules, ",")
archName := api.hostArch
if queryArch := request.URL.Query().Get("arch"); queryArch != "" {
archName = queryArch
}
// Optional distro parameter
// If it is empty it will return api.hostDistroName
distroName, err := api.parseDistro(request.URL.Query())
distroName, err := api.parseDistro(request.URL.Query(), archName)
if err != nil {
errors := responseError{
ID: "DistroError",
@ -1227,7 +1242,7 @@ func (api *API) modulesInfoHandler(writer http.ResponseWriter, request *http.Req
statusResponseError(writer, http.StatusBadRequest, errors)
return
}
foundPackages, err := api.fetchPackageList(distroName, names)
foundPackages, err := api.fetchPackageList(distroName, archName, names)
if err != nil {
errors := responseError{
@ -1250,7 +1265,7 @@ func (api *API) modulesInfoHandler(writer http.ResponseWriter, request *http.Req
packageInfos := foundPackages.ToPackageInfos()
if modulesRequested {
repos, err := api.allRepositories(distroName)
repos, err := api.allRepositories(distroName, archName)
if err != nil {
errors := responseError{
ID: "InternalError",
@ -1259,7 +1274,7 @@ func (api *API) modulesInfoHandler(writer http.ResponseWriter, request *http.Req
statusResponseError(writer, http.StatusBadRequest, errors)
return
}
d := api.getDistro(distroName)
d := api.getDistro(distroName, archName)
if d == nil {
errors := responseError{
ID: "DistroError",
@ -1269,7 +1284,7 @@ func (api *API) modulesInfoHandler(writer http.ResponseWriter, request *http.Req
return
}
solver := api.solver.NewWithConfig(d.ModulePlatformID(), d.Releasever(), api.archName, d.Name())
solver := api.solver.NewWithConfig(d.ModulePlatformID(), d.Releasever(), archName, d.Name())
for i := range packageInfos {
pkgName := packageInfos[i].Name
solved, err := solver.Depsolve([]rpmmd.PackageSet{{Include: []string{pkgName}, Repositories: repos}})
@ -1321,9 +1336,14 @@ func (api *API) projectsDepsolveHandler(writer http.ResponseWriter, request *htt
projects = projects[1:]
names := strings.Split(projects, ",")
archName := api.hostArch
if queryArch := request.URL.Query().Get("arch"); queryArch != "" {
archName = queryArch
}
// Optional distro parameter
// If it is empty it will return api.hostDistroName
distroName, err := api.parseDistro(request.URL.Query())
distroName, err := api.parseDistro(request.URL.Query(), archName)
if err != nil {
errors := responseError{
ID: "DistroError",
@ -1333,7 +1353,7 @@ func (api *API) projectsDepsolveHandler(writer http.ResponseWriter, request *htt
return
}
d := api.getDistro(distroName)
d := api.getDistro(distroName, archName)
if d == nil {
errors := responseError{
ID: "DistroError",
@ -1343,7 +1363,7 @@ func (api *API) projectsDepsolveHandler(writer http.ResponseWriter, request *htt
return
}
repos, err := api.allRepositories(distroName)
repos, err := api.allRepositories(distroName, archName)
if err != nil {
errors := responseError{
ID: "ProjectsError",
@ -1353,7 +1373,7 @@ func (api *API) projectsDepsolveHandler(writer http.ResponseWriter, request *htt
return
}
solver := api.solver.NewWithConfig(d.ModulePlatformID(), d.Releasever(), api.archName, d.Name())
solver := api.solver.NewWithConfig(d.ModulePlatformID(), d.Releasever(), archName, d.Name())
deps, err := solver.Depsolve([]rpmmd.PackageSet{{Include: names, Repositories: repos}})
if err != nil {
errors := responseError{
@ -2034,10 +2054,14 @@ func (api *API) blueprintsNewHandler(writer http.ResponseWriter, request *http.R
// Check the blueprint's distro to make sure it is valid
if len(blueprint.Distro) > 0 {
if !common.IsStringInSortedSlice(api.distros, blueprint.Distro) {
arch := blueprint.Arch
if arch == "" {
arch = api.hostArch
}
if !common.IsStringInSortedSlice(api.validDistros(arch), blueprint.Distro) {
errors := responseError{
ID: "BlueprintsError",
Msg: fmt.Sprintf("'%s' is not a valid distribution", blueprint.Distro),
Msg: fmt.Sprintf("'%s' is not a valid distribution (architecture '%s')", blueprint.Distro, arch),
}
statusResponseError(writer, http.StatusBadRequest, errors)
return
@ -2243,12 +2267,13 @@ func (api *API) blueprintsTagHandler(writer http.ResponseWriter, request *http.R
}
// depsolve handles depsolving package sets required for serializing a manifest for a given distribution.
func (api *API) depsolve(packageSets map[string][]rpmmd.PackageSet, distro distro.Distro) (map[string][]rpmmd.PackageSpec, error) {
func (api *API) depsolve(packageSets map[string][]rpmmd.PackageSet, arch distro.Arch) (map[string][]rpmmd.PackageSpec, error) {
distro := arch.Distro()
platformID := distro.ModulePlatformID()
releasever := distro.Releasever()
distroName := distro.Name()
solver := api.solver.NewWithConfig(platformID, releasever, api.archName, distroName)
solver := api.solver.NewWithConfig(platformID, releasever, arch.Name(), distroName)
depsolvedSets := make(map[string][]rpmmd.PackageSpec, len(packageSets))
@ -2266,7 +2291,7 @@ func (api *API) depsolve(packageSets map[string][]rpmmd.PackageSet, distro distr
return depsolvedSets, nil
}
func (api *API) resolveContainers(sourceSpecs map[string][]container.SourceSpec) (map[string][]container.Spec, error) {
func (api *API) resolveContainers(sourceSpecs map[string][]container.SourceSpec, archName string) (map[string][]container.Spec, error) {
specs := make(map[string][]container.Spec, len(sourceSpecs))
@ -2280,7 +2305,7 @@ func (api *API) resolveContainers(sourceSpecs map[string][]container.SourceSpec)
// to multiple pipelines at any point, this should work.
for name, sources := range sourceSpecs {
job := worker.ContainerResolveJob{
Arch: api.archName,
Arch: archName,
Specs: make([]worker.ContainerSpec, len(sources)),
}
@ -2423,17 +2448,23 @@ func (api *API) composeHandler(writer http.ResponseWriter, request *http.Request
if distroName == "" {
distroName = api.hostDistroName
}
if api.getDistro(distroName) == nil {
archName := bp.Arch
if archName == "" {
archName = api.hostArch
}
if api.getDistro(distroName, archName) == nil {
errors := responseError{
ID: "DistroError",
Msg: fmt.Sprintf("Unknown distribution: %s", distroName),
Msg: fmt.Sprintf("Unknown distribution: %s for arch %s", distroName, archName),
}
statusResponseError(writer, http.StatusBadRequest, errors)
return
}
// Get the imageType that corresponds to the distribution selected by the blueprint
imageType, err := api.getImageType(distroName, cr.ComposeType)
imageType, err := api.getImageType(distroName, cr.ComposeType, archName)
if err != nil {
errors := responseError{
ID: "ComposeError",
@ -2526,7 +2557,7 @@ func (api *API) composeHandler(writer http.ResponseWriter, request *http.Request
return
}
packageSets, err := api.depsolve(manifest.GetPackageSetChains(), imageType.Arch().Distro())
packageSets, err := api.depsolve(manifest.GetPackageSetChains(), imageType.Arch())
if err != nil {
errors := responseError{
ID: "DepsolveError",
@ -2536,7 +2567,7 @@ func (api *API) composeHandler(writer http.ResponseWriter, request *http.Request
return
}
containerSpecs, err := api.resolveContainers(manifest.GetContainerSourceSpecs())
containerSpecs, err := api.resolveContainers(manifest.GetContainerSourceSpecs(), archName)
if err != nil {
errors := responseError{
ID: "ContainerResolveError",
@ -2577,6 +2608,25 @@ func (api *API) composeHandler(writer http.ResponseWriter, request *http.Request
}
}
workerAvailable, err := api.workers.WorkerAvailableForArch(archName)
if err != nil {
log.Println("error when pushing new compose: ", err.Error())
errors := responseError{
ID: "ComposePushErrored",
Msg: err.Error(),
}
statusResponseError(writer, http.StatusInternalServerError, errors)
return
}
if !workerAvailable {
errors := responseError{
ID: "ComposePushErrored",
Msg: fmt.Sprintf("No worker for arch '%s' available", archName),
}
statusResponseError(writer, http.StatusBadRequest, errors)
return
}
if testMode == "1" {
// Create a failed compose
err = api.store.PushTestCompose(composeID, mf, imageType, bp, size, targets, false, packages)
@ -2585,8 +2635,7 @@ func (api *API) composeHandler(writer http.ResponseWriter, request *http.Request
err = api.store.PushTestCompose(composeID, mf, imageType, bp, size, targets, true, packages)
} else {
var jobId uuid.UUID
jobId, err = api.workers.EnqueueOSBuild(api.archName, &worker.OSBuildJob{
jobId, err = api.workers.EnqueueOSBuild(archName, &worker.OSBuildJob{
Manifest: mf,
Targets: targets,
PipelineNames: &worker.PipelineNames{
@ -2767,9 +2816,15 @@ func (api *API) composeTypesHandler(writer http.ResponseWriter, request *http.Re
if !verifyRequestVersion(writer, params, 0) {
return
}
archName := api.hostArch
if queryArch := request.URL.Query().Get("arch"); queryArch != "" {
archName = queryArch
}
// Optional distro parameter
// If it is empty it will return api.hostDistroName
distroName, err := api.parseDistro(request.URL.Query())
distroName, err := api.parseDistro(request.URL.Query(), archName)
if err != nil {
errors := responseError{
ID: "DistroError",
@ -2779,7 +2834,7 @@ func (api *API) composeTypesHandler(writer http.ResponseWriter, request *http.Re
return
}
d := api.getDistro(distroName)
d := api.getDistro(distroName, archName)
if d == nil {
errors := responseError{
ID: "DistroError",
@ -2790,11 +2845,11 @@ func (api *API) composeTypesHandler(writer http.ResponseWriter, request *http.Re
}
// Get the distro specific arch so that we can return the correct list of image types
arch, err := d.GetArch(api.archName)
arch, err := d.GetArch(archName)
if err != nil {
errors := responseError{
ID: "DistroError",
Msg: fmt.Sprintf("Unknown arch: %s", api.archName),
Msg: fmt.Sprintf("Unknown arch: %s", arch),
}
statusResponseError(writer, http.StatusBadRequest, errors)
return
@ -3442,17 +3497,17 @@ func (api *API) composeFailedHandler(writer http.ResponseWriter, request *http.R
}
// fetchPackageList returns the package list or the selected distribution
func (api *API) fetchPackageList(distroName string, names []string) (packages rpmmd.PackageList, err error) {
d := api.getDistro(distroName)
func (api *API) fetchPackageList(distroName, arch string, names []string) (packages rpmmd.PackageList, err error) {
d := api.getDistro(distroName, arch)
if d == nil {
return nil, fmt.Errorf("GetDistro - unknown distribution: %s", distroName)
}
repos, err := api.allRepositories(distroName)
repos, err := api.allRepositories(distroName, arch)
if err != nil {
return nil, err
}
solver := api.solver.NewWithConfig(d.ModulePlatformID(), d.Releasever(), api.archName, d.Name())
solver := api.solver.NewWithConfig(d.ModulePlatformID(), d.Releasever(), arch, d.Name())
if len(names) == 0 {
packages, err = solver.FetchMetadata(repos)
} else {
@ -3503,8 +3558,8 @@ func (api *API) allRepositoriesByImageType(imageType distro.ImageType) ([]rpmmd.
}
// Returns all configured repositories (base + sources) as rpmmd.RepoConfig
func (api *API) allRepositories(distroName string) ([]rpmmd.RepoConfig, error) {
repos, err := api.repoRegistry.ReposByArchName(distroName, api.archName, false)
func (api *API) allRepositories(distroName, arch string) ([]rpmmd.RepoConfig, error) {
repos, err := api.repoRegistry.ReposByArchName(distroName, arch, false)
if err != nil {
return nil, err
}
@ -3521,16 +3576,21 @@ func (api *API) depsolveBlueprint(bp blueprint.Blueprint) ([]rpmmd.PackageSpec,
bp.Distro = api.hostDistroName
}
d := api.getDistro(bp.Distro)
arch := bp.Arch
if arch == "" {
arch = api.hostArch
}
d := api.getDistro(bp.Distro, arch)
if d == nil {
return nil, fmt.Errorf("GetDistro - unknown distribution: %s", bp.Distro)
}
repos, err := api.allRepositories(bp.Distro)
repos, err := api.allRepositories(bp.Distro, arch)
if err != nil {
return nil, err
}
solver := api.solver.NewWithConfig(d.ModulePlatformID(), d.Releasever(), api.archName, d.Name())
solver := api.solver.NewWithConfig(d.ModulePlatformID(), d.Releasever(), arch, d.Name())
solved, err := solver.Depsolve([]rpmmd.PackageSet{{Include: bp.GetPackages(), Repositories: repos}})
if err != nil {
return nil, err
@ -3629,10 +3689,15 @@ func (api *API) distrosListHandler(writer http.ResponseWriter, request *http.Req
return
}
archName := api.hostArch
if arch := request.URL.Query().Get("arch"); arch != "" {
archName = arch
}
var reply struct {
Distros []string `json:"distros"`
}
reply.Distros = api.distros
reply.Distros = api.validDistros(archName)
err := json.NewEncoder(writer).Encode(reply)
common.PanicOnError(err)

View file

@ -70,6 +70,9 @@ func createWeldrAPI(tempdir string, fixtureGenerator rpmmd_mock.FixtureGenerator
test_distro.TestArchName: {
{Name: "test-id", BaseURLs: []string{"http://example.com/test/os/x86_64"}, CheckGPG: common.ToPtr(true)},
},
test_distro.TestArch2Name: {
{Name: "test-id", BaseURLs: []string{"http://example.com/test/os/aarch64"}, CheckGPG: common.ToPtr(true)},
},
},
test_distro.TestDistro2Name: {
test_distro.TestArchName: {
@ -236,7 +239,7 @@ func TestBlueprintsNew(t *testing.T) {
{"POST", "/api/v0/blueprints/new", ``, http.StatusBadRequest, `{"status":false,"errors":[{"id":"BlueprintsError","msg":"Missing blueprint"}]}`},
{"POST", "/api/v0/blueprints/new", `{"name":"test","description":"Test","distro":"test-distro","packages":[],"version":""}`, http.StatusOK, `{"status":true}`},
{"POST", "/api/v0/blueprints/new", `{"name":"test2","description":"Test 2","distro":"test-distro-2","packages":[],"version":""}`, http.StatusOK, `{"status":true}`},
{"POST", "/api/v0/blueprints/new", `{"name":"test","description":"Test","distro":"fedora-1","packages":[],"version":""}`, http.StatusBadRequest, `{"status":false,"errors":[{"id":"BlueprintsError","msg":"'fedora-1' is not a valid distribution"}]}`},
{"POST", "/api/v0/blueprints/new", `{"name":"test","description":"Test","distro":"fedora-1","packages":[],"version":""}`, http.StatusBadRequest, `{"status":false,"errors":[{"id":"BlueprintsError","msg":"'fedora-1' is not a valid distribution (architecture 'test_arch')"}]}`},
}
tempdir := t.TempDir()
@ -1191,7 +1194,27 @@ func TestCompose(t *testing.T) {
"/api/v1/compose",
fmt.Sprintf(`{"blueprint_name": "test-fedora-1","compose_type": "%s","branch": "master"}`, test_distro.TestImageTypeName),
http.StatusBadRequest,
`{"status": false,"errors":[{"id":"DistroError", "msg":"Unknown distribution: fedora-1"}]}`,
`{"status": false,"errors":[{"id":"DistroError", "msg":"Unknown distribution: fedora-1 for arch test_arch"}]}`,
nil,
[]string{"build_id", "warnings"},
},
"bad-arch": {
false,
"POST",
"/api/v1/compose",
fmt.Sprintf(`{"blueprint_name": "test-badarch","compose_type": "%s","branch": "master"}`, test_distro.TestImageTypeName),
http.StatusBadRequest,
`{"status": false,"errors":[{"id":"DistroError", "msg":"Unknown distribution: test-distro for arch badarch"}]}`,
nil,
[]string{"build_id", "warnings"},
},
"cross-arch": {
false,
"POST",
"/api/v1/compose",
fmt.Sprintf(`{"blueprint_name": "test-crossarch","compose_type": "%s","branch": "master"}`, test_distro.TestImageTypeName),
http.StatusBadRequest,
`{"status": false,"errors":[{"id":"ComposePushErrored", "msg":"No worker for arch 'test_arch2' available"}]}`,
nil,
[]string{"build_id", "warnings"},
},
@ -1300,6 +1323,8 @@ func TestCompose(t *testing.T) {
tempdir := t.TempDir()
for name, c := range cases {
api, s := createWeldrAPI(tempdir, rpmmd_mock.NoComposesFixture)
_, err = api.workers.RegisterWorker(arch.Name())
require.NoError(t, err)
test.TestRoute(t, api, c.External, c.Method, c.Path, c.Body, c.ExpectedStatus, c.ExpectedJSON, c.IgnoreFields...)
if c.ExpectedStatus != http.StatusOK {
@ -2271,6 +2296,8 @@ func TestComposePOST_ImageTypeDenylist(t *testing.T) {
for _, c := range cases {
api, s := createWeldrAPI2(tempdir, rpmmd_mock.NoComposesFixture, c.imageTypeDenylist)
_, err = api.workers.RegisterWorker(arch.Name())
require.NoError(t, err)
test.TestRoute(t, api, true, "POST", c.Path, c.Body, c.ExpectedStatus, c.ExpectedJSON, c.IgnoreFields...)
if c.ExpectedStatus != http.StatusOK {

View file

@ -42,6 +42,8 @@ func TestComposeStatusFromLegacyError(t *testing.T) {
t.Fatalf("error serializing osbuild manifest: %v", err)
}
_, err = api.workers.RegisterWorker(arch.Name())
require.NoError(t, err)
jobId, err := api.workers.EnqueueOSBuild(arch.Name(), &worker.OSBuildJob{Manifest: mf}, "")
require.NoError(t, err)
@ -95,6 +97,8 @@ func TestComposeStatusFromJobError(t *testing.T) {
t.Fatalf("error serializing osbuild manifest: %v", err)
}
_, err = api.workers.RegisterWorker(arch.Name())
require.NoError(t, err)
jobId, err := api.workers.EnqueueOSBuild(arch.Name(), &worker.OSBuildJob{Manifest: mf}, "")
require.NoError(t, err)

View file

@ -146,7 +146,7 @@ EOF
# there is a different reponse if legacy composer-cli is used
if rpm -q --quiet weldr-client; then
EXPECTED_RESPONSE="ERROR: BlueprintsError: '$REMAINING_DISTRO' is not a valid distribution"
EXPECTED_RESPONSE="ERROR: BlueprintsError: '$REMAINING_DISTRO' is not a valid distribution (architecture '$(uname -m)')"
else
EXPECTED_RESPONSE="'$REMAINING_DISTRO' is not a valid distribution"
RESPONSE=${RESPONSE#*: }

2
vendor/modules.txt vendored
View file

@ -289,7 +289,7 @@ github.com/containers/storage/pkg/unshare
# github.com/coreos/go-semver v0.3.1
## explicit; go 1.8
github.com/coreos/go-semver/semver
# github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f
# github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
## explicit
github.com/coreos/go-systemd/activation
# github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46