Andrey Smirnov 354baca8a0
feat: implement boot asset cache
Fixes #4

This caches the result of the boot asset build (a call to Talos
`imager`) so that we don't rebuild the asset twice.

OCI Registry is used as a cache. An internal registry can be used for
caching, we don't need to expose it to the world.

Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
2023-10-30 14:05:23 +04:00

109 lines
2.7 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 http
import (
"context"
_ "embed"
"fmt"
"io"
"net/http"
"strings"
"text/template"
"github.com/blang/semver/v4"
"github.com/julienschmidt/httprouter"
"github.com/siderolabs/gen/ensure"
"github.com/siderolabs/image-factory/internal/profile"
)
//go:embed standard.ipxe
var standardIPXE string
//go:embed secureboot.ipxe
var securebootIPXE string
// handlePXE delivers a PXE script to boot Talos.
func (f *Frontend) handlePXE(ctx context.Context, w http.ResponseWriter, _ *http.Request, p httprouter.Params) error {
schematicID := p.ByName("schematic")
schematic, err := f.schematicFactory.Get(ctx, schematicID)
if err != nil {
return err
}
versionTag := p.ByName("version")
if !strings.HasPrefix(versionTag, "v") {
versionTag = "v" + versionTag
}
version, err := semver.Parse(versionTag[1:])
if err != nil {
return fmt.Errorf("error parsing version: %w", err)
}
// the PXE format is just platform+arch, so if we append cmdline, it should parse
path := "cmdline-" + p.ByName("path")
prof, err := profile.ParseFromPath(path)
if err != nil {
return fmt.Errorf("error parsing profile from path: %w", err)
}
prof, err = profile.EnhanceFromSchematic(ctx, prof, schematic, f.artifactsManager, versionTag)
if err != nil {
return fmt.Errorf("error enhancing profile from schematic: %w", err)
}
if err = prof.Validate(); err != nil {
return fmt.Errorf("error validating profile: %w", err)
}
if prof.SecureBootEnabled() {
return ensure.Value(template.New("secureboot.ipxe").
Parse(securebootIPXE)).
Execute(w,
struct {
UKIURL string
}{
UKIURL: f.options.ExternalURL.JoinPath("image", schematicID, versionTag, fmt.Sprintf("%s-%s-secureboot.uki.efi", prof.Platform, prof.Arch)).String(),
},
)
}
// build the cmdline
asset, err := f.assetBuilder.Build(ctx, prof, version.String())
if err != nil {
return err
}
reader, err := asset.Reader()
if err != nil {
return err
}
defer reader.Close() //nolint:errcheck
cmdline, err := io.ReadAll(reader)
if err != nil {
return err
}
return ensure.Value(template.New("standard.ipxe").
Parse(standardIPXE)).
Execute(w,
struct {
KernelURL string
Cmdline string
InitramfsURL string
}{
KernelURL: f.options.ExternalURL.JoinPath("image", schematicID, versionTag, fmt.Sprintf("kernel-%s", prof.Arch)).String(),
Cmdline: string(cmdline),
InitramfsURL: f.options.ExternalURL.JoinPath("image", schematicID, versionTag, fmt.Sprintf("initramfs-%s.xz", prof.Arch)).String(),
},
)
}