Mateusz Urbanek 42c0bdbf32
feat: add provisioner flag to images default command
Fixes #11925

Signed-off-by: Mateusz Urbanek <mateusz.urbanek@siderolabs.com>
2025-10-02 10:54:25 +02:00

105 lines
2.4 KiB
Go

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package main
import (
"context"
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
"path/filepath"
"golang.org/x/sync/errgroup"
)
var extensions = map[string]string{
"aws": ".raw.zst",
"gcp": ".raw.tar.gz",
}
// FactoryDownloader is helper for downloading images from Image Factory.
type FactoryDownloader struct {
Target string
Options Options
}
func (f *FactoryDownloader) Download(ctx context.Context) error {
g, ctx := errgroup.WithContext(ctx)
for _, arch := range f.Options.Architectures {
g.Go(func() error {
artifact := fmt.Sprintf("%s-%s%s", f.Target, arch, extensions[f.Target])
r, err := f.getArtifact(ctx, artifact)
if err != nil {
return err
}
defer r.Close() //nolint:errcheck
return f.saveArtifact(artifact, r)
})
}
return g.Wait()
}
func (f *FactoryDownloader) getArtifact(ctx context.Context, name string) (io.ReadCloser, error) {
url, err := url.JoinPath(
f.Options.FactoryHost,
"image",
f.Options.SchematicFor(f.Target),
f.Options.Tag,
name,
)
if err != nil {
return nil, fmt.Errorf("failed to construct URL: %w", err)
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
log.Printf("requesting artifact: %s", url)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to download image from %q: %w", url, err)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code %d when fetching %q", resp.StatusCode, url)
}
return resp.Body, nil
}
func (f *FactoryDownloader) saveArtifact(name string, r io.Reader) error {
artifact := filepath.Join(f.Options.ArtifactsPath, name)
err := os.MkdirAll(f.Options.ArtifactsPath, 0o755)
if err != nil {
return fmt.Errorf("failed to create artifacts directory %q: %w", f.Options.ArtifactsPath, err)
}
of, err := os.OpenFile(artifact, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644)
if err != nil {
return fmt.Errorf("failed to create file %q: %w", artifact, err)
}
defer of.Close() //nolint:errcheck
log.Printf("saving artifact: %s", artifact)
_, err = io.Copy(of, r)
if err != nil {
return fmt.Errorf("failed to write image to file %q: %w", artifact, err)
}
return nil
}