diff --git a/internal/cloudapi/v2/compose.go b/internal/cloudapi/v2/compose.go index 2d71b2eb6..a0f16bddf 100644 --- a/internal/cloudapi/v2/compose.go +++ b/internal/cloudapi/v2/compose.go @@ -235,7 +235,7 @@ func (rbp *Blueprint) GetCustomizationsFromBlueprintRequest() (*blueprint.Custom } if rbpc.Openscap.PolicyId != nil { - oscap.PolicyID = *rbpc.Openscap.PolicyId + oscap.PolicyID = rbpc.Openscap.PolicyId.String() } if rbpc.Openscap.Datastream != nil { @@ -288,23 +288,27 @@ func (rbp *Blueprint) GetCustomizationsFromBlueprintRequest() (*blueprint.Custom dirCustomization.Mode = *d.Mode } if d.User != nil { - dirCustomization.User = *d.User - if uid, ok := dirCustomization.User.(float64); ok { - // check if uid can be converted to int64 - if uid != float64(int64(uid)) { - return nil, fmt.Errorf("invalid user %f: must be an integer", uid) + user0, err := d.User.AsDirectoryUser0() + if err == nil { + dirCustomization.User = user0 + } else { + user1, err := d.User.AsDirectoryUser1() + if err != nil { + return nil, fmt.Errorf("invalid user: %w", err) } - dirCustomization.User = int64(uid) + dirCustomization.User = user1 } } if d.Group != nil { - dirCustomization.Group = *d.Group - if gid, ok := dirCustomization.Group.(float64); ok { - // check if gid can be converted to int64 - if gid != float64(int64(gid)) { - return nil, fmt.Errorf("invalid group %f: must be an integer", gid) + group0, err := d.Group.AsDirectoryGroup0() + if err == nil { + dirCustomization.Group = group0 + } else { + group1, err := d.Group.AsDirectoryGroup1() + if err != nil { + return nil, fmt.Errorf("invalid group: %w", err) } - dirCustomization.Group = int64(gid) + dirCustomization.Group = group1 } } if d.EnsureParents != nil { @@ -335,23 +339,29 @@ func (rbp *Blueprint) GetCustomizationsFromBlueprintRequest() (*blueprint.Custom fileCustomization.Mode = *f.Mode } if f.User != nil { - fileCustomization.User = *f.User - if uid, ok := fileCustomization.User.(float64); ok { - // check if uid can be converted to int64 - if uid != float64(int64(uid)) { - return nil, fmt.Errorf("invalid user %f: must be an integer", uid) + user0, err := f.User.AsBlueprintFileUser0() + if err == nil { + fileCustomization.User = user0 + } else { + user1, err := f.User.AsBlueprintFileUser1() + if err != nil { + return nil, fmt.Errorf("invalid user: %w", err) } - fileCustomization.User = int64(uid) + fileCustomization.User = user1 } } if f.Group != nil { - fileCustomization.Group = *f.Group - if gid, ok := fileCustomization.Group.(float64); ok { - // check if gid can be converted to int64 - if gid != float64(int64(gid)) { - return nil, fmt.Errorf("invalid group %f: must be an integer", gid) + group0, err := f.Group.AsBlueprintFileGroup0() + if err == nil { + fileCustomization.Group = group0 + } else { + group1, err := f.Group.AsBlueprintFileGroup1() + if err != nil { + return nil, fmt.Errorf("invalid group: %w", err) + } + if group1 != 0 { + fileCustomization.Group = group1 } - fileCustomization.Group = int64(gid) } } fileCustomizations = append(fileCustomizations, fileCustomization) @@ -655,23 +665,27 @@ func (request *ComposeRequest) GetBlueprintFromCustomizations() (blueprint.Bluep dirCustomization.Mode = *d.Mode } if d.User != nil { - dirCustomization.User = *d.User - if uid, ok := dirCustomization.User.(float64); ok { - // check if uid can be converted to int64 - if uid != float64(int64(uid)) { - return bp, fmt.Errorf("invalid user %f: must be an integer", uid) + user0, err := d.User.AsDirectoryUser0() + if err == nil { + dirCustomization.User = user0 + } else { + user1, err := d.User.AsDirectoryUser1() + if err != nil { + return bp, fmt.Errorf("invalid user: %w", err) } - dirCustomization.User = int64(uid) + dirCustomization.User = user1 } } if d.Group != nil { - dirCustomization.Group = *d.Group - if gid, ok := dirCustomization.Group.(float64); ok { - // check if gid can be converted to int64 - if gid != float64(int64(gid)) { - return bp, fmt.Errorf("invalid group %f: must be an integer", gid) + group0, err := d.Group.AsDirectoryGroup0() + if err == nil { + dirCustomization.Group = group0 + } else { + group1, err := d.Group.AsDirectoryGroup1() + if err != nil { + return bp, fmt.Errorf("invalid group: %w", err) } - dirCustomization.Group = int64(gid) + dirCustomization.Group = group1 } } if d.EnsureParents != nil { @@ -702,23 +716,27 @@ func (request *ComposeRequest) GetBlueprintFromCustomizations() (blueprint.Bluep fileCustomization.Mode = *f.Mode } if f.User != nil { - fileCustomization.User = *f.User - if uid, ok := fileCustomization.User.(float64); ok { - // check if uid can be converted to int64 - if uid != float64(int64(uid)) { - return bp, fmt.Errorf("invalid user %f: must be an integer", uid) + user0, err := f.User.AsFileUser0() + if err == nil { + fileCustomization.User = user0 + } else { + user1, err := f.User.AsFileUser1() + if err != nil { + return bp, fmt.Errorf("invalid user: %w", err) } - fileCustomization.User = int64(uid) + fileCustomization.User = user1 } } if f.Group != nil { - fileCustomization.Group = *f.Group - if gid, ok := fileCustomization.Group.(float64); ok { - // check if gid can be converted to int64 - if gid != float64(int64(gid)) { - return bp, fmt.Errorf("invalid group %f: must be an integer", gid) + group0, err := f.Group.AsFileGroup0() + if err == nil { + fileCustomization.Group = group0 + } else { + group1, err := f.Group.AsFileGroup1() + if err != nil { + return bp, fmt.Errorf("invalid group: %w", err) } - fileCustomization.Group = int64(gid) + fileCustomization.Group = group1 } } fileCustomizations = append(fileCustomizations, fileCustomization) @@ -770,7 +788,7 @@ func (request *ComposeRequest) GetBlueprintFromCustomizations() (blueprint.Bluep } if request.Customizations.Openscap.PolicyId != nil { - openSCAPCustomization.PolicyID = *request.Customizations.Openscap.PolicyId + openSCAPCustomization.PolicyID = request.Customizations.Openscap.PolicyId.String() } if request.Customizations.Openscap.Tailoring != nil && request.Customizations.Openscap.JsonTailoring != nil { diff --git a/internal/cloudapi/v2/compose_test.go b/internal/cloudapi/v2/compose_test.go index 28f91829a..5f87806a6 100644 --- a/internal/cloudapi/v2/compose_test.go +++ b/internal/cloudapi/v2/compose_test.go @@ -201,8 +201,14 @@ func TestGetBlueprintFromCustomizations(t *testing.T) { assert.Equal(t, "0.0.0", bp.Version) assert.Nil(t, bp.Customizations) - // interface{} is a terrible idea. Work around it... - var rootStr interface{} = "root" + var dirUser Directory_User + require.NoError(t, dirUser.FromDirectoryUser0("root")) + var dirGroup Directory_Group + require.NoError(t, dirGroup.FromDirectoryGroup0("root")) + var fileUser File_User + require.NoError(t, fileUser.FromFileUser0("root")) + var fileGroup File_Group + require.NoError(t, fileGroup.FromFileGroup0("root")) // Construct the compose request with customizations cr = ComposeRequest{Customizations: &Customizations{ @@ -230,8 +236,8 @@ func TestGetBlueprintFromCustomizations(t *testing.T) { Directory{ Path: "/opt/extra", EnsureParents: common.ToPtr(true), - User: &rootStr, - Group: &rootStr, + User: &dirUser, + Group: &dirGroup, Mode: common.ToPtr("0755"), }, }, @@ -240,8 +246,8 @@ func TestGetBlueprintFromCustomizations(t *testing.T) { Path: "/etc/mad.conf", Data: common.ToPtr("Alfred E. Neuman was here.\n"), EnsureParents: common.ToPtr(true), - User: &rootStr, - Group: &rootStr, + User: &fileUser, + Group: &fileGroup, Mode: common.ToPtr("0644"), }, }, @@ -379,8 +385,14 @@ func TestGetBlueprintFromCompose(t *testing.T) { assert.Equal(t, "0.0.0", bp.Version) assert.Nil(t, bp.Customizations) - // interface{} is a terrible idea. Work around it... - var rootStr interface{} = "root" + var dirUser Directory_User + require.NoError(t, dirUser.FromDirectoryUser0("root")) + var dirGroup Directory_Group + require.NoError(t, dirGroup.FromDirectoryGroup0("root")) + var fileUser BlueprintFile_User + require.NoError(t, fileUser.FromBlueprintFileUser0("root")) + var fileGroup BlueprintFile_Group + require.NoError(t, fileGroup.FromBlueprintFileGroup0("root")) // Construct the compose request with a blueprint cr = ComposeRequest{Blueprint: &Blueprint{ @@ -411,8 +423,8 @@ func TestGetBlueprintFromCompose(t *testing.T) { Directory{ Path: "/opt/extra", EnsureParents: common.ToPtr(true), - User: &rootStr, - Group: &rootStr, + User: &dirUser, + Group: &dirGroup, Mode: common.ToPtr("0755"), }, }, @@ -420,8 +432,8 @@ func TestGetBlueprintFromCompose(t *testing.T) { { Path: "/etc/mad.conf", Data: common.ToPtr("Alfred E. Neuman was here.\n"), - User: &rootStr, - Group: &rootStr, + User: &fileUser, + Group: &fileGroup, Mode: common.ToPtr("0644"), }, }, @@ -800,13 +812,12 @@ func TestGetImageRequests_ImageTypeConversion(t *testing.T) { for _, tt := range tests { t.Run(string(tt.requestedImageType), func(t *testing.T) { for _, d := range tt.requestedDistros { - uo := UploadOptions(struct{}{}) request := &ComposeRequest{ Distribution: d, ImageRequest: &ImageRequest{ Architecture: "x86_64", ImageType: tt.requestedImageType, - UploadOptions: &uo, + UploadOptions: &UploadOptions{}, Repositories: []Repository{ Repository{ Baseurl: common.ToPtr("http://example.org/pub/linux/repo"), @@ -828,13 +839,12 @@ func TestGetImageRequests_ImageTypeConversion(t *testing.T) { } func TestGetImageRequests_NoRepositories(t *testing.T) { - uo := UploadOptions(struct{}{}) request := &ComposeRequest{ Distribution: TEST_DISTRO_NAME, ImageRequest: &ImageRequest{ Architecture: "x86_64", ImageType: ImageTypesAws, - UploadOptions: &uo, + UploadOptions: &UploadOptions{}, Repositories: []Repository{}, }, } @@ -849,13 +859,12 @@ func TestGetImageRequests_NoRepositories(t *testing.T) { // TestGetImageRequests_BlueprintDistro test to make sure blueprint distro overrides request distro func TestGetImageRequests_BlueprintDistro(t *testing.T) { - uo := UploadOptions(struct{}{}) request := &ComposeRequest{ Distribution: TEST_DISTRO_NAME, ImageRequest: &ImageRequest{ Architecture: "x86_64", ImageType: ImageTypesAws, - UploadOptions: &uo, + UploadOptions: &UploadOptions{}, Repositories: []Repository{}, }, Blueprint: &Blueprint{ diff --git a/internal/cloudapi/v2/errors.go b/internal/cloudapi/v2/errors.go index b5aed0f14..216c6d2e7 100644 --- a/internal/cloudapi/v2/errors.go +++ b/internal/cloudapi/v2/errors.go @@ -52,6 +52,7 @@ const ( ErrorBlueprintOrCustomNotBoth ServiceErrorCode = 39 ErrorMismatchedDistribution ServiceErrorCode = 40 ErrorMismatchedArchitecture ServiceErrorCode = 41 + ErrorBadRequest ServiceErrorCode = 42 // Internal errors, these are bugs ErrorFailedToInitializeBlueprint ServiceErrorCode = 1000 @@ -136,8 +137,9 @@ 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{ErrorMismatchedDistribution, http.StatusBadRequest, "Invalid request, Blueprint and Cloud API request Distribution must match."}, - serviceError{ErrorMismatchedArchitecture, http.StatusBadRequest, "Invalid request, Blueprint and Cloud API request Architecture must match."}, + serviceError{ErrorMismatchedDistribution, http.StatusBadRequest, "Invalid request, Blueprint and Cloud API request Distribution must match"}, + serviceError{ErrorMismatchedArchitecture, http.StatusBadRequest, "Invalid request, Blueprint and Cloud API request Architecture must match"}, + serviceError{ErrorBadRequest, http.StatusBadRequest, "Invalid request, see details for more information"}, serviceError{ErrorFailedToInitializeBlueprint, http.StatusInternalServerError, "Failed to initialize blueprint"}, serviceError{ErrorFailedToGenerateManifestSeed, http.StatusInternalServerError, "Failed to generate manifest seed"}, @@ -221,11 +223,9 @@ func APIError(serviceError *serviceError, c echo.Context, details interface{}) * } apiErr := &Error{ - ObjectReference: ObjectReference{ - Href: fmt.Sprintf("%s/%d", ErrorHREF, serviceError.code), - Id: fmt.Sprintf("%d", serviceError.code), - Kind: "Error", - }, + Href: fmt.Sprintf("%s/%d", ErrorHREF, serviceError.code), + Id: fmt.Sprintf("%d", serviceError.code), + Kind: "Error", Code: fmt.Sprintf("%s%d", ErrorCodePrefix, serviceError.code), OperationId: operationID, // set operation id from context Reason: serviceError.reason, @@ -239,12 +239,10 @@ func APIError(serviceError *serviceError, c echo.Context, details interface{}) * // Helper to make the ErrorList as defined in openapi.v2.yml func APIErrorList(page int, pageSize int, c echo.Context) *ErrorList { list := &ErrorList{ - List: List{ - Kind: "ErrorList", - Page: page, - Size: 0, - Total: len(getServiceErrors()), - }, + Kind: "ErrorList", + Page: page, + Size: 0, + Total: len(getServiceErrors()), Items: []Error{}, } @@ -273,6 +271,8 @@ func apiErrorFromEchoError(echoError *echo.HTTPError) ServiceErrorCode { switch echoError.Code { case http.StatusNotFound: return ErrorResourceNotFound + case http.StatusBadRequest: + return ErrorBadRequest case http.StatusMethodNotAllowed: return ErrorMethodNotAllowed case http.StatusNotAcceptable: @@ -322,7 +322,7 @@ func HTTPErrorHandler(echoError error, c echo.Context) { err, ok := he.Message.(detailsError) if !ok { - // No service code was set, so Echo threw this error + // No service code was set, so Echo or the generated code threw this error doResponse(he.Error(), apiErrorFromEchoError(he), c, he.Internal) return } diff --git a/internal/cloudapi/v2/handler.go b/internal/cloudapi/v2/handler.go index c9b0e27dc..ad73a65fe 100644 --- a/internal/cloudapi/v2/handler.go +++ b/internal/cloudapi/v2/handler.go @@ -159,12 +159,9 @@ func (h *apiHandlers) PostCompose(ctx echo.Context) error { } return ctx.JSON(http.StatusCreated, &ComposeId{ - ObjectReference: ObjectReference{ - Href: "/api/image-builder-composer/v2/compose", - Id: id.String(), - Kind: "ComposeId", - }, - Id: id.String(), + Href: "/api/image-builder-composer/v2/compose", + Id: id, + Kind: "ComposeId", }) } @@ -229,56 +226,56 @@ func imageTypeFromApiImageType(it ImageTypes, arch distro.Arch) string { } func (h *apiHandlers) targetResultToUploadStatus(jobId uuid.UUID, t *target.TargetResult) (*UploadStatus, error) { - var us *UploadStatus var uploadType UploadTypes - var uploadOptions interface{} + var fromErr error + var uploadOptions UploadStatus_Options switch t.Name { case target.TargetNameAWS: uploadType = UploadTypesAws awsOptions := t.Options.(*target.AWSTargetResultOptions) - uploadOptions = AWSEC2UploadStatus{ + fromErr = uploadOptions.FromAWSEC2UploadStatus(AWSEC2UploadStatus{ Ami: awsOptions.Ami, Region: awsOptions.Region, - } + }) case target.TargetNameAWSS3: uploadType = UploadTypesAwsS3 awsOptions := t.Options.(*target.AWSS3TargetResultOptions) - uploadOptions = AWSS3UploadStatus{ + fromErr = uploadOptions.FromAWSS3UploadStatus(AWSS3UploadStatus{ Url: awsOptions.URL, - } + }) case target.TargetNameGCP: uploadType = UploadTypesGcp gcpOptions := t.Options.(*target.GCPTargetResultOptions) - uploadOptions = GCPUploadStatus{ + fromErr = uploadOptions.FromGCPUploadStatus(GCPUploadStatus{ ImageName: gcpOptions.ImageName, ProjectId: gcpOptions.ProjectID, - } + }) case target.TargetNameAzureImage: uploadType = UploadTypesAzure gcpOptions := t.Options.(*target.AzureImageTargetResultOptions) - uploadOptions = AzureUploadStatus{ + fromErr = uploadOptions.FromAzureUploadStatus(AzureUploadStatus{ ImageName: gcpOptions.ImageName, - } + }) case target.TargetNameContainer: uploadType = UploadTypesContainer containerOptions := t.Options.(*target.ContainerTargetResultOptions) - uploadOptions = ContainerUploadStatus{ + fromErr = uploadOptions.FromContainerUploadStatus(ContainerUploadStatus{ Url: containerOptions.URL, Digest: containerOptions.Digest, - } + }) case target.TargetNameOCIObjectStorage: uploadType = UploadTypesOciObjectstorage ociOptions := t.Options.(*target.OCIObjectStorageTargetResultOptions) - uploadOptions = OCIUploadStatus{ + fromErr = uploadOptions.FromOCIUploadStatus(OCIUploadStatus{ Url: ociOptions.URL, - } + }) case target.TargetNamePulpOSTree: uploadType = UploadTypesPulpOstree pulpOSTreeOptions := t.Options.(*target.PulpOSTreeTargetResultOptions) - uploadOptions = PulpOSTreeUploadStatus{ + fromErr = uploadOptions.FromPulpOSTreeUploadStatus(PulpOSTreeUploadStatus{ RepoUrl: pulpOSTreeOptions.RepoURL, - } + }) case target.TargetNameWorkerServer: uploadType = UploadTypesLocal workerServerOptions := t.Options.(*target.WorkerServerTargetResultOptions) @@ -286,14 +283,18 @@ func (h *apiHandlers) targetResultToUploadStatus(jobId uuid.UUID, t *target.Targ if err != nil { return nil, fmt.Errorf("unable to find job artifact: %w", err) } - uploadOptions = LocalUploadStatus{ + fromErr = uploadOptions.FromLocalUploadStatus(LocalUploadStatus{ ArtifactPath: absPath, - } + }) default: return nil, fmt.Errorf("unknown upload target: %s", t.Name) } - us = &UploadStatus{ + if fromErr != nil { + return nil, fmt.Errorf("unable to form upload status: %w", fromErr) + } + + us := &UploadStatus{ // TODO: determine upload status based on the target results, not job results // Don't set the status here for now, but let it be set by the caller. //Status: UploadStatusValue(result.UploadStatus), @@ -328,16 +329,11 @@ func (h *apiHandlers) GetComposeList(ctx echo.Context) error { return ctx.JSON(http.StatusOK, stats) } -func (h *apiHandlers) GetComposeStatus(ctx echo.Context, id string) error { - return h.server.EnsureJobChannel(h.getComposeStatusImpl)(ctx, id) +func (h *apiHandlers) GetComposeStatus(ctx echo.Context, jobId uuid.UUID) error { + return h.server.EnsureJobChannel(h.getComposeStatusImpl)(ctx, jobId) } -func (h *apiHandlers) getComposeStatusImpl(ctx echo.Context, id string) error { - jobId, err := uuid.Parse(id) - if err != nil { - return HTTPError(ErrorInvalidComposeId) - } - +func (h *apiHandlers) getComposeStatusImpl(ctx echo.Context, jobId uuid.UUID) error { response, err := h.getJobIDComposeStatus(jobId) if err != nil { return err @@ -388,11 +384,9 @@ func (h *apiHandlers) getJobIDComposeStatus(jobId uuid.UUID) (ComposeStatus, err } return ComposeStatus{ - ObjectReference: ObjectReference{ - Href: fmt.Sprintf("/api/image-builder-composer/v2/composes/%v", jobId), - Id: jobId.String(), - Kind: "ComposeStatus", - }, + Href: fmt.Sprintf("/api/image-builder-composer/v2/composes/%v", jobId), + Id: jobId.String(), + Kind: "ComposeStatus", Status: composeStatusFromOSBuildJobStatus(jobInfo.JobStatus, &result), ImageStatus: ImageStatus{ Status: imageStatusFromOSBuildJobStatus(jobInfo.JobStatus, &result), @@ -462,11 +456,9 @@ func (h *apiHandlers) getJobIDComposeStatus(jobId uuid.UUID) (ComposeStatus, err }) } response := ComposeStatus{ - ObjectReference: ObjectReference{ - Href: fmt.Sprintf("/api/image-builder-composer/v2/composes/%v", jobId), - Id: jobId.String(), - Kind: "ComposeStatus", - }, + Href: fmt.Sprintf("/api/image-builder-composer/v2/composes/%v", jobId), + Id: jobId.String(), + Kind: "ComposeStatus", Status: composeStatusFromKojiJobStatus(finalizeInfo.JobStatus, &initResult, buildJobResults, &result), ImageStatus: buildJobStatuses[0], // backwards compatibility ImageStatuses: &buildJobStatuses, @@ -599,16 +591,11 @@ func composeStatusFromKojiJobStatus(js *worker.JobStatus, initResult *worker.Koj } // ComposeMetadata handles a /composes/{id}/metadata GET request -func (h *apiHandlers) GetComposeMetadata(ctx echo.Context, id string) error { - return h.server.EnsureJobChannel(h.getComposeMetadataImpl)(ctx, id) +func (h *apiHandlers) GetComposeMetadata(ctx echo.Context, jobId uuid.UUID) error { + return h.server.EnsureJobChannel(h.getComposeMetadataImpl)(ctx, jobId) } -func (h *apiHandlers) getComposeMetadataImpl(ctx echo.Context, id string) error { - jobId, err := uuid.Parse(id) - if err != nil { - return HTTPError(ErrorInvalidComposeId) - } - +func (h *apiHandlers) getComposeMetadataImpl(ctx echo.Context, jobId uuid.UUID) error { jobType, err := h.server.workers.JobType(jobId) if err != nil { return HTTPError(ErrorComposeNotFound) @@ -639,11 +626,9 @@ func (h *apiHandlers) getComposeMetadataImpl(ctx echo.Context, id string) error if buildInfo.JobStatus.Finished.IsZero() { // job still running: empty response return ctx.JSON(200, ComposeMetadata{ - ObjectReference: ObjectReference{ - Href: fmt.Sprintf("/api/image-builder-composer/v2/composes/%v/metadata", jobId), - Id: jobId.String(), - Kind: "ComposeMetadata", - }, + Href: fmt.Sprintf("/api/image-builder-composer/v2/composes/%v/metadata", jobId), + Id: jobId.String(), + Kind: "ComposeMetadata", Request: request, }) } @@ -651,11 +636,9 @@ func (h *apiHandlers) getComposeMetadataImpl(ctx echo.Context, id string) error if buildInfo.JobStatus.Canceled || !result.Success { // job canceled or failed, empty response return ctx.JSON(200, ComposeMetadata{ - ObjectReference: ObjectReference{ - Href: fmt.Sprintf("/api/image-builder-composer/v2/composes/%v/metadata", jobId), - Id: jobId.String(), - Kind: "ComposeMetadata", - }, + Href: fmt.Sprintf("/api/image-builder-composer/v2/composes/%v/metadata", jobId), + Id: jobId.String(), + Kind: "ComposeMetadata", Request: request, }) } @@ -685,11 +668,9 @@ func (h *apiHandlers) getComposeMetadataImpl(ctx echo.Context, id string) error packages := stagesToPackageMetadata(rpmStagesMd) resp := &ComposeMetadata{ - ObjectReference: ObjectReference{ - Href: fmt.Sprintf("/api/image-builder-composer/v2/composes/%v/metadata", jobId), - Id: jobId.String(), - Kind: "ComposeMetadata", - }, + Href: fmt.Sprintf("/api/image-builder-composer/v2/composes/%v/metadata", jobId), + Id: jobId.String(), + Kind: "ComposeMetadata", Packages: &packages, Request: request, } @@ -707,16 +688,14 @@ func stagesToPackageMetadata(stages []osbuild.RPMStageMetadata) []PackageMetadat for _, rpm := range md.Packages { packages = append(packages, PackageMetadata{ - PackageMetadataCommon: PackageMetadataCommon{ - Type: "rpm", - Name: rpm.Name, - Version: rpm.Version, - Release: rpm.Release, - Epoch: rpm.Epoch, - Arch: rpm.Arch, - Signature: osbuild.RPMPackageMetadataToSignature(rpm), - }, - Sigmd5: rpm.SigMD5, + Type: "rpm", + Name: rpm.Name, + Version: rpm.Version, + Release: rpm.Release, + Epoch: rpm.Epoch, + Arch: rpm.Arch, + Signature: osbuild.RPMPackageMetadataToSignature(rpm), + Sigmd5: rpm.SigMD5, }, ) } @@ -725,17 +704,11 @@ func stagesToPackageMetadata(stages []osbuild.RPMStageMetadata) []PackageMetadat } // Get logs for a compose -func (h *apiHandlers) GetComposeLogs(ctx echo.Context, id string) error { - return h.server.EnsureJobChannel(h.getComposeLogsImpl)(ctx, id) +func (h *apiHandlers) GetComposeLogs(ctx echo.Context, jobId uuid.UUID) error { + return h.server.EnsureJobChannel(h.getComposeLogsImpl)(ctx, jobId) } -func (h *apiHandlers) getComposeLogsImpl(ctx echo.Context, id string) error { - - jobId, err := uuid.Parse(id) - if err != nil { - return HTTPError(ErrorInvalidComposeId) - } - +func (h *apiHandlers) getComposeLogsImpl(ctx echo.Context, jobId uuid.UUID) error { jobType, err := h.server.workers.JobType(jobId) if err != nil { return HTTPError(ErrorComposeNotFound) @@ -744,11 +717,9 @@ func (h *apiHandlers) getComposeLogsImpl(ctx echo.Context, id string) error { var buildResultBlobs []interface{} resp := &ComposeLogs{ - ObjectReference: ObjectReference{ - Href: fmt.Sprintf("/api/image-builder-composer/v2/composes/%v/logs", jobId), - Id: jobId.String(), - Kind: "ComposeLogs", - }, + Href: fmt.Sprintf("/api/image-builder-composer/v2/composes/%v/logs", jobId), + Id: jobId.String(), + Kind: "ComposeLogs", } switch jobType { @@ -831,16 +802,11 @@ func manifestJobResultsFromJobDeps(w *worker.Server, deps []uuid.UUID) (*worker. } // GetComposeIdManifests returns the Manifests for a given Compose (one for each image). -func (h *apiHandlers) GetComposeManifests(ctx echo.Context, id string) error { - return h.server.EnsureJobChannel(h.getComposeManifestsImpl)(ctx, id) +func (h *apiHandlers) GetComposeManifests(ctx echo.Context, jobId uuid.UUID) error { + return h.server.EnsureJobChannel(h.getComposeManifestsImpl)(ctx, jobId) } -func (h *apiHandlers) getComposeManifestsImpl(ctx echo.Context, id string) error { - jobId, err := uuid.Parse(id) - if err != nil { - return HTTPError(ErrorInvalidComposeId) - } - +func (h *apiHandlers) getComposeManifestsImpl(ctx echo.Context, jobId uuid.UUID) error { jobType, err := h.server.workers.JobType(jobId) if err != nil { return HTTPError(ErrorComposeNotFound) @@ -921,11 +887,9 @@ func (h *apiHandlers) getComposeManifestsImpl(ctx echo.Context, id string) error } resp := &ComposeManifests{ - ObjectReference: ObjectReference{ - Href: fmt.Sprintf("/api/image-builder-composer/v2/composes/%v/manifests", jobId), - Id: jobId.String(), - Kind: "ComposeManifests", - }, + Href: fmt.Sprintf("/api/image-builder-composer/v2/composes/%v/manifests", jobId), + Id: jobId.String(), + Kind: "ComposeManifests", Manifests: manifestBlobs, } @@ -942,10 +906,10 @@ func sbomsFromOSBuildJob(w *worker.Server, osbuildJobUUID uuid.UUID) ([]ImageSBO pipelineNameToPurpose := func(pipelineName string) (ImageSBOMPipelinePurpose, error) { if slices.Contains(osbuildJobResult.PipelineNames.Payload, pipelineName) { - return ImageSBOMPipelinePurposeImage, nil + return ImageSBOMPipelinePurpose(Image), nil } if slices.Contains(osbuildJobResult.PipelineNames.Build, pipelineName) { - return ImageSBOMPipelinePurposeBuildroot, nil + return ImageSBOMPipelinePurpose(Buildroot), nil } return "", fmt.Errorf("Pipeline %q is not listed as either a payload or build pipeline", pipelineName) } @@ -988,7 +952,7 @@ func sbomsFromOSBuildJob(w *worker.Server, osbuildJobUUID uuid.UUID) ([]ImageSBO var sbomType ImageSBOMSbomType switch sbomDoc.DocType { case sbom.StandardTypeSpdx: - sbomType = ImageSBOMSbomTypeSpdx + sbomType = ImageSBOMSbomType(Spdx) default: return nil, fmt.Errorf("Unknown SBOM type %q attached to depsolve job %q", sbomDoc.DocType, manifestDepUUID) } @@ -1020,16 +984,11 @@ func sbomsFromOSBuildJob(w *worker.Server, osbuildJobUUID uuid.UUID) ([]ImageSBO } // GetComposeSBOMs returns the SBOM documents for a given Compose (multiple SBOMs for each image). -func (h *apiHandlers) GetComposeSBOMs(ctx echo.Context, id string) error { - return h.server.EnsureJobChannel(h.getComposeSBOMsImpl)(ctx, id) +func (h *apiHandlers) GetComposeSBOMs(ctx echo.Context, jobId uuid.UUID) error { + return h.server.EnsureJobChannel(h.getComposeSBOMsImpl)(ctx, jobId) } -func (h *apiHandlers) getComposeSBOMsImpl(ctx echo.Context, id string) error { - jobId, err := uuid.Parse(id) - if err != nil { - return HTTPError(ErrorInvalidComposeId) - } - +func (h *apiHandlers) getComposeSBOMsImpl(ctx echo.Context, jobId uuid.UUID) error { jobType, err := h.server.workers.JobType(jobId) if err != nil { return HTTPError(ErrorComposeNotFound) @@ -1084,11 +1043,9 @@ func (h *apiHandlers) getComposeSBOMsImpl(ctx echo.Context, id string) error { } resp := &ComposeSBOMs{ - ObjectReference: ObjectReference{ - Href: fmt.Sprintf("/api/image-builder-composer/v2/composes/%v/sboms", jobId), - Id: jobId.String(), - Kind: "ComposeSBOMs", - }, + Href: fmt.Sprintf("/api/image-builder-composer/v2/composes/%v/sboms", jobId), + Id: jobId.String(), + Kind: "ComposeSBOMs", Items: items, } @@ -1172,21 +1129,16 @@ func genRepoConfig(repo Repository) (*rpmmd.RepoConfig, error) { return repoConfig, nil } -func (h *apiHandlers) PostCloneCompose(ctx echo.Context, id string) error { - return h.server.EnsureJobChannel(h.postCloneComposeImpl)(ctx, id) +func (h *apiHandlers) PostCloneCompose(ctx echo.Context, jobId uuid.UUID) error { + return h.server.EnsureJobChannel(h.postCloneComposeImpl)(ctx, jobId) } -func (h *apiHandlers) postCloneComposeImpl(ctx echo.Context, id string) error { +func (h *apiHandlers) postCloneComposeImpl(ctx echo.Context, jobId uuid.UUID) error { channel, err := h.server.getTenantChannel(ctx) if err != nil { return HTTPErrorWithInternal(ErrorTenantNotFound, err) } - jobId, err := uuid.Parse(id) - if err != nil { - return HTTPError(ErrorInvalidComposeId) - } - jobType, err := h.server.workers.JobType(jobId) if err != nil { return HTTPError(ErrorComposeNotFound) @@ -1233,9 +1185,12 @@ func (h *apiHandlers) postCloneComposeImpl(ctx echo.Context, id string) error { finalJob := jobId // look at the upload status of the osbuild dependency to decide what to do if us.Type == UploadTypesAws { - options := us.Options.(AWSEC2UploadStatus) + options, err := us.Options.AsAWSEC2UploadStatus() + if err != nil { + return err + } var img AWSEC2CloneCompose - err := ctx.Bind(&img) + err = ctx.Bind(&img) if err != nil { return err } @@ -1310,31 +1265,25 @@ func (h *apiHandlers) postCloneComposeImpl(ctx echo.Context, id string) error { } return ctx.JSON(http.StatusCreated, CloneComposeResponse{ - ObjectReference: ObjectReference{ - Href: fmt.Sprintf("/api/image-builder-composer/v2/composes/%v/clone", jobId), - Id: finalJob.String(), - Kind: "CloneComposeId", - }, - Id: finalJob.String(), + Href: fmt.Sprintf("/api/image-builder-composer/v2/composes/%v/clone", jobId), + Id: finalJob, + Kind: "CloneComposeId", }) } -func (h *apiHandlers) GetCloneStatus(ctx echo.Context, id string) error { - return h.server.EnsureJobChannel(h.getCloneStatus)(ctx, id) +func (h *apiHandlers) GetCloneStatus(ctx echo.Context, jobId uuid.UUID) error { + return h.server.EnsureJobChannel(h.getCloneStatus)(ctx, jobId) } -func (h *apiHandlers) getCloneStatus(ctx echo.Context, id string) error { - jobId, err := uuid.Parse(id) - if err != nil { - return HTTPError(ErrorInvalidComposeId) - } - +func (h *apiHandlers) getCloneStatus(ctx echo.Context, jobId uuid.UUID) error { jobType, err := h.server.workers.JobType(jobId) if err != nil { return HTTPError(ErrorComposeNotFound) } - var us UploadStatus + var status UploadStatusValue + var uploadType UploadTypes + var options CloneStatus_Options switch jobType { case worker.JobTypeAWSEC2Copy: var result worker.AWSEC2CopyJobResult @@ -1343,13 +1292,14 @@ func (h *apiHandlers) getCloneStatus(ctx echo.Context, id string) error { return HTTPError(ErrorGettingAWSEC2JobStatus) } - us = UploadStatus{ - Status: uploadStatusFromJobStatus(info.JobStatus, result.JobError), - Type: UploadTypesAws, - Options: AWSEC2UploadStatus{ - Ami: result.Ami, - Region: result.Region, - }, + status = uploadStatusFromJobStatus(info.JobStatus, result.JobError) + uploadType = UploadTypesAws + err = options.FromAWSEC2UploadStatus(AWSEC2UploadStatus{ + Ami: result.Ami, + Region: result.Region, + }) + if err != nil { + return HTTPErrorWithInternal(ErrorGettingAWSEC2JobStatus, err) } case worker.JobTypeAWSEC2Share: var result worker.AWSEC2ShareJobResult @@ -1358,42 +1308,43 @@ func (h *apiHandlers) getCloneStatus(ctx echo.Context, id string) error { return HTTPError(ErrorGettingAWSEC2JobStatus) } - us = UploadStatus{ - Status: uploadStatusFromJobStatus(info.JobStatus, result.JobError), - Type: UploadTypesAws, - Options: AWSEC2UploadStatus{ - Ami: result.Ami, - Region: result.Region, - }, + status = uploadStatusFromJobStatus(info.JobStatus, result.JobError) + uploadType = UploadTypesAws + err = options.FromAWSEC2UploadStatus(AWSEC2UploadStatus{ + Ami: result.Ami, + Region: result.Region, + }) + if err != nil { + return HTTPErrorWithInternal(ErrorGettingAWSEC2JobStatus, err) } default: return HTTPError(ErrorInvalidJobType) } return ctx.JSON(http.StatusOK, CloneStatus{ - ObjectReference: ObjectReference{ - Href: fmt.Sprintf("/api/image-builder-composer/v2/clones/%v", jobId), - Id: jobId.String(), - Kind: "CloneComposeStatus", - }, - UploadStatus: us, + Href: fmt.Sprintf("/api/image-builder-composer/v2/clones/%v", jobId), + Id: jobId.String(), + Kind: "CloneComposeStatus", + Status: status, + Type: uploadType, + Options: options, }) } // TODO: determine upload status based on the target results, not job results func uploadStatusFromJobStatus(js *worker.JobStatus, je *clienterrors.Error) UploadStatusValue { if je != nil || js.Canceled { - return UploadStatusValueFailure + return UploadStatusValue(Failure) } if js.Started.IsZero() { - return UploadStatusValuePending + return UploadStatusValue(Pending) } if js.Finished.IsZero() { - return UploadStatusValueRunning + return UploadStatusValue(Running) } - return UploadStatusValueSuccess + return UploadStatusValue(Success) } // PostDepsolveBlueprint depsolves the packages in a blueprint and returns @@ -1541,16 +1492,11 @@ func (h *apiHandlers) GetDistributionList(ctx echo.Context) error { } // GetComposeDownload downloads a compose artifact -func (h *apiHandlers) GetComposeDownload(ctx echo.Context, id string) error { - return h.server.EnsureJobChannel(h.getComposeDownloadImpl)(ctx, id) +func (h *apiHandlers) GetComposeDownload(ctx echo.Context, jobId uuid.UUID) error { + return h.server.EnsureJobChannel(h.getComposeDownloadImpl)(ctx, jobId) } -func (h *apiHandlers) getComposeDownloadImpl(ctx echo.Context, id string) error { - jobId, err := uuid.Parse(id) - if err != nil { - return HTTPError(ErrorInvalidComposeId) - } - +func (h *apiHandlers) getComposeDownloadImpl(ctx echo.Context, jobId uuid.UUID) error { jobType, err := h.server.workers.JobType(jobId) if err != nil { return HTTPError(ErrorComposeNotFound) diff --git a/internal/cloudapi/v2/imagerequest.go b/internal/cloudapi/v2/imagerequest.go index 82bfe4de8..f0b3015b0 100644 --- a/internal/cloudapi/v2/imagerequest.go +++ b/internal/cloudapi/v2/imagerequest.go @@ -187,7 +187,7 @@ func newAzureTarget(options UploadOptions, imageType distro.ImageType) (*target. hypvgen := target.HyperVGenV1 if azureUploadOptions.HyperVGeneration != nil && - *azureUploadOptions.HyperVGeneration == AzureUploadOptionsHyperVGenerationV2 { + *azureUploadOptions.HyperVGeneration == AzureUploadOptionsHyperVGeneration(V2) { hypvgen = target.HyperVGenV2 } diff --git a/internal/cloudapi/v2/middleware.go b/internal/cloudapi/v2/middleware.go index b248253f4..5a6042076 100644 --- a/internal/cloudapi/v2/middleware.go +++ b/internal/cloudapi/v2/middleware.go @@ -25,16 +25,11 @@ func (s *Server) getTenantChannel(ctx echo.Context) (string, error) { return "", nil } -type ComposeHandlerFunc func(ctx echo.Context, id string) error +type ComposeHandlerFunc func(ctx echo.Context, jobId uuid.UUID) error // Ensures that the job's channel matches the JWT cannel set in the echo.Context func (s *Server) EnsureJobChannel(next ComposeHandlerFunc) ComposeHandlerFunc { - return func(c echo.Context, id string) error { - jobId, err := uuid.Parse(id) - if err != nil { - return HTTPError(ErrorInvalidComposeId) - } - + return func(c echo.Context, jobId uuid.UUID) error { ctxChannel, err := s.getTenantChannel(c) if err != nil { return err @@ -50,7 +45,7 @@ func (s *Server) EnsureJobChannel(next ComposeHandlerFunc) ComposeHandlerFunc { return HTTPError(ErrorComposeNotFound) } - return next(c, id) + return next(c, jobId) } } diff --git a/internal/cloudapi/v2/openapi.v2.gen.go b/internal/cloudapi/v2/openapi.v2.gen.go index 86f942cb5..e9f1357fa 100644 --- a/internal/cloudapi/v2/openapi.v2.gen.go +++ b/internal/cloudapi/v2/openapi.v2.gen.go @@ -314,7 +314,7 @@ type BlueprintFile struct { type BlueprintFileGroup0 = string // BlueprintFileGroup1 defines model for . -type BlueprintFileGroup1 = int +type BlueprintFileGroup1 = int64 // BlueprintFile_Group Group of the file as a gid or a group name type BlueprintFile_Group struct { @@ -325,7 +325,7 @@ type BlueprintFile_Group struct { type BlueprintFileUser0 = string // BlueprintFileUser1 defines model for . -type BlueprintFileUser1 = int +type BlueprintFileUser1 = int64 // BlueprintFile_User Owner of the file as a uid or a user name type BlueprintFile_User struct { @@ -688,7 +688,7 @@ type Directory struct { type DirectoryGroup0 = string // DirectoryGroup1 defines model for . -type DirectoryGroup1 = int +type DirectoryGroup1 = int64 // Directory_Group Group of the directory as a group name or a gid type Directory_Group struct { @@ -699,18 +699,15 @@ type Directory_Group struct { type DirectoryUser0 = string // DirectoryUser1 defines model for . -type DirectoryUser1 = int +type DirectoryUser1 = int64 // Directory_User Owner of the directory as a user name or a uid type Directory_User struct { union json.RawMessage } -// DistributionList defines model for DistributionList. -type DistributionList struct { - // Map Distribution name - Map *map[string]interface{} `json:"map,omitempty"` -} +// DistributionList Map of distributions to their architecture. +type DistributionList map[string]map[string][]BlueprintRepository // Error defines model for Error. type Error struct { @@ -772,7 +769,7 @@ type File struct { type FileGroup0 = string // FileGroup1 defines model for . -type FileGroup1 = int +type FileGroup1 = int64 // File_Group Group of the file as a gid or a group name type File_Group struct { @@ -783,7 +780,7 @@ type File_Group struct { type FileUser0 = string // FileUser1 defines model for . -type FileUser1 = int +type FileUser1 = int64 // File_User Owner of the file as a uid or a user name type File_User struct { @@ -2786,207 +2783,207 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "N87X7/8/AAD//86RvuTV6AAA", + "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", + "1IWqTKCeZt04X7///wAAAP//wpER3BXpAAA=", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/internal/cloudapi/v2/openapi.v2.yml b/internal/cloudapi/v2/openapi.v2.yml index e10904406..1912baef9 100644 --- a/internal/cloudapi/v2/openapi.v2.yml +++ b/internal/cloudapi/v2/openapi.v2.yml @@ -767,23 +767,18 @@ components: $ref: '#/components/schemas/ComposeStatus' DistributionList: - properties: - map: - type: object - description: Distribution name - additionalProperties: - map: - type: object - description: Architecture name - additionalProperties: - map: - type: object - description: Image type name - additionalProperties: - type: array - description: Repository used for this distro:arch:image-type - items: - $ref: '#/components/schemas/BlueprintRepository' + type: object + description: | + Map of distributions to their architecture. + additionalProperties: + type: object + description: | + Map of architectures to their repositories. + additionalProperties: + type: array + description: Repository used for this distro:arch:image-type + items: + $ref: '#/components/schemas/BlueprintRepository' ComposeStatus: allOf: @@ -2056,12 +2051,14 @@ components: oneOf: - type: string - type: integer + x-go-type: int64 description: Owner of the directory as a user name or a uid example: 'root' group: oneOf: - type: string - type: integer + x-go-type: int64 description: Group of the directory as a group name or a gid example: 'root' ensure_parents: @@ -2087,12 +2084,14 @@ components: oneOf: - type: string - type: integer + x-go-type: int64 description: Owner of the file as a uid or a user name example: 'root' group: oneOf: - type: string - type: integer + x-go-type: int64 description: Group of the file as a gid or a group name example: 'root' data: @@ -2122,12 +2121,14 @@ components: oneOf: - type: string - type: integer + x-go-type: int64 description: Owner of the file as a uid or a user name example: 'root' group: oneOf: - type: string - type: integer + x-go-type: int64 description: Group of the file as a gid or a group name example: 'root' data: diff --git a/internal/cloudapi/v2/v2_internal_test.go b/internal/cloudapi/v2/v2_internal_test.go index feae5ee3a..a2ea00c49 100644 --- a/internal/cloudapi/v2/v2_internal_test.go +++ b/internal/cloudapi/v2/v2_internal_test.go @@ -279,28 +279,24 @@ func TestStagesToPackageMetadata(t *testing.T) { }, pkgs: []PackageMetadata{ { - PackageMetadataCommon: PackageMetadataCommon{ - Type: "rpm", - Name: "vim-minimal", - Version: "8.0.1763", - Release: "15.el8", - Epoch: common.ToPtr("2"), - Arch: "x86_64", - Signature: common.ToPtr("v"), - }, - Sigmd5: "v", + Type: "rpm", + Name: "vim-minimal", + Version: "8.0.1763", + Release: "15.el8", + Epoch: common.ToPtr("2"), + Arch: "x86_64", + Signature: common.ToPtr("v"), + Sigmd5: "v", }, { - PackageMetadataCommon: PackageMetadataCommon{ - Type: "rpm", - Name: "unique", - Version: "1.90", - Release: "10", - Epoch: nil, - Arch: "aarch64", - Signature: common.ToPtr("v"), - }, - Sigmd5: "v", + Type: "rpm", + Name: "unique", + Version: "1.90", + Release: "10", + Epoch: nil, + Arch: "aarch64", + Signature: common.ToPtr("v"), + Sigmd5: "v", }, }, }, @@ -337,28 +333,24 @@ func TestStagesToPackageMetadata(t *testing.T) { }, pkgs: []PackageMetadata{ { - PackageMetadataCommon: PackageMetadataCommon{ - Type: "rpm", - Name: "vim-minimal", - Version: "8.0.1763", - Release: "15.el8", - Epoch: common.ToPtr("2"), - Arch: "x86_64", - Signature: common.ToPtr("v"), - }, - Sigmd5: "v", + Type: "rpm", + Name: "vim-minimal", + Version: "8.0.1763", + Release: "15.el8", + Epoch: common.ToPtr("2"), + Arch: "x86_64", + Signature: common.ToPtr("v"), + Sigmd5: "v", }, { - PackageMetadataCommon: PackageMetadataCommon{ - Type: "rpm", - Name: "unique", - Version: "1.90", - Release: "10", - Epoch: nil, - Arch: "aarch64", - Signature: common.ToPtr("v"), - }, - Sigmd5: "v", + Type: "rpm", + Name: "unique", + Version: "1.90", + Release: "10", + Epoch: nil, + Arch: "aarch64", + Signature: common.ToPtr("v"), + Sigmd5: "v", }, }, }, diff --git a/internal/cloudapi/v2/v2_koji_test.go b/internal/cloudapi/v2/v2_koji_test.go index 0aea316d4..44b26d57f 100644 --- a/internal/cloudapi/v2/v2_koji_test.go +++ b/internal/cloudapi/v2/v2_koji_test.go @@ -414,8 +414,6 @@ func TestKojiCompose(t *testing.T) { var composeReply v2.ComposeId err := json.Unmarshal(composeRawReply, &composeReply) require.NoError(t, err) - composeId, err := uuid.Parse(composeReply.Id) - require.NoError(t, err) // handle koji-init _, token, jobType, rawJob, _, err := workerServer.RequestJob(context.Background(), test_distro.TestArch3Name, []string{worker.JobTypeKojiInit}, []string{""}, uuid.Nil) @@ -438,7 +436,7 @@ func TestKojiCompose(t *testing.T) { // Finishing of the goroutine handling the manifest job is not deterministic and as a result, we may get // the second osbuild job first. // The build jobs ID is determined from the dependencies of the koji-finalize job dependencies. - finalizeInfo, err := workerServer.KojiFinalizeJobInfo(composeId, &worker.KojiFinalizeJobResult{}) + finalizeInfo, err := workerServer.KojiFinalizeJobInfo(composeReply.Id, &worker.KojiFinalizeJobResult{}) require.NoError(t, err) buildJobIDs := finalizeInfo.Deps[1:] require.Len(t, buildJobIDs, 2) @@ -551,7 +549,7 @@ func TestKojiCompose(t *testing.T) { } ] ] - }`, finalizeID, finalizeID, sbomDoc, v2.ImageSBOMSbomTypeSpdx), "details") + }`, finalizeID, finalizeID, sbomDoc, v2.ImageSBOMSbomType(v2.Spdx)), "details") }) } } diff --git a/internal/cloudapi/v2/v2_multi_tenancy_test.go b/internal/cloudapi/v2/v2_multi_tenancy_test.go index ea63e1e4d..89dafd553 100644 --- a/internal/cloudapi/v2/v2_multi_tenancy_test.go +++ b/internal/cloudapi/v2/v2_multi_tenancy_test.go @@ -95,8 +95,7 @@ func scheduleRequest(t *testing.T, handler http.Handler, orgID, request string) // Parse ID var id v2.ComposeId require.NoError(t, json.Unmarshal(result.Body, &id)) - - return uuid.MustParse(id.Id) + return id.Id } func getAllJobsOfCompose(t *testing.T, q jobqueue.JobQueue, finalJob uuid.UUID) []uuid.UUID { diff --git a/internal/cloudapi/v2/v2_test.go b/internal/cloudapi/v2/v2_test.go index 4fbd1bc45..b3a25fea6 100644 --- a/internal/cloudapi/v2/v2_test.go +++ b/internal/cloudapi/v2/v2_test.go @@ -895,7 +895,7 @@ func TestComposeStatusSuccess(t *testing.T) { } ] ] - }`, jobId, jobId, sbomDoc, v2.ImageSBOMSbomTypeSpdx), "details") + }`, jobId, jobId, sbomDoc, v2.ImageSBOMSbomType(v2.Spdx)), "details") } func TestComposeStatusFailure(t *testing.T) { @@ -959,12 +959,12 @@ func TestComposeStatusInvalidUUID(t *testing.T) { test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "GET", "/api/image-builder-composer/v2/composes/abcdef", ``, http.StatusBadRequest, ` { - "code": "IMAGE-BUILDER-COMPOSER-14", - "details": "", - "href": "/api/image-builder-composer/v2/errors/14", - "id": "14", + "code": "IMAGE-BUILDER-COMPOSER-42", + "details": "code=400, message=Invalid format for parameter id: error unmarshaling 'abcdef' text as *uuid.UUID: invalid UUID length: 6", + "href": "/api/image-builder-composer/v2/errors/42", + "id": "42", "kind": "Error", - "reason": "Invalid format for compose id" + "reason": "Invalid request, see details for more information" } `, "operation_id") } @@ -1748,7 +1748,7 @@ func TestDepsolveDistroErrors(t *testing.T) { "id": "40", "kind": "Error", "code": "IMAGE-BUILDER-COMPOSER-40", - "reason": "Invalid request, Blueprint and Cloud API request Distribution must match." + "reason": "Invalid request, Blueprint and Cloud API request Distribution must match" }`, "operation_id", "details") // Bad distro in request, none in blueprint @@ -1905,13 +1905,11 @@ func TestComposesRoute(t *testing.T) { var composeReply v2.ComposeId err := json.Unmarshal(reply, &composeReply) require.NoError(t, err) - jobID, err := uuid.Parse(composeReply.Id) - require.NoError(t, err) // List root composes test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "GET", "/api/image-builder-composer/v2/composes/", ``, http.StatusOK, fmt.Sprintf(`[{"href":"/api/image-builder-composer/v2/composes/%[1]s", "id":"%[1]s", "image_status":{"status":"pending"}, "kind":"ComposeStatus", "status":"pending"}]`, - jobID.String())) + composeReply.Id.String())) } func TestDownload(t *testing.T) { @@ -2082,12 +2080,16 @@ func TestComposeRequestMetadata(t *testing.T) { "image_requests":[{ "architecture": "%s", "image_type": "aws", + "size": 0, "repositories": [{ "baseurl": "somerepo.org", - "rhsm": false + "rhsm": false, + "check_repo_gpg": false, + "module_hotfixes": false }], "upload_options": { - "region": "eu-central-1" + "region": "eu-central-1", + "public": false } }] }`, test_distro.TestDistro1Name, test_distro.TestArch3Name)