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

Add cloudapi routes for listing and deleting composes #4154

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
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
8 changes: 8 additions & 0 deletions internal/cloudapi/v2/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const (
ErrorInvalidPartitioningMode ServiceErrorCode = 37
ErrorInvalidUploadTarget ServiceErrorCode = 38
ErrorBlueprintOrCustomNotBoth ServiceErrorCode = 39
ErrorComposeRunning ServiceErrorCode = 40

// Internal errors, these are bugs
ErrorFailedToInitializeBlueprint ServiceErrorCode = 1000
Expand All @@ -73,6 +74,9 @@ const (
ErrorGettingAWSEC2JobStatus ServiceErrorCode = 1018
ErrorGettingJobType ServiceErrorCode = 1019
ErrorTenantNotInContext ServiceErrorCode = 1020
ErrorGettingComposeList ServiceErrorCode = 1021
ErrorRemovingJob ServiceErrorCode = 1022
ErrorRemovingArtifacts ServiceErrorCode = 1023

// Errors contained within this file
ErrorUnspecified ServiceErrorCode = 10000
Expand Down Expand Up @@ -131,6 +135,7 @@ func getServiceErrors() serviceErrors {
serviceError{ErrorInvalidPartitioningMode, http.StatusBadRequest, "Requested partitioning mode is invalid"},
serviceError{ErrorInvalidUploadTarget, http.StatusBadRequest, "Invalid upload target for image type"},
serviceError{ErrorBlueprintOrCustomNotBoth, http.StatusBadRequest, "Invalid request, include blueprint or customizations, not both"},
serviceError{ErrorComposeRunning, http.StatusBadRequest, "Compose is running"},

serviceError{ErrorFailedToInitializeBlueprint, http.StatusInternalServerError, "Failed to initialize blueprint"},
serviceError{ErrorFailedToGenerateManifestSeed, http.StatusInternalServerError, "Failed to generate manifest seed"},
Expand All @@ -153,6 +158,9 @@ func getServiceErrors() serviceErrors {
serviceError{ErrorGettingAWSEC2JobStatus, http.StatusInternalServerError, "Unable to get ec2 job status"},
serviceError{ErrorGettingJobType, http.StatusInternalServerError, "Unable to get job type of existing job"},
serviceError{ErrorTenantNotInContext, http.StatusInternalServerError, "Unable to retrieve tenant from request context"},
serviceError{ErrorGettingComposeList, http.StatusInternalServerError, "Unable to get list of composes"},
serviceError{ErrorRemovingJob, http.StatusInternalServerError, "Unable to remove job"},
serviceError{ErrorRemovingArtifacts, http.StatusInternalServerError, "Unable to remove job artifacts"},

serviceError{ErrorUnspecified, http.StatusInternalServerError, "Unspecified internal error "},
serviceError{ErrorNotHTTPError, http.StatusInternalServerError, "Error is not an instance of HTTPError"},
Expand Down
90 changes: 76 additions & 14 deletions internal/cloudapi/v2/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,58 @@ func targetResultToUploadStatus(t *target.TargetResult) (*UploadStatus, error) {
return us, nil
}

// GetComposeList returns a list of the root job UUIDs
func (h *apiHandlers) GetComposeList(ctx echo.Context) error {
jobs, err := h.server.workers.AllRootJobIDs()
if err != nil {
return HTTPErrorWithInternal(ErrorGettingComposeList, err)
}

return ctx.JSON(http.StatusOK, jobs)
}

// DeleteCompose deletes a compose by UUID
func (h *apiHandlers) DeleteCompose(ctx echo.Context, id string) error {
return h.server.EnsureJobChannel(h.deleteComposeImpl)(ctx, id)
}

func (h *apiHandlers) deleteComposeImpl(ctx echo.Context, id string) error {
var jobId uuid.UUID
jobId, err := uuid.Parse(id)
if err != nil {
return HTTPError(ErrorInvalidComposeId)
}
_, err = h.server.workers.JobType(jobId)
if err != nil {
return HTTPError(ErrorComposeNotFound)
}

// Is it possible to remove this job? It has to be: success or failure
status, err := h.getJobIDComposeStatus(jobId)
if err != nil {
return err
}
if status.Status != ComposeStatusValueSuccess && status.Status != ComposeStatusValueFailure {
return HTTPError(ErrorComposeRunning)
}

err = h.server.workers.RemoveJob(jobId)
if err != nil {
return HTTPErrorWithInternal(ErrorRemovingJob, err)
}

err = h.server.workers.CleanupArtifacts()
if err != nil {
return HTTPErrorWithInternal(ErrorRemovingArtifacts, err)
}

return ctx.JSON(http.StatusOK, ComposeDeleteStatus{
Href: fmt.Sprintf("/api/image-builder-composer/v2/composes/delete/%v", jobId),
Id: jobId.String(),
Kind: "ComposeDeleteStatus",
})
}

func (h *apiHandlers) GetComposeStatus(ctx echo.Context, id string) error {
return h.server.EnsureJobChannel(h.getComposeStatusImpl)(ctx, id)
}
Expand All @@ -324,21 +376,31 @@ func (h *apiHandlers) getComposeStatusImpl(ctx echo.Context, id string) error {
return HTTPError(ErrorInvalidComposeId)
}

response, err := h.getJobIDComposeStatus(jobId)
if err != nil {
return err
}
return ctx.JSON(http.StatusOK, response)
}

// getJobIDComposeStatus returns the ComposeStatus for the job
// or an HTTPError
func (h *apiHandlers) getJobIDComposeStatus(jobId uuid.UUID) (ComposeStatus, error) {
jobType, err := h.server.workers.JobType(jobId)
if err != nil {
return HTTPError(ErrorComposeNotFound)
return ComposeStatus{}, HTTPError(ErrorComposeNotFound)
}

if jobType == worker.JobTypeOSBuild {
var result worker.OSBuildJobResult
jobInfo, err := h.server.workers.OSBuildJobInfo(jobId, &result)
if err != nil {
return HTTPError(ErrorMalformedOSBuildJobResult)
return ComposeStatus{}, HTTPError(ErrorMalformedOSBuildJobResult)
}

jobError, err := h.server.workers.JobDependencyChainErrors(jobId)
if err != nil {
return HTTPError(ErrorGettingBuildDependencyStatus)
return ComposeStatus{}, HTTPError(ErrorGettingBuildDependencyStatus)
}

var uploadStatuses *[]UploadStatus
Expand All @@ -349,7 +411,7 @@ func (h *apiHandlers) getComposeStatusImpl(ctx echo.Context, id string) error {
tr := result.TargetResults[idx]
us, err := targetResultToUploadStatus(tr)
if err != nil {
return HTTPError(ErrorUnknownUploadTarget)
return ComposeStatus{}, HTTPError(ErrorUnknownUploadTarget)
}
us.Status = uploadStatusFromJobStatus(jobInfo.JobStatus, result.JobError)
statuses[idx] = *us
Expand All @@ -363,7 +425,7 @@ func (h *apiHandlers) getComposeStatusImpl(ctx echo.Context, id string) error {
}
}

return ctx.JSON(http.StatusOK, ComposeStatus{
return ComposeStatus{
ObjectReference: ObjectReference{
Href: fmt.Sprintf("/api/image-builder-composer/v2/composes/%v", jobId),
Id: jobId.String(),
Expand All @@ -376,32 +438,32 @@ func (h *apiHandlers) getComposeStatusImpl(ctx echo.Context, id string) error {
UploadStatus: us0, // add the first upload status to the old top-level field
UploadStatuses: uploadStatuses,
},
})
}, nil
} else if jobType == worker.JobTypeKojiFinalize {
var result worker.KojiFinalizeJobResult
finalizeInfo, err := h.server.workers.KojiFinalizeJobInfo(jobId, &result)
if err != nil {
return HTTPError(ErrorMalformedOSBuildJobResult)
return ComposeStatus{}, HTTPError(ErrorMalformedOSBuildJobResult)
}
if len(finalizeInfo.Deps) < 2 {
return HTTPError(ErrorUnexpectedNumberOfImageBuilds)
return ComposeStatus{}, HTTPError(ErrorUnexpectedNumberOfImageBuilds)
}
var initResult worker.KojiInitJobResult
_, err = h.server.workers.KojiInitJobInfo(finalizeInfo.Deps[0], &initResult)
if err != nil {
return HTTPError(ErrorMalformedOSBuildJobResult)
return ComposeStatus{}, HTTPError(ErrorMalformedOSBuildJobResult)
}
var buildJobResults []worker.OSBuildJobResult
var buildJobStatuses []ImageStatus
for i := 1; i < len(finalizeInfo.Deps); i++ {
var buildJobResult worker.OSBuildJobResult
buildInfo, err := h.server.workers.OSBuildJobInfo(finalizeInfo.Deps[i], &buildJobResult)
if err != nil {
return HTTPError(ErrorMalformedOSBuildJobResult)
return ComposeStatus{}, HTTPError(ErrorMalformedOSBuildJobResult)
}
buildJobError, err := h.server.workers.JobDependencyChainErrors(finalizeInfo.Deps[i])
if err != nil {
return HTTPError(ErrorGettingBuildDependencyStatus)
return ComposeStatus{}, HTTPError(ErrorGettingBuildDependencyStatus)
}

var uploadStatuses *[]UploadStatus
Expand All @@ -414,7 +476,7 @@ func (h *apiHandlers) getComposeStatusImpl(ctx echo.Context, id string) error {
if tr.Name != target.TargetNameKoji {
us, err := targetResultToUploadStatus(tr)
if err != nil {
return HTTPError(ErrorUnknownUploadTarget)
return ComposeStatus{}, HTTPError(ErrorUnknownUploadTarget)
}
us.Status = uploadStatusFromJobStatus(buildInfo.JobStatus, result.JobError)
statuses = append(statuses, *us)
Expand Down Expand Up @@ -452,9 +514,9 @@ func (h *apiHandlers) getComposeStatusImpl(ctx echo.Context, id string) error {
if buildID != 0 {
response.KojiStatus.BuildId = &buildID
}
return ctx.JSON(http.StatusOK, response)
return response, nil
} else {
return HTTPError(ErrorInvalidJobType)
return ComposeStatus{}, HTTPError(ErrorInvalidJobType)
}
}

Expand Down
Loading
Loading