Skip to content

Commit

Permalink
cloudapi: Add architecture support to the optional Blueprint
Browse files Browse the repository at this point in the history
The blueprint may have the architecture set. If set it must match the
architecture set by the request. This ensures that clients have
correctly set the architecture and prevents unexpectedly ignoring the
blueprint architecture.

With the WELR API the blueprint architecture would override the host
arch. But with the Cloud API there is no host arch to override so the
request must have the expected architecture set as part of the request.

Related: RHEL-60125
  • Loading branch information
bcl committed Dec 17, 2024
1 parent e90010f commit f683e0f
Show file tree
Hide file tree
Showing 5 changed files with 279 additions and 185 deletions.
7 changes: 7 additions & 0 deletions internal/cloudapi/v2/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,9 @@ func (request *ComposeRequest) GetBlueprintFromCompose() (blueprint.Blueprint, e
if rbp.Distro != nil {
bp.Distro = *rbp.Distro
}
if rbp.Architecture != nil {
bp.Arch = *rbp.Architecture
}

if rbp.Packages != nil {
for _, pkg := range *rbp.Packages {
Expand Down Expand Up @@ -1144,6 +1147,10 @@ func (request *ComposeRequest) GetImageRequests(distroFactory *distrofactory.Fac
}
var irs []imageRequest
for _, ir := range *request.ImageRequests {
// If there is an architecture in the blueprint it must match the request's arch
if len(bp.Arch) > 0 && ir.Architecture != bp.Arch {
return nil, HTTPError(ErrorMismatchedArchitecture)
}
arch, err := distribution.GetArch(ir.Architecture)
if err != nil {
return nil, HTTPError(ErrorUnsupportedArchitecture)
Expand Down
75 changes: 75 additions & 0 deletions internal/cloudapi/v2/compose_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,81 @@ func TestGetImageRequests_BlueprintDistro(t *testing.T) {
assert.Equal(t, got[0].blueprint.Distro, "fedora-39")
}

// TestGetImageRequests_BlueprintArchEmpty test blueprint with no arch
func TestGetImageRequests_BlueprintArchEmpty(t *testing.T) {
uo := UploadOptions(struct{}{})
request := &ComposeRequest{
Distribution: "fedora-40",
ImageRequest: &ImageRequest{
Architecture: "x86_64",
ImageType: ImageTypesAws,
UploadOptions: &uo,
Repositories: []Repository{},
},
Blueprint: &Blueprint{
Name: "arch-test",
},
}
// NOTE: current directory is the location of this file, back up so it can use ./repositories/
rr, err := reporegistry.New([]string{"../../../"})
require.NoError(t, err)
got, err := request.GetImageRequests(distrofactory.NewDefault(), rr)
assert.NoError(t, err)
require.Len(t, got, 1)
require.Greater(t, len(got[0].repositories), 0)
assert.Equal(t, got[0].imageType.Arch().Name(), "x86_64")
}

// TestGetImageRequests_BlueprintArch test to make sure matching arch works
func TestGetImageRequests_BlueprintArch(t *testing.T) {
uo := UploadOptions(struct{}{})
request := &ComposeRequest{
Distribution: "fedora-40",
ImageRequest: &ImageRequest{
Architecture: "x86_64",
ImageType: ImageTypesAws,
UploadOptions: &uo,
Repositories: []Repository{},
},
Blueprint: &Blueprint{
Name: "arch-test",
Architecture: common.ToPtr("x86_64"),
},
}
// NOTE: current directory is the location of this file, back up so it can use ./repositories/
rr, err := reporegistry.New([]string{"../../../"})
require.NoError(t, err)
got, err := request.GetImageRequests(distrofactory.NewDefault(), rr)
assert.NoError(t, err)
require.Len(t, got, 1)
require.Greater(t, len(got[0].repositories), 0)
assert.Equal(t, got[0].imageType.Arch().Name(), "x86_64")
}

// TestGetImageRequests_BlueprintArchError test to make sure mismatched arch returns error
func TestGetImageRequests_BlueprintArchError(t *testing.T) {
uo := UploadOptions(struct{}{})
request := &ComposeRequest{
Distribution: "fedora-40",
ImageRequest: &ImageRequest{
Architecture: "x86_64",
ImageType: ImageTypesAws,
UploadOptions: &uo,
Repositories: []Repository{},
},
Blueprint: &Blueprint{
Name: "arch-test",
Architecture: common.ToPtr("aarch64"),
},
}
// NOTE: current directory is the location of this file, back up so it can use ./repositories/
rr, err := reporegistry.New([]string{"../../../"})
require.NoError(t, err)
_, err = request.GetImageRequests(distrofactory.NewDefault(), rr)
require.Error(t, err)
assert.Equal(t, HTTPError(ErrorMismatchedArchitecture), err)
}

func TestOpenSCAPTailoringOptions(t *testing.T) {
cr := ComposeRequest{
Customizations: &Customizations{
Expand Down
2 changes: 2 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
ErrorMismatchedArchitecture ServiceErrorCode = 40

// Internal errors, these are bugs
ErrorFailedToInitializeBlueprint ServiceErrorCode = 1000
Expand Down Expand Up @@ -131,6 +132,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{ErrorMismatchedArchitecture, http.StatusBadRequest, "Invalid request, Blueprint and Cloud API request Architecture must match."},

serviceError{ErrorFailedToInitializeBlueprint, http.StatusInternalServerError, "Failed to initialize blueprint"},
serviceError{ErrorFailedToGenerateManifestSeed, http.StatusInternalServerError, "Failed to generate manifest seed"},
Expand Down
Loading

0 comments on commit f683e0f

Please sign in to comment.