worker/osbuild: move target errors to detail of job error

Add a new worker client error type `ErrorTargetError` representing that
at least one of job targets failed. The actual target errors are added
to the job detail.

Add a new `OSBuildJobResult.TargetErrors()` method for gathering a slice
of target errors contained within an `OSBuildJobResult` instance. Cover
the method with unit test.
This commit is contained in:
Tomas Hozza 2022-06-14 18:29:52 +02:00 committed by Tom Gundersen
parent 20cb2e1b2c
commit 6dcadc9d20
5 changed files with 112 additions and 3 deletions

View file

@ -839,9 +839,9 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
}
osbuildJobResult.TargetResults = append(osbuildJobResult.TargetResults, targetResult)
if targetResult.TargetError != nil {
// for now just duplicate the Target error into the Job result's error to keep backward compatibility
osbuildJobResult.JobError = targetResult.TargetError
targetErrors := osbuildJobResult.TargetErrors()
if len(targetErrors) != 0 {
osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorTargetError, "at least one target failed", targetErrors)
} else {
osbuildJobResult.Success = true
osbuildJobResult.UploadStatus = "success"

View file

@ -28,6 +28,7 @@ const (
ErrorDNFRepoError ClientErrorCode = 25
ErrorJobDependency ClientErrorCode = 26
ErrorJobMissingHeartbeat ClientErrorCode = 27
ErrorTargetError ClientErrorCode = 28
)
type ClientErrorCode int
@ -69,6 +70,8 @@ func GetStatusCode(err *Error) StatusCode {
return JobStatusUserInputError
case ErrorInvalidTarget:
return JobStatusUserInputError
case ErrorTargetError:
return JobStatusUserInputError
case ErrorDepsolveDependency:
return JobStatusUserInputError
case ErrorManifestDependency:

View file

@ -46,6 +46,25 @@ type OSBuildJobResult struct {
JobResult
}
// TargetErrors returns a slice of *clienterrors.Error gathered
// from the job result's target results. If there were no target errors
// then the returned slice will be empty.
func (j *OSBuildJobResult) TargetErrors() []*clienterrors.Error {
targetErrors := []*clienterrors.Error{}
for _, targetResult := range j.TargetResults {
if targetResult.TargetError != nil {
targetError := targetResult.TargetError
// Add the target name to the error details, because the error reason
// may not contain any information to determine the type of the target
// which failed.
targetErrors = append(targetErrors, clienterrors.WorkerClientError(targetError.ID, targetError.Reason, targetResult.Name))
}
}
return targetErrors
}
type KojiInitJob struct {
Server string `json:"server"`
Name string `json:"name"`

View file

@ -0,0 +1,85 @@
package worker
import (
"testing"
"github.com/osbuild/osbuild-composer/internal/target"
"github.com/osbuild/osbuild-composer/internal/worker/clienterrors"
"github.com/stretchr/testify/assert"
)
func TestOSBuildJobResultTargetErrors(t *testing.T) {
testCases := []struct {
jobResult OSBuildJobResult
targetErrors []*clienterrors.Error
}{
{
jobResult: OSBuildJobResult{
TargetResults: []*target.TargetResult{
{
Name: target.TargetNameAWS,
TargetError: clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, "can't login to AWS"),
},
{
Name: target.TargetNameVMWare,
TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "can't upload image to VMWare"),
},
{
Name: target.TargetNameAWSS3,
TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "failed to upload image to AWS S3"),
},
},
},
targetErrors: []*clienterrors.Error{
clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, "can't login to AWS", target.TargetNameAWS),
clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "can't upload image to VMWare", target.TargetNameVMWare),
clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "failed to upload image to AWS S3", target.TargetNameAWSS3),
},
},
{
jobResult: OSBuildJobResult{
TargetResults: []*target.TargetResult{
{
Name: target.TargetNameAWS,
TargetError: clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, "can't login to AWS"),
},
{
Name: target.TargetNameVMWare,
},
{
Name: target.TargetNameAWSS3,
TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "failed to upload image to AWS S3"),
},
},
},
targetErrors: []*clienterrors.Error{
clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, "can't login to AWS", target.TargetNameAWS),
clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "failed to upload image to AWS S3", target.TargetNameAWSS3),
},
},
{
jobResult: OSBuildJobResult{
TargetResults: []*target.TargetResult{
{
Name: target.TargetNameAWS,
},
{
Name: target.TargetNameVMWare,
},
{
Name: target.TargetNameAWSS3,
},
},
},
targetErrors: []*clienterrors.Error{},
},
{
jobResult: OSBuildJobResult{},
targetErrors: []*clienterrors.Error{},
},
}
for _, testCase := range testCases {
assert.EqualValues(t, testCase.targetErrors, testCase.jobResult.TargetErrors())
}
}

View file

@ -258,6 +258,8 @@ func (s *Server) OSBuildJobStatus(id uuid.UUID, result *OSBuildJobResult) (*JobS
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorBuildJob, "osbuild build failed")
} else if len(result.OSBuildOutput.Error) > 0 {
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorOldResultCompatible, string(result.OSBuildOutput.Error))
} else if len(result.TargetErrors()) > 0 {
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorTargetError, "at least one target failed", result.TargetErrors())
}
}
// For backwards compatibility: OSBuildJobResult didn't use to have a