diff --git a/cmd/osbuild-worker/jobimpl-ostree-resolve.go b/cmd/osbuild-worker/jobimpl-ostree-resolve.go new file mode 100644 index 000000000..2ebb4ddc6 --- /dev/null +++ b/cmd/osbuild-worker/jobimpl-ostree-resolve.go @@ -0,0 +1,79 @@ +package main + +import ( + "fmt" + + "github.com/sirupsen/logrus" + + "github.com/osbuild/osbuild-composer/internal/ostree" + "github.com/osbuild/osbuild-composer/internal/worker" + "github.com/osbuild/osbuild-composer/internal/worker/clienterrors" +) + +type OSTreeResolveJobImpl struct { +} + +func setError(err error, result *worker.OSTreeResolveJobResult) { + switch err.(type) { + case ostree.RefError: + result.JobError = clienterrors.WorkerClientError( + clienterrors.ErrorOSTreeRefInvalid, + "Invalid OSTree ref", + err, + ) + case ostree.ResolveRefError: + result.JobError = clienterrors.WorkerClientError( + clienterrors.ErrorOSTreeRefResolution, + "Error resolving OSTree ref", + err, + ) + default: + result.JobError = clienterrors.WorkerClientError( + clienterrors.ErrorOSTreeParamsInvalid, + "Invalid OSTree parameters or parameter combination", + err, + ) + } +} + +func (impl *OSTreeResolveJobImpl) Run(job worker.Job) error { + logWithId := logrus.WithField("jobId", job.Id()) + var args worker.OSTreeResolveJob + err := job.Args(&args) + if err != nil { + return err + } + + result := worker.OSTreeResolveJobResult{ + Specs: make([]worker.OSTreeResolveResultSpec, len(args.Specs)), + } + + logWithId.Infof("Resolving (%d) ostree commits", len(args.Specs)) + + for _, s := range args.Specs { + reqParams := ostree.RequestParams{ + URL: s.URL, + Ref: s.Ref, + Parent: s.Parent, + } + + ref, checksum, err := ostree.ResolveParams(reqParams) + if err != nil { + setError(err, &result) + break + } + + result.Specs = append(result.Specs, worker.OSTreeResolveResultSpec{ + URL: s.URL, + Ref: ref, + Checksum: checksum, + }) + } + + err = job.Update(&result) + if err != nil { + return fmt.Errorf("Error reporting job result: %v", err) + } + + return nil +} diff --git a/cmd/osbuild-worker/main.go b/cmd/osbuild-worker/main.go index 03e321c97..6792130f6 100644 --- a/cmd/osbuild-worker/main.go +++ b/cmd/osbuild-worker/main.go @@ -465,6 +465,7 @@ func main() { worker.JobTypeContainerResolve: &ContainerResolveJobImpl{ AuthFilePath: containersAuthFilePath, }, + worker.JobTypeOSTreeResolve: &OSTreeResolveJobImpl{}, worker.JobTypeAWSEC2Copy: &AWSEC2CopyJobImpl{ AWSCreds: awsCredentials, }, diff --git a/internal/worker/clienterrors/errors.go b/internal/worker/clienterrors/errors.go index c7a56d100..1d35da15f 100644 --- a/internal/worker/clienterrors/errors.go +++ b/internal/worker/clienterrors/errors.go @@ -32,6 +32,10 @@ const ( ErrorParsingJobArgs ClientErrorCode = 29 ErrorContainerResolution ClientErrorCode = 30 ErrorContainerDependency ClientErrorCode = 31 + ErrorOSTreeRefInvalid ClientErrorCode = 32 + ErrorOSTreeRefResolution ClientErrorCode = 33 + ErrorOSTreeParamsInvalid ClientErrorCode = 34 + ErrorOSTreeDependency ClientErrorCode = 35 ) type ClientErrorCode int @@ -87,6 +91,8 @@ func GetStatusCode(err *Error) StatusCode { return JobStatusUserInputError case ErrorContainerResolution: return JobStatusUserInputError + case ErrorOSTreeDependency: + return JobStatusUserInputError default: return JobStatusInternalError } @@ -97,6 +103,8 @@ func (e *Error) IsDependencyError() bool { switch e.ID { case ErrorContainerDependency: return true + case ErrorOSTreeDependency: + return true case ErrorDepsolveDependency: return true case ErrorManifestDependency: diff --git a/internal/worker/json.go b/internal/worker/json.go index 7adc79447..439b85227 100644 --- a/internal/worker/json.go +++ b/internal/worker/json.go @@ -256,6 +256,28 @@ type ContainerResolveJobResult struct { JobResult } +type OSTreeResolveSpec struct { + URL string `json:"url"` + Ref string `json:"ref"` + Parent string `json:"parent"` +} + +type OSTreeResolveJob struct { + Specs []OSTreeResolveSpec `json:"ostree_resolve_specs"` +} + +type OSTreeResolveResultSpec struct { + URL string `json:"url"` + Ref string `json:"ref"` + Checksum string `json:"checksum"` +} + +type OSTreeResolveJobResult struct { + Specs []OSTreeResolveResultSpec `json:"ostree_resolve_result_specs"` + + JobResult +} + type AWSEC2ShareJob struct { Ami string `json:"ami"` Region string `json:"region"` diff --git a/internal/worker/server.go b/internal/worker/server.go index ff57ca8dd..6e170edb6 100644 --- a/internal/worker/server.go +++ b/internal/worker/server.go @@ -35,6 +35,7 @@ const ( JobTypeDepsolve string = "depsolve" JobTypeManifestIDOnly string = "manifest-id-only" JobTypeContainerResolve string = "container-resolve" + JobTypeOSTreeResolve string = "ostree-resolve" JobTypeAWSEC2Copy string = "aws-ec2-copy" JobTypeAWSEC2Share string = "aws-ec2-share" ) @@ -160,6 +161,10 @@ func (s *Server) EnqueueContainerResolveJob(job *ContainerResolveJob, channel st return s.enqueue(JobTypeContainerResolve, job, nil, channel) } +func (s *Server) EnqueueOSTreeResolveJob(job *OSTreeResolveJob, channel string) (uuid.UUID, error) { + return s.enqueue(JobTypeOSTreeResolve, job, nil, channel) +} + func (s *Server) EnqueueAWSEC2CopyJob(job *AWSEC2CopyJob, parent uuid.UUID, channel string) (uuid.UUID, error) { return s.enqueue(JobTypeAWSEC2Copy, job, []uuid.UUID{parent}, channel) } @@ -231,6 +236,13 @@ func (s *Server) JobDependencyChainErrors(id uuid.UUID) (*clienterrors.Error, er return nil, err } jobResult = &containerResolveJR.JobResult + case JobTypeOSTreeResolve: + var ostreeResolveJR OSTreeResolveJobResult + jobInfo, err = s.OSTreeResolveJobInfo(id, &ostreeResolveJR) + if err != nil { + return nil, err + } + jobResult = &ostreeResolveJR.JobResult default: return nil, fmt.Errorf("unexpected job type: %s", jobType) @@ -370,6 +382,19 @@ func (s *Server) ContainerResolveJobInfo(id uuid.UUID, result *ContainerResolveJ return jobInfo, nil } +func (s *Server) OSTreeResolveJobInfo(id uuid.UUID, result *OSTreeResolveJobResult) (*JobInfo, error) { + jobInfo, err := s.jobInfo(id, result) + if err != nil { + return nil, err + } + + if jobInfo.JobType != JobTypeOSTreeResolve { + return nil, fmt.Errorf("expected %q, found %q job instead", JobTypeOSTreeResolve, jobInfo.JobType) + } + + return jobInfo, nil +} + func (s *Server) AWSEC2CopyJobInfo(id uuid.UUID, result *AWSEC2CopyJobResult) (*JobInfo, error) { jobInfo, err := s.jobInfo(id, result) if err != nil { @@ -684,6 +709,13 @@ func (s *Server) FinishJob(token uuid.UUID, result json.RawMessage) error { return err } jobResult = &containerResolveJR.JobResult + case JobTypeOSTreeResolve: + var ostreeResolveJR OSTreeResolveJobResult + jobInfo, err = s.OSTreeResolveJobInfo(jobId, &ostreeResolveJR) + if err != nil { + return err + } + jobResult = &ostreeResolveJR.JobResult default: return fmt.Errorf("unexpected job type: %s", jobType) diff --git a/internal/worker/server_test.go b/internal/worker/server_test.go index b08416e37..a0c3dfea2 100644 --- a/internal/worker/server_test.go +++ b/internal/worker/server_test.go @@ -790,6 +790,16 @@ func enqueueAndFinishTestJobDependencies(s *worker.Server, deps []testJob) ([]uu return nil, err } + case *worker.OSTreeResolveJob: + job := dep.main.(*worker.OSTreeResolveJob) + if len(depUUIDs) != 0 { + return nil, fmt.Errorf("dependencies are not supported for OSTreeResolveJob, got: %d", len(depUUIDs)) + } + id, err = s.EnqueueOSTreeResolveJob(job, "") + if err != nil { + return nil, err + } + default: return nil, fmt.Errorf("unexpected job type") } @@ -1467,6 +1477,71 @@ func TestJobDependencyChainErrors(t *testing.T) { }, expectedError: nil, }, + // osbuild + manifest + depsolve + ostree resolve + // failed ostree resolve + { + job: testJob{ + main: &worker.OSBuildJob{}, + deps: []testJob{ + { + main: &worker.KojiInitJob{}, + result: &worker.KojiInitJobResult{}, + }, + { + main: &worker.ManifestJobByID{}, + deps: []testJob{ + { + main: &worker.OSTreeResolveJob{}, + result: &worker.OSTreeResolveJobResult{ + JobResult: worker.JobResult{ + JobError: &clienterrors.Error{ + ID: clienterrors.ErrorOSTreeRefResolution, + Reason: "remote ostree ref not found", + }, + }, + }, + }, + { + main: &worker.DepsolveJob{}, + result: &worker.DepsolveJobResult{}, + }, + }, + result: &worker.ManifestJobByIDResult{ + JobResult: worker.JobResult{ + JobError: &clienterrors.Error{ + ID: clienterrors.ErrorOSTreeDependency, + Reason: "ostree dependency job failed", + }, + }, + }, + }, + }, + result: &worker.OSBuildJobResult{ + JobResult: worker.JobResult{ + JobError: &clienterrors.Error{ + ID: clienterrors.ErrorManifestDependency, + Reason: "manifest dependency job failed", + }, + }, + }, + }, + expectedError: &clienterrors.Error{ + ID: clienterrors.ErrorManifestDependency, + Reason: "manifest dependency job failed", + Details: []*clienterrors.Error{ + { + ID: clienterrors.ErrorOSTreeDependency, + Reason: "ostree dependency job failed", + Details: []*clienterrors.Error{ + { + ID: clienterrors.ErrorOSTreeRefResolution, + Reason: "remote ostree ref not found", + }, + }, + }, + }, + }, + }, } for idx, c := range cases {