worker: remove osbuild-koji job

Koji API removed by the previous commit was the last user of osbuild-koji job.
Let's remove it since nothing uses it. This also removes all of the
compatibility code in Cloud API, see concerns below:

Compatibility concerns:
- the internal deployment was moved to a completely different composer
  instance, thus there are no old jobs
- Fedora deployment is still unused in prod, thus we don't care about keeping
  backward compatibility of the old jobs

Signed-off-by: Ondřej Budai <ondrej@budai.cz>
This commit is contained in:
Ondřej Budai 2022-07-12 11:57:10 +02:00 committed by Ondřej Budai
parent 74eb3860df
commit e779562f3c
9 changed files with 103 additions and 861 deletions

View file

@ -103,179 +103,92 @@ func (impl *KojiFinalizeJobImpl) Run(job worker.Job) error {
var buildRoots []koji.BuildRoot
var images []koji.Image
isOldKojiCompose, err := isOldKojiComposeReq(job)
var osbuildResults []worker.OSBuildJobResult
initArgs, osbuildResults, err = extractDynamicArgs(job)
if err != nil {
return err
}
build.BuildID = initArgs.BuildID
// TODO: remove eventually. Kept for backward compatibility.
if isOldKojiCompose {
var osbuildKojiResults []worker.OSBuildKojiJobResult
initArgs, osbuildKojiResults, err = extractDynamicArgsOld(job)
// Check the dependencies early. Fail the koji build if any of them failed.
if hasFailedDependency(*initArgs, osbuildResults) {
err = impl.kojiFail(args.Server, int(initArgs.BuildID), initArgs.Token)
// Update the status immediately and bail out.
var result worker.KojiFinalizeJobResult
if err != nil {
return err
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorKojiFailedDependency, err.Error())
}
build.BuildID = initArgs.BuildID
// Check the dependencies early. Fail the koji build if any of them failed.
if hasFailedDependencyOld(*initArgs, osbuildKojiResults) {
err = impl.kojiFail(args.Server, int(initArgs.BuildID), initArgs.Token)
// Update the status immediately and bail out.
var result worker.KojiFinalizeJobResult
if err != nil {
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorKojiFailedDependency, err.Error())
}
err = job.Update(&result)
if err != nil {
return fmt.Errorf("Error reporting job result: %v", err)
}
return nil
}
for i, buildArgs := range osbuildKojiResults {
buildRPMs := make([]rpmmd.RPM, 0)
// collect packages from stages in build pipelines
for _, plName := range buildArgs.PipelineNames.Build {
buildPipelineMd := buildArgs.OSBuildOutput.Metadata[plName]
buildRPMs = append(buildRPMs, osbuild.OSBuildMetadataToRPMs(buildPipelineMd)...)
}
// this dedupe is usually not necessary since we generally only have
// one rpm stage in one build pipeline, but it's not invalid to have
// multiple
buildRPMs = rpmmd.DeduplicateRPMs(buildRPMs)
buildRoots = append(buildRoots, koji.BuildRoot{
ID: uint64(i),
Host: koji.Host{
Os: buildArgs.HostOS,
Arch: buildArgs.Arch,
},
ContentGenerator: koji.ContentGenerator{
Name: "osbuild",
Version: "0", // TODO: put the correct version here
},
Container: koji.Container{
Type: "none",
Arch: buildArgs.Arch,
},
Tools: []koji.Tool{},
RPMs: buildRPMs,
})
// collect packages from stages in payload pipelines
imageRPMs := make([]rpmmd.RPM, 0)
for _, plName := range buildArgs.PipelineNames.Payload {
payloadPipelineMd := buildArgs.OSBuildOutput.Metadata[plName]
imageRPMs = append(imageRPMs, osbuild.OSBuildMetadataToRPMs(payloadPipelineMd)...)
}
// deduplicate
imageRPMs = rpmmd.DeduplicateRPMs(imageRPMs)
images = append(images, koji.Image{
BuildRootID: uint64(i),
Filename: args.KojiFilenames[i],
FileSize: buildArgs.ImageSize,
Arch: buildArgs.Arch,
ChecksumType: "md5",
MD5: buildArgs.ImageHash,
Type: "image",
RPMs: imageRPMs,
Extra: koji.ImageExtra{
Info: koji.ImageExtraInfo{
Arch: buildArgs.Arch,
},
},
})
}
} else {
var osbuildResults []worker.OSBuildJobResult
initArgs, osbuildResults, err = extractDynamicArgs(job)
err = job.Update(&result)
if err != nil {
return err
return fmt.Errorf("Error reporting job result: %v", err)
}
build.BuildID = initArgs.BuildID
return nil
}
// Check the dependencies early. Fail the koji build if any of them failed.
if hasFailedDependency(*initArgs, osbuildResults) {
err = impl.kojiFail(args.Server, int(initArgs.BuildID), initArgs.Token)
for i, buildArgs := range osbuildResults {
buildRPMs := make([]rpmmd.RPM, 0)
// collect packages from stages in build pipelines
for _, plName := range buildArgs.PipelineNames.Build {
buildPipelineMd := buildArgs.OSBuildOutput.Metadata[plName]
buildRPMs = append(buildRPMs, osbuild.OSBuildMetadataToRPMs(buildPipelineMd)...)
}
// this dedupe is usually not necessary since we generally only have
// one rpm stage in one build pipeline, but it's not invalid to have
// multiple
buildRPMs = rpmmd.DeduplicateRPMs(buildRPMs)
// Update the status immediately and bail out.
var result worker.KojiFinalizeJobResult
if err != nil {
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorKojiFailedDependency, err.Error())
}
err = job.Update(&result)
if err != nil {
return fmt.Errorf("Error reporting job result: %v", err)
}
return nil
// TODO: support multiple upload targets
if len(buildArgs.TargetResults) != 1 {
// TODO: should we call kojiFail() and update job status, instead of just returning?
return fmt.Errorf("error: Koji compose OSBuild job result doesn't contain exactly one target result")
}
kojiTarget := buildArgs.TargetResults[0]
kojiTargetOptions := kojiTarget.Options.(*target.KojiTargetResultOptions)
buildRoots = append(buildRoots, koji.BuildRoot{
ID: uint64(i),
Host: koji.Host{
Os: buildArgs.HostOS,
Arch: buildArgs.Arch,
},
ContentGenerator: koji.ContentGenerator{
Name: "osbuild",
Version: "0", // TODO: put the correct version here
},
Container: koji.Container{
Type: "none",
Arch: buildArgs.Arch,
},
Tools: []koji.Tool{},
RPMs: buildRPMs,
})
// collect packages from stages in payload pipelines
imageRPMs := make([]rpmmd.RPM, 0)
for _, plName := range buildArgs.PipelineNames.Payload {
payloadPipelineMd := buildArgs.OSBuildOutput.Metadata[plName]
imageRPMs = append(imageRPMs, osbuild.OSBuildMetadataToRPMs(payloadPipelineMd)...)
}
for i, buildArgs := range osbuildResults {
buildRPMs := make([]rpmmd.RPM, 0)
// collect packages from stages in build pipelines
for _, plName := range buildArgs.PipelineNames.Build {
buildPipelineMd := buildArgs.OSBuildOutput.Metadata[plName]
buildRPMs = append(buildRPMs, osbuild.OSBuildMetadataToRPMs(buildPipelineMd)...)
}
// this dedupe is usually not necessary since we generally only have
// one rpm stage in one build pipeline, but it's not invalid to have
// multiple
buildRPMs = rpmmd.DeduplicateRPMs(buildRPMs)
// deduplicate
imageRPMs = rpmmd.DeduplicateRPMs(imageRPMs)
// TODO: support multiple upload targets
if len(buildArgs.TargetResults) != 1 {
// TODO: should we call kojiFail() and update job status, instead of just returning?
return fmt.Errorf("error: Koji compose OSBuild job result doesn't contain exactly one target result")
}
kojiTarget := buildArgs.TargetResults[0]
kojiTargetOptions := kojiTarget.Options.(*target.KojiTargetResultOptions)
buildRoots = append(buildRoots, koji.BuildRoot{
ID: uint64(i),
Host: koji.Host{
Os: buildArgs.HostOS,
images = append(images, koji.Image{
BuildRootID: uint64(i),
Filename: args.KojiFilenames[i],
FileSize: kojiTargetOptions.ImageSize,
Arch: buildArgs.Arch,
ChecksumType: "md5",
MD5: kojiTargetOptions.ImageMD5,
Type: "image",
RPMs: imageRPMs,
Extra: koji.ImageExtra{
Info: koji.ImageExtraInfo{
Arch: buildArgs.Arch,
},
ContentGenerator: koji.ContentGenerator{
Name: "osbuild",
Version: "0", // TODO: put the correct version here
},
Container: koji.Container{
Type: "none",
Arch: buildArgs.Arch,
},
Tools: []koji.Tool{},
RPMs: buildRPMs,
})
// collect packages from stages in payload pipelines
imageRPMs := make([]rpmmd.RPM, 0)
for _, plName := range buildArgs.PipelineNames.Payload {
payloadPipelineMd := buildArgs.OSBuildOutput.Metadata[plName]
imageRPMs = append(imageRPMs, osbuild.OSBuildMetadataToRPMs(payloadPipelineMd)...)
}
// deduplicate
imageRPMs = rpmmd.DeduplicateRPMs(imageRPMs)
images = append(images, koji.Image{
BuildRootID: uint64(i),
Filename: args.KojiFilenames[i],
FileSize: kojiTargetOptions.ImageSize,
Arch: buildArgs.Arch,
ChecksumType: "md5",
MD5: kojiTargetOptions.ImageMD5,
Type: "image",
RPMs: imageRPMs,
Extra: koji.ImageExtra{
Info: koji.ImageExtraInfo{
Arch: buildArgs.Arch,
},
},
})
}
},
})
}
var result worker.KojiFinalizeJobResult
@ -292,27 +205,6 @@ func (impl *KojiFinalizeJobImpl) Run(job worker.Job) error {
return nil
}
// Extracts dynamic args of the koji-finalize job. Returns an error if they
// cannot be unmarshalled.
func extractDynamicArgsOld(job worker.Job) (*worker.KojiInitJobResult, []worker.OSBuildKojiJobResult, error) {
var kojiInitResult worker.KojiInitJobResult
err := job.DynamicArgs(0, &kojiInitResult)
if err != nil {
return nil, nil, err
}
osbuildKojiResults := make([]worker.OSBuildKojiJobResult, job.NDynamicArgs()-1)
for i := 1; i < job.NDynamicArgs(); i++ {
err = job.DynamicArgs(i, &osbuildKojiResults[i-1])
if err != nil {
return nil, nil, err
}
}
return &kojiInitResult, osbuildKojiResults, nil
}
// Extracts dynamic args of the koji-finalize job. Returns an error if they
// cannot be unmarshalled.
func extractDynamicArgs(job worker.Job) (*worker.KojiInitJobResult, []worker.OSBuildJobResult, error) {
@ -334,48 +226,6 @@ func extractDynamicArgs(job worker.Job) (*worker.KojiInitJobResult, []worker.OSB
return &kojiInitResult, osbuildResults, nil
}
// Tests the first osbuild job result for specific values and decides
// if it is a `worker.OSBuildKojiJobResult` or `worker.OSBuildJobResult`.
// In case of `worker.OSBuildKojiJobResult` returns `true`.
// In case of 'worker.OSBuildJobResult` returns `false`.
func isOldKojiComposeReq(job worker.Job) (bool, error) {
if job.NDynamicArgs() < 2 {
return false, fmt.Errorf("error: koji-finalize job does not have any osbuild job dynamic arg attached")
}
var osbuildKojiResult worker.OSBuildKojiJobResult
err := job.DynamicArgs(1, &osbuildKojiResult)
if err != nil {
return false, err
}
// The decision is based on the fact if all result values specific to
// `worker.OSBuildKojiJobResult` are set to meaningful values or not.
// The assumption is that the default type values are not meaningful.
if osbuildKojiResult.Arch == "" || osbuildKojiResult.HostOS == "" ||
osbuildKojiResult.ImageHash == "" || osbuildKojiResult.ImageSize == 0 {
return false, nil
}
return true, nil
}
// Returns true if any of koji-finalize dependencies failed.
func hasFailedDependencyOld(kojiInitResult worker.KojiInitJobResult, osbuildKojiResults []worker.OSBuildKojiJobResult) bool {
if kojiInitResult.JobError != nil {
return true
}
for _, r := range osbuildKojiResults {
// No `OSBuildOutput` implies failure: either osbuild crashed or
// rejected the input (manifest or command line arguments)
if r.OSBuildOutput == nil || !r.OSBuildOutput.Success || r.JobError != nil {
return true
}
}
return false
}
// Returns true if any of koji-finalize dependencies failed.
func hasFailedDependency(kojiInitResult worker.KojiInitJobResult, osbuildResults []worker.OSBuildJobResult) bool {
if kojiInitResult.JobError != nil {

View file

@ -1,173 +0,0 @@
package main
import (
"fmt"
"io/ioutil"
"net/url"
"os"
"path"
"github.com/sirupsen/logrus"
"github.com/osbuild/osbuild-composer/internal/common"
"github.com/osbuild/osbuild-composer/internal/osbuild"
"github.com/osbuild/osbuild-composer/internal/upload/koji"
"github.com/osbuild/osbuild-composer/internal/worker"
"github.com/osbuild/osbuild-composer/internal/worker/clienterrors"
)
type OSBuildKojiJobImpl struct {
Store string
Output string
KojiServers map[string]kojiServer
}
func (impl *OSBuildKojiJobImpl) kojiUpload(file *os.File, server, directory, filename string) (string, uint64, error) {
serverURL, err := url.Parse(server)
if err != nil {
return "", 0, err
}
kojiServer, exists := impl.KojiServers[serverURL.Hostname()]
transport := koji.CreateKojiTransport(kojiServer.relaxTimeoutFactor)
if !exists {
return "", 0, fmt.Errorf("Koji server has not been configured: %s", serverURL.Hostname())
}
k, err := koji.NewFromGSSAPI(server, &kojiServer.creds, transport)
if err != nil {
return "", 0, err
}
defer func() {
err := k.Logout()
if err != nil {
logrus.Warnf("koji logout failed: %v", err)
}
}()
return k.Upload(file, directory, filename)
}
func validateKojiResult(result *worker.OSBuildKojiJobResult, jobID string) {
logWithId := logrus.WithField("jobId", jobID)
if result.JobError != nil {
logWithId.Errorf("osbuild job failed: %s", result.JobError.Reason)
return
}
// if the job failed, but the JobError is
// nil, we still need to handle this as an error
if result.OSBuildOutput == nil || !result.OSBuildOutput.Success {
reason := "osbuild job was unsuccessful"
logWithId.Errorf("osbuild job failed: %s", reason)
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorBuildJob, reason)
} else {
logWithId.Infof("osbuild-koji job succeeded")
}
}
func (impl *OSBuildKojiJobImpl) Run(job worker.Job) error {
var result worker.OSBuildKojiJobResult
outputDirectory, err := ioutil.TempDir(impl.Output, job.Id().String()+"-*")
if err != nil {
return fmt.Errorf("error creating temporary output directory: %v", err)
}
defer func() {
validateKojiResult(&result, job.Id().String())
// this is necessary for early return errors
err = job.Update(&result)
if err != nil {
logrus.Warnf("Error reporting job result: %v", err)
}
err := os.RemoveAll(outputDirectory)
if err != nil {
logrus.Warnf("Error removing temporary output directory (%s): %v", outputDirectory, err)
}
}()
var args worker.OSBuildKojiJob
err = job.Args(&args)
if err != nil {
return err
}
var initArgs worker.KojiInitJobResult
err = job.DynamicArgs(0, &initArgs)
if err != nil {
return err
}
result.Arch = common.CurrentArch()
result.HostOS, err = common.GetRedHatRelease()
if err != nil {
return err
}
// In case the manifest is empty, try to get it from dynamic args
if len(args.Manifest) == 0 {
if job.NDynamicArgs() > 1 {
var manifestJR worker.ManifestJobByIDResult
err = job.DynamicArgs(1, &manifestJR)
if err != nil {
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorParsingDynamicArgs, "Error parsing dynamic args")
return err
}
// skip the job if the manifest generation failed
if manifestJR.JobError != nil {
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorManifestDependency, "Manifest dependency failed")
return nil
}
args.Manifest = manifestJR.Manifest
if len(args.Manifest) == 0 {
err := fmt.Errorf("Received empty manifest")
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorEmptyManifest, err.Error())
return err
}
} else {
err := fmt.Errorf("Job has no manifest")
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorEmptyManifest, err.Error())
return err
}
}
if initArgs.JobError == nil {
exports := args.Exports
if len(exports) == 0 {
// job did not define exports, likely coming from an older version of composer
// fall back to default "assembler"
exports = []string{"assembler"}
} else if len(exports) > 1 {
// this worker only supports returning one (1) export
err = fmt.Errorf("at most one build artifact can be exported")
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorBuildJob, err.Error())
return err
}
result.OSBuildOutput, err = osbuild.RunOSBuild(args.Manifest, impl.Store, outputDirectory, exports, nil, true, os.Stderr)
if err != nil {
return err
}
// NOTE: Currently OSBuild supports multiple exports, but this isn't used
// by any of the image types and it can't be specified during the request.
// Use the first (and presumably only) export for the imagePath.
exportPath := exports[0]
if result.OSBuildOutput.Success {
f, err := os.Open(path.Join(outputDirectory, exportPath, args.ImageName))
if err != nil {
return err
}
result.ImageHash, result.ImageSize, err = impl.kojiUpload(f, args.KojiServer, args.KojiDirectory, args.KojiFilename)
if err != nil {
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorKojiBuild, err.Error())
}
}
}
// copy pipeline info to the result
result.PipelineNames = args.PipelineNames
return nil
}

View file

@ -431,11 +431,6 @@ func main() {
SkipSSLVerification: genericS3SkipSSLVerification,
},
},
worker.JobTypeOSBuildKoji: &OSBuildKojiJobImpl{
Store: store,
Output: output,
KojiServers: kojiServers,
},
worker.JobTypeKojiInit: &KojiInitJobImpl{
KojiServers: kojiServers,
},