Skip to content

Commit

Permalink
cloudapi: Add ability to skip uploading and save image locally
Browse files Browse the repository at this point in the history
During development it can be very useful to store the results locally
instead of uploading to a remote system. This implements a development
only option to help with that.

To use it you need to add OSBUILD_LOCALSAVE to the server's environment.
This can be done by editing /usr/lib/systemd/system/osbuild-composer.service
and adding:

Environment="OSBUILD_LOCALSAVE=1"

You can then use an 'upload_options' object to skip trying to upload to
the default service for the type of image, eg:

    "image_requests": [
    {
      "architecture": "x86_64",
      "image_type": "guest-image",
      "upload_options": {
          "local_save": true
      },
      ...
    }]

The results will be saved to /var/lib/osbuild-composer/artifacts/UUID/
using the default filename for the image type.

If local_save is used without OSBUILD_LOCALSAVE being set it will return
an error with id=36 saying 'local_save is not enabled'.
  • Loading branch information
bcl committed Aug 7, 2023
1 parent 1b04a78 commit 4a35ed7
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 128 deletions.
2 changes: 2 additions & 0 deletions internal/cloudapi/v2/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const (
ErrorInvalidImageFromComposeId ServiceErrorCode = 33
ErrorImageNotFound ServiceErrorCode = 34
ErrorInvalidCustomization ServiceErrorCode = 35
ErrorLocalSaveNotEnabled ServiceErrorCode = 36

// Internal errors, these are bugs
ErrorFailedToInitializeBlueprint ServiceErrorCode = 1000
Expand Down Expand Up @@ -123,6 +124,7 @@ func getServiceErrors() serviceErrors {
serviceError{ErrorInvalidImageFromComposeId, http.StatusBadRequest, "Invalid format for image id"},
serviceError{ErrorImageNotFound, http.StatusBadRequest, "Image with given id not found"},
serviceError{ErrorInvalidCustomization, http.StatusBadRequest, "Invalid image customization"},
serviceError{ErrorLocalSaveNotEnabled, http.StatusBadRequest, "local_save is not enabled"},

serviceError{ErrorFailedToInitializeBlueprint, http.StatusInternalServerError, "Failed to initialize blueprint"},
serviceError{ErrorFailedToGenerateManifestSeed, http.StatusInternalServerError, "Failed to generate manifest seed"},
Expand Down
41 changes: 41 additions & 0 deletions internal/cloudapi/v2/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"math"
"math/big"
"net/http"
"os"
"strconv"
"strings"

Expand Down Expand Up @@ -106,6 +107,33 @@ func splitExtension(filename string) string {
return "." + strings.Join(filenameParts[1:], ".")
}

// isLocalSave checks the environment to see if a local save has been enabled
// and tests the UploadOptions to see if it has been selected
func isLocalSave(options *UploadOptions) (bool, error) {
if options == nil {
return false, nil
}

var local LocalUploadOptions
// This is a terrible way to do this, but it is imposed by the OpenAPI code generator so...
j, err := json.Marshal(*options)
if err != nil {
return false, nil
}
err = json.Unmarshal(j, &local)
if err != nil {
return false, nil
}

// Return an error if local_save is set but not enabled
_, enabled := os.LookupEnv("OSBUILD_LOCALSAVE")
if !enabled && local.LocalSave {
return false, HTTPError(ErrorLocalSaveNotEnabled)
}

return enabled && local.LocalSave, nil
}

type imageRequest struct {
imageType distro.ImageType
arch distro.Arch
Expand Down Expand Up @@ -196,12 +224,25 @@ func (h *apiHandlers) PostCompose(ctx echo.Context) error {
return err
}

// Check to see if local_save is enabled and set
localSave, err := isLocalSave(ir.UploadOptions)
if err != nil {
return err
}

var irTarget *target.Target
if ir.UploadOptions == nil {
// nowhere to put the image, this is a user error
if request.Koji == nil {
return HTTPError(ErrorJSONUnMarshallingError)
}
} else if localSave {
// Override the image type upload selection and save it locally
// Final image is in /var/lib/osbuild-composer/artifacts/UUID/
irTarget = target.NewWorkerServerTarget()
irTarget.ImageName = imageType.Filename()
irTarget.OsbuildArtifact.ExportFilename = imageType.Filename()
irTarget.OsbuildArtifact.ExportName = imageType.Exports()[0]
} else {
// Get the target for the selected image type
irTarget, err = ir.GetTarget(&request, imageType)
Expand Down
Loading

0 comments on commit 4a35ed7

Please sign in to comment.