debian-forge-cli/cmd/image-builder/upload.go
Michael Vogt 25f21a3205 main: add upload support directly to build
This commit adds support to upload the build image directly to
the target cloud. Currently only ami/AWS is supported.

If the cloud specific configuration is given at the commandline
and the image type is a cloud image the cloud upload will happen
automatically (just like with bib). Incomplete upload config is
an error.
2025-02-11 13:38:04 +01:00

132 lines
2.8 KiB
Go

package main
import (
"bufio"
"fmt"
"io"
"os"
"github.com/cheggaaa/pb/v3"
"github.com/spf13/cobra"
"github.com/osbuild/bootc-image-builder/bib/pkg/progress"
"github.com/osbuild/images/pkg/cloud"
"github.com/osbuild/images/pkg/cloud/awscloud"
)
type MissingUploadConfigError struct {
missing []string
allMissing bool
}
func (e *MissingUploadConfigError) Error() string {
return fmt.Sprintf("missing upload configuration: %q", e.missing)
}
type UploadTypeUnsupportedError struct {
typ string
}
func (e *UploadTypeUnsupportedError) Error() string {
return fmt.Sprintf("unsupported upload type %q", e.typ)
}
var awscloudNewUploader = awscloud.NewUploader
func uploadImageWithProgress(uploader cloud.Uploader, imagePath string) error {
f, err := os.Open(imagePath)
if err != nil {
return err
}
defer f.Close()
// setup basic progress
st, err := f.Stat()
if err != nil {
return fmt.Errorf("cannot stat upload: %v", err)
}
pbar := pb.New64(st.Size())
pbar.Set(pb.Bytes, true)
pbar.SetWriter(osStdout)
r := pbar.NewProxyReader(f)
pbar.Start()
defer pbar.Finish()
return uploader.UploadAndRegister(r, osStderr)
}
func uploaderCheckWithProgress(pbar progress.ProgressBar, uploader cloud.Uploader) error {
pr, pw := io.Pipe()
defer pw.Close()
go func() {
scanner := bufio.NewScanner(pr)
for scanner.Scan() {
pbar.SetMessagef("%s", scanner.Text())
}
}()
return uploader.Check(pw)
}
func uploaderFor(cmd *cobra.Command, typeOrCloud string) (cloud.Uploader, error) {
switch typeOrCloud {
case "ami", "aws":
return uploaderForCmdAWS(cmd)
default:
return nil, &UploadTypeUnsupportedError{typeOrCloud}
}
}
func uploaderForCmdAWS(cmd *cobra.Command) (cloud.Uploader, error) {
amiName, err := cmd.Flags().GetString("aws-ami-name")
if err != nil {
return nil, err
}
bucketName, err := cmd.Flags().GetString("aws-bucket")
if err != nil {
return nil, err
}
region, err := cmd.Flags().GetString("aws-region")
if err != nil {
return nil, err
}
var missing []string
requiredArgs := []string{"aws-ami-name", "aws-bucket", "aws-region"}
for _, argName := range requiredArgs {
arg, err := cmd.Flags().GetString(argName)
if err != nil {
return nil, err
}
if arg == "" {
missing = append(missing, fmt.Sprintf("--%s", argName))
}
}
if len(missing) > 0 {
return nil, &MissingUploadConfigError{
missing: missing,
allMissing: len(missing) == len(requiredArgs),
}
}
return awscloudNewUploader(region, bucketName, amiName, nil)
}
func cmdUpload(cmd *cobra.Command, args []string) error {
uploadTo, err := cmd.Flags().GetString("to")
if err != nil {
return err
}
if uploadTo == "" {
return fmt.Errorf("missing --to parameter, try --to=aws")
}
imagePath := args[0]
uploader, err := uploaderFor(cmd, uploadTo)
if err != nil {
return err
}
return uploadImageWithProgress(uploader, imagePath)
}