talos/pkg/imager/filemap/filemap.go
Andrey Smirnov cbe6e7622d
fix: generate images for SBCs using imager
See https://github.com/siderolabs/image-factory/issues/43

Two fixes:

* pass path to the dtb, uboot and rpi-firmware explicitly
* include dtb, uboot and rpi-firmware into arm64 installer image when
  generated via imager (regular arm64 installer was fine)

(The generation of SBC images was not broken for Talos itself, but only
when used via Image Factory).

Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
2023-10-30 13:46:58 +04:00

109 lines
2.2 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 filemap provides a way to create reproducible layers from a file system.
package filemap
import (
"archive/tar"
"bytes"
"io"
"os"
"path/filepath"
"sort"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/tarball"
)
// File is a path -> file content map representing a file system.
type File struct {
ImagePath string
SourcePath string
}
// Walk the filesystem generating a filemap.
func Walk(sourceBasePath, imageBasePath string) ([]File, error) {
var filemap []File
err := filepath.WalkDir(sourceBasePath, func(path string, d os.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
rel, err := filepath.Rel(sourceBasePath, path)
if err != nil {
return err
}
filemap = append(filemap, File{
ImagePath: filepath.Join(imageBasePath, rel),
SourcePath: path,
})
return nil
})
return filemap, err
}
// Layer creates a layer from a single file map.
//
// These layers are reproducible and consistent.
//
// A filemap is a path -> file content map representing a file system.
func Layer(filemap []File) (v1.Layer, error) {
b := &bytes.Buffer{}
w := tar.NewWriter(b)
sort.Slice(filemap, func(i, j int) bool {
return filemap[i].ImagePath < filemap[j].ImagePath
})
for _, entry := range filemap {
if err := func(entry File) error {
in, err := os.Open(entry.SourcePath)
if err != nil {
return err
}
defer in.Close() //nolint:errcheck
st, err := in.Stat()
if err != nil {
return err
}
if err = w.WriteHeader(&tar.Header{
Name: entry.ImagePath,
Size: st.Size(),
}); err != nil {
return err
}
_, err = io.Copy(w, in)
if err != nil {
return err
}
return in.Close()
}(entry); err != nil {
return nil, err
}
}
if err := w.Close(); err != nil {
return nil, err
}
// Return a new copy of the buffer each time it's opened.
return tarball.LayerFromOpener(func() (io.ReadCloser, error) {
return io.NopCloser(bytes.NewReader(b.Bytes())), nil
})
}