mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-07-19 01:06:27 +00:00
Merge pull request #4025 from deitch/tag-sources-in-tar
include image reference as source in every tar file header
This commit is contained in:
commit
a610332100
@ -8,17 +8,21 @@ import (
|
||||
// apkTarWriter apk-aware tar writer that consolidates installed database, so that
|
||||
// it can be called multiple times and will do the union of all such databases,
|
||||
// rather than overwriting the previous one.
|
||||
// Useful only for things that write to the base filesystem, i.e. init, since everything
|
||||
// else is inside containers.
|
||||
const apkInstalledPath = "lib/apk/db/installed"
|
||||
|
||||
type apkTarWriter struct {
|
||||
*tar.Writer
|
||||
dbs [][]byte
|
||||
current *bytes.Buffer
|
||||
dbs [][]byte
|
||||
current *bytes.Buffer
|
||||
location string
|
||||
}
|
||||
|
||||
func newAPKTarWriter(w *tar.Writer) *apkTarWriter {
|
||||
func newAPKTarWriter(w *tar.Writer, location string) *apkTarWriter {
|
||||
return &apkTarWriter{
|
||||
Writer: w,
|
||||
Writer: w,
|
||||
location: location,
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,6 +71,10 @@ func (a *apkTarWriter) WriteAPKDB() error {
|
||||
Gid: 0,
|
||||
Typeflag: tar.TypeReg,
|
||||
Size: int64(size),
|
||||
PAXRecords: map[string]string{
|
||||
PaxRecordLinuxkitSource: "LINUXKIT.apkinit",
|
||||
PaxRecordLinuxkitLocation: a.location,
|
||||
},
|
||||
}
|
||||
if err := a.Writer.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
|
@ -92,25 +92,25 @@ func outputImage(image *Image, section string, prefix string, m Moby, idMap map[
|
||||
}
|
||||
src, err := imagePull(&ref, opts.Pull, opts.CacheDir, opts.DockerCache, opts.Arch)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not pull image %s: %v", image.Image, err)
|
||||
return fmt.Errorf("could not pull image %s: %v", image.Image, err)
|
||||
}
|
||||
configRaw, err := src.Config()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to retrieve config for %s: %v", image.Image, err)
|
||||
return fmt.Errorf("failed to retrieve config for %s: %v", image.Image, err)
|
||||
}
|
||||
oci, runtime, err := ConfigToOCI(image, configRaw, idMap)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to create OCI spec for %s: %v", image.Image, err)
|
||||
return fmt.Errorf("failed to create OCI spec for %s: %v", image.Image, err)
|
||||
}
|
||||
config, err := json.MarshalIndent(oci, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to create config for %s: %v", image.Image, err)
|
||||
return fmt.Errorf("failed to create config for %s: %v", image.Image, err)
|
||||
}
|
||||
path := path.Join("containers", section, prefix+image.Name)
|
||||
readonly := oci.Root.Readonly
|
||||
err = ImageBundle(path, image.ref, config, runtime, iw, readonly, dupMap, opts)
|
||||
err = ImageBundle(path, section, image.ref, config, runtime, iw, readonly, dupMap, opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to extract root filesystem for %s: %v", image.Image, err)
|
||||
return fmt.Errorf("failed to extract root filesystem for %s: %v", image.Image, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -153,14 +153,14 @@ func Build(m Moby, w io.Writer, opts BuildOpts) error {
|
||||
if m.Kernel.ref != nil {
|
||||
// get kernel and initrd tarball and ucode cpio archive from container
|
||||
log.Infof("Extract kernel image: %s", m.Kernel.ref)
|
||||
kf := newKernelFilter(iw, m.Kernel.Cmdline, m.Kernel.Binary, m.Kernel.Tar, m.Kernel.UCode, opts.DecompressKernel)
|
||||
err := ImageTar(m.Kernel.ref, "", kf, "", opts)
|
||||
kf := newKernelFilter(m.Kernel.ref, iw, m.Kernel.Cmdline, m.Kernel.Binary, m.Kernel.Tar, m.Kernel.UCode, opts.DecompressKernel)
|
||||
err := ImageTar("kernel", m.Kernel.ref, "", kf, "", opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to extract kernel image and tarball: %v", err)
|
||||
return fmt.Errorf("failed to extract kernel image and tarball: %v", err)
|
||||
}
|
||||
err = kf.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Close error: %v", err)
|
||||
return fmt.Errorf("close error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,10 +168,10 @@ func Build(m Moby, w io.Writer, opts BuildOpts) error {
|
||||
if len(m.Init) != 0 {
|
||||
log.Infof("Add init containers:")
|
||||
}
|
||||
apkTar := newAPKTarWriter(iw)
|
||||
apkTar := newAPKTarWriter(iw, "init")
|
||||
for _, ii := range m.initRefs {
|
||||
log.Infof("Process init image: %s", ii)
|
||||
err := ImageTar(ii, "", apkTar, resolvconfSymlink, opts)
|
||||
err := ImageTar("init", ii, "", apkTar, resolvconfSymlink, opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to build init tarball from %s: %v", ii, err)
|
||||
}
|
||||
@ -252,9 +252,10 @@ type kernelFilter struct {
|
||||
foundKernel bool
|
||||
foundKTar bool
|
||||
foundUCode bool
|
||||
ref *reference.Spec
|
||||
}
|
||||
|
||||
func newKernelFilter(tw *tar.Writer, cmdline string, kernel string, tar, ucode *string, decompressKernel bool) *kernelFilter {
|
||||
func newKernelFilter(ref *reference.Spec, tw *tar.Writer, cmdline string, kernel string, tar, ucode *string, decompressKernel bool) *kernelFilter {
|
||||
tarName, kernelName, ucodeName := "kernel.tar", "kernel", ""
|
||||
if tar != nil {
|
||||
tarName = *tar
|
||||
@ -268,7 +269,7 @@ func newKernelFilter(tw *tar.Writer, cmdline string, kernel string, tar, ucode *
|
||||
if ucode != nil {
|
||||
ucodeName = *ucode
|
||||
}
|
||||
return &kernelFilter{tw: tw, cmdline: cmdline, kernel: kernelName, tar: tarName, ucode: ucodeName, decompressKernel: decompressKernel}
|
||||
return &kernelFilter{ref: ref, tw: tw, cmdline: cmdline, kernel: kernelName, tar: tarName, ucode: ucodeName, decompressKernel: decompressKernel}
|
||||
}
|
||||
|
||||
func (k *kernelFilter) finishTar() error {
|
||||
@ -299,7 +300,7 @@ func (k *kernelFilter) finishTar() error {
|
||||
}
|
||||
|
||||
tr := tar.NewReader(k.buffer)
|
||||
err := tarAppend(k.tw, tr)
|
||||
err := tarAppend(k.ref, k.tw, tr)
|
||||
k.buffer = nil
|
||||
return err
|
||||
}
|
||||
@ -348,11 +349,12 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error {
|
||||
// If we handled the ucode, /boot already exist.
|
||||
if !k.foundUCode {
|
||||
whdr := &tar.Header{
|
||||
Name: "boot",
|
||||
Mode: 0755,
|
||||
Typeflag: tar.TypeDir,
|
||||
ModTime: defaultModTime,
|
||||
Format: tar.FormatPAX,
|
||||
Name: "boot",
|
||||
Mode: 0755,
|
||||
Typeflag: tar.TypeDir,
|
||||
ModTime: defaultModTime,
|
||||
Format: tar.FormatPAX,
|
||||
PAXRecords: hdr.PAXRecords,
|
||||
}
|
||||
if err := tw.WriteHeader(whdr); err != nil {
|
||||
return err
|
||||
@ -360,11 +362,12 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error {
|
||||
}
|
||||
// add the cmdline in /boot/cmdline
|
||||
whdr := &tar.Header{
|
||||
Name: "boot/cmdline",
|
||||
Mode: 0644,
|
||||
Size: int64(len(k.cmdline)),
|
||||
ModTime: defaultModTime,
|
||||
Format: tar.FormatPAX,
|
||||
Name: "boot/cmdline",
|
||||
Mode: 0644,
|
||||
Size: int64(len(k.cmdline)),
|
||||
ModTime: defaultModTime,
|
||||
Format: tar.FormatPAX,
|
||||
PAXRecords: hdr.PAXRecords,
|
||||
}
|
||||
if err := tw.WriteHeader(whdr); err != nil {
|
||||
return err
|
||||
@ -375,11 +378,12 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error {
|
||||
}
|
||||
// Stash the kernel header and prime the buffer for the kernel
|
||||
k.hdr = &tar.Header{
|
||||
Name: "boot/kernel",
|
||||
Mode: hdr.Mode,
|
||||
Size: hdr.Size,
|
||||
ModTime: defaultModTime,
|
||||
Format: tar.FormatPAX,
|
||||
Name: "boot/kernel",
|
||||
Mode: hdr.Mode,
|
||||
Size: hdr.Size,
|
||||
ModTime: defaultModTime,
|
||||
Format: tar.FormatPAX,
|
||||
PAXRecords: hdr.PAXRecords,
|
||||
}
|
||||
k.buffer = new(bytes.Buffer)
|
||||
case k.tar:
|
||||
@ -392,22 +396,24 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error {
|
||||
// If we handled the kernel, /boot already exist.
|
||||
if !k.foundKernel {
|
||||
whdr := &tar.Header{
|
||||
Name: "boot",
|
||||
Mode: 0755,
|
||||
Typeflag: tar.TypeDir,
|
||||
ModTime: defaultModTime,
|
||||
Format: tar.FormatPAX,
|
||||
Name: "boot",
|
||||
Mode: 0755,
|
||||
Typeflag: tar.TypeDir,
|
||||
ModTime: defaultModTime,
|
||||
Format: tar.FormatPAX,
|
||||
PAXRecords: hdr.PAXRecords,
|
||||
}
|
||||
if err := tw.WriteHeader(whdr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
whdr := &tar.Header{
|
||||
Name: "boot/ucode.cpio",
|
||||
Mode: hdr.Mode,
|
||||
Size: hdr.Size,
|
||||
ModTime: defaultModTime,
|
||||
Format: tar.FormatPAX,
|
||||
Name: "boot/ucode.cpio",
|
||||
Mode: hdr.Mode,
|
||||
Size: hdr.Size,
|
||||
ModTime: defaultModTime,
|
||||
Format: tar.FormatPAX,
|
||||
PAXRecords: hdr.PAXRecords,
|
||||
}
|
||||
if err := tw.WriteHeader(whdr); err != nil {
|
||||
return err
|
||||
@ -419,7 +425,7 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func tarAppend(iw *tar.Writer, tr *tar.Reader) error {
|
||||
func tarAppend(ref *reference.Spec, iw *tar.Writer, tr *tar.Reader) error {
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
@ -428,6 +434,12 @@ func tarAppend(iw *tar.Writer, tr *tar.Reader) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hdr.Format = tar.FormatPAX
|
||||
if hdr.PAXRecords == nil {
|
||||
hdr.PAXRecords = make(map[string]string)
|
||||
}
|
||||
hdr.PAXRecords[PaxRecordLinuxkitSource] = ref.String()
|
||||
hdr.PAXRecords[PaxRecordLinuxkitLocation] = "kernel"
|
||||
err = iw.WriteHeader(hdr)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -478,7 +490,7 @@ func decompressKernel(src *bytes.Buffer) (*bytes.Buffer, error) {
|
||||
versionMajor := int(s[versionIdx])
|
||||
versionMinor := int(s[versionIdx+1])
|
||||
if versionMajor < 2 && versionMinor < 8 {
|
||||
return nil, fmt.Errorf("Unsupported bzImage version: %d.%d", versionMajor, versionMinor)
|
||||
return nil, fmt.Errorf("unsupported bzImage version: %d.%d", versionMajor, versionMinor)
|
||||
}
|
||||
|
||||
setupSectors := uint32(s[setupSectorsIdx])
|
||||
@ -488,7 +500,7 @@ func decompressKernel(src *bytes.Buffer) (*bytes.Buffer, error) {
|
||||
log.Debugf("bzImage: Payload at Offset: %d Length: %d", payloadOff, payloadLen)
|
||||
|
||||
if len(s) < int(payloadOff+payloadLen) {
|
||||
return nil, fmt.Errorf("Compressed bzImage payload exceeds size of image")
|
||||
return nil, fmt.Errorf("compressed bzImage payload exceeds size of image")
|
||||
}
|
||||
|
||||
if bytes.HasPrefix(s[payloadOff:], []byte(gzipMagic)) {
|
||||
@ -496,10 +508,10 @@ func decompressKernel(src *bytes.Buffer) (*bytes.Buffer, error) {
|
||||
return gunzip(bytes.NewBuffer(s[payloadOff : payloadOff+payloadLen]))
|
||||
}
|
||||
// TODO(rn): Add more supported formats
|
||||
return nil, fmt.Errorf("Unsupported bzImage payload format at offset %d", payloadOff)
|
||||
return nil, fmt.Errorf("unsupported bzImage payload format at offset %d", payloadOff)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("No compressed kernel or no supported format found")
|
||||
return nil, fmt.Errorf("no compressed kernel or no supported format found")
|
||||
}
|
||||
|
||||
func gunzip(src *bytes.Buffer) (*bytes.Buffer, error) {
|
||||
@ -529,7 +541,7 @@ func metadata(m Moby, md string) ([]byte, error) {
|
||||
case "yaml":
|
||||
return yaml.Marshal(m)
|
||||
default:
|
||||
return []byte{}, fmt.Errorf("Unsupported metadata type: %s", md)
|
||||
return []byte{}, fmt.Errorf("unsupported metadata type: %s", md)
|
||||
}
|
||||
}
|
||||
|
||||
@ -540,10 +552,10 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error {
|
||||
if len(m.Files) != 0 {
|
||||
log.Infof("Add files:")
|
||||
}
|
||||
for _, f := range m.Files {
|
||||
for filecount, f := range m.Files {
|
||||
log.Infof(" %s", f.Path)
|
||||
if f.Path == "" {
|
||||
return errors.New("Did not specify path for file")
|
||||
return errors.New("did not specify path for file")
|
||||
}
|
||||
// tar archives should not have absolute paths
|
||||
if f.Path[0] == '/' {
|
||||
@ -557,7 +569,7 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error {
|
||||
var err error
|
||||
mode, err = strconv.ParseInt(f.Mode, 8, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Cannot parse file mode as octal value: %v", err)
|
||||
return fmt.Errorf("cannot parse file mode as octal value: %v", err)
|
||||
}
|
||||
}
|
||||
dirMode := mode
|
||||
@ -586,10 +598,10 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error {
|
||||
}
|
||||
if !f.Directory && f.Symlink == "" && f.Contents == nil {
|
||||
if f.Source == "" && f.Metadata == "" {
|
||||
return fmt.Errorf("Contents of file (%s) not specified", f.Path)
|
||||
return fmt.Errorf("contents of file (%s) not specified", f.Path)
|
||||
}
|
||||
if f.Source != "" && f.Metadata != "" {
|
||||
return fmt.Errorf("Specified Source and Metadata for file: %s", f.Path)
|
||||
return fmt.Errorf("specified Source and Metadata for file: %s", f.Path)
|
||||
}
|
||||
if f.Source != "" {
|
||||
source := f.Source
|
||||
@ -600,7 +612,7 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error {
|
||||
_, err := os.Stat(source)
|
||||
if err != nil {
|
||||
// skip if not found or readable
|
||||
log.Debugf("Skipping file [%s] as not readable and marked optional", source)
|
||||
log.Debugf("skipping file [%s] as not readable and marked optional", source)
|
||||
continue
|
||||
}
|
||||
}
|
||||
@ -617,10 +629,10 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error {
|
||||
}
|
||||
} else {
|
||||
if f.Metadata != "" {
|
||||
return fmt.Errorf("Specified Contents and Metadata for file: %s", f.Path)
|
||||
return fmt.Errorf("specified Contents and Metadata for file: %s", f.Path)
|
||||
}
|
||||
if f.Source != "" {
|
||||
return fmt.Errorf("Specified Contents and Source for file: %s", f.Path)
|
||||
return fmt.Errorf("specified Contents and Source for file: %s", f.Path)
|
||||
}
|
||||
}
|
||||
// we need all the leading directories
|
||||
@ -644,6 +656,10 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error {
|
||||
Uid: int(uid),
|
||||
Gid: int(gid),
|
||||
Format: tar.FormatPAX,
|
||||
PAXRecords: map[string]string{
|
||||
PaxRecordLinuxkitSource: "linuxkit.files",
|
||||
PaxRecordLinuxkitLocation: fmt.Sprintf("files[%d]", filecount),
|
||||
},
|
||||
}
|
||||
err := tw.WriteHeader(hdr)
|
||||
if err != nil {
|
||||
@ -660,10 +676,14 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error {
|
||||
Uid: int(uid),
|
||||
Gid: int(gid),
|
||||
Format: tar.FormatPAX,
|
||||
PAXRecords: map[string]string{
|
||||
PaxRecordLinuxkitSource: "linuxkit.files",
|
||||
PaxRecordLinuxkitLocation: fmt.Sprintf("files[%d]", filecount),
|
||||
},
|
||||
}
|
||||
if f.Directory {
|
||||
if f.Contents != nil {
|
||||
return errors.New("Directory with contents not allowed")
|
||||
return errors.New("directory with contents not allowed")
|
||||
}
|
||||
hdr.Typeflag = tar.TypeDir
|
||||
err := tw.WriteHeader(hdr)
|
||||
|
@ -14,6 +14,14 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
// PaxRecordLinuxkitSource report the package source for a specific file
|
||||
PaxRecordLinuxkitSource = "LINUXKIT.source"
|
||||
// PaxRecordLinuxkitLocation report the location of the file in the linuxkit.yaml
|
||||
// that led to this file being in this location
|
||||
PaxRecordLinuxkitLocation = "LINUXKIT.location"
|
||||
)
|
||||
|
||||
type tarWriter interface {
|
||||
Close() error
|
||||
Flush() error
|
||||
@ -140,7 +148,8 @@ var touch = map[string]tar.Header{
|
||||
}
|
||||
|
||||
// tarPrefix creates the leading directories for a path
|
||||
func tarPrefix(path string, tw tarWriter) error {
|
||||
// path is the path to prefix, location is where this appears in the linuxkit.yaml file
|
||||
func tarPrefix(path, location string, ref *reference.Spec, tw tarWriter) error {
|
||||
if path == "" {
|
||||
return nil
|
||||
}
|
||||
@ -160,6 +169,10 @@ func tarPrefix(path string, tw tarWriter) error {
|
||||
ModTime: defaultModTime,
|
||||
Typeflag: tar.TypeDir,
|
||||
Format: tar.FormatPAX,
|
||||
PAXRecords: map[string]string{
|
||||
PaxRecordLinuxkitSource: ref.String(),
|
||||
PaxRecordLinuxkitLocation: location,
|
||||
},
|
||||
}
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
@ -170,13 +183,14 @@ func tarPrefix(path string, tw tarWriter) error {
|
||||
}
|
||||
|
||||
// ImageTar takes a Docker image and outputs it to a tar stream
|
||||
func ImageTar(ref *reference.Spec, prefix string, tw tarWriter, resolv string, opts BuildOpts) (e error) {
|
||||
// location is where it is in the linuxkit.yaml file
|
||||
func ImageTar(location string, ref *reference.Spec, prefix string, tw tarWriter, resolv string, opts BuildOpts) (e error) {
|
||||
log.Debugf("image tar: %s %s", ref, prefix)
|
||||
if prefix != "" && prefix[len(prefix)-1] != '/' {
|
||||
return fmt.Errorf("prefix does not end with /: %s", prefix)
|
||||
}
|
||||
|
||||
err := tarPrefix(prefix, tw)
|
||||
err := tarPrefix(prefix, location, ref, tw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -185,12 +199,12 @@ func ImageTar(ref *reference.Spec, prefix string, tw tarWriter, resolv string, o
|
||||
// If pull==true, then it always tries to pull from registry.
|
||||
src, err := imagePull(ref, opts.Pull, opts.CacheDir, opts.DockerCache, opts.Arch)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not pull image %s: %v", ref, err)
|
||||
return fmt.Errorf("could not pull image %s: %v", ref, err)
|
||||
}
|
||||
|
||||
contents, err := src.TarReader()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not unpack image %s: %v", ref, err)
|
||||
return fmt.Errorf("could not unpack image %s: %v", ref, err)
|
||||
}
|
||||
|
||||
defer contents.Close()
|
||||
@ -214,6 +228,12 @@ func ImageTar(ref *reference.Spec, prefix string, tw tarWriter, resolv string, o
|
||||
// force PAX format, since it allows for unlimited Name/Linkname
|
||||
// and we move all files below prefix.
|
||||
hdr.Format = tar.FormatPAX
|
||||
// ensure we record the source of the file in the PAX header
|
||||
if hdr.PAXRecords == nil {
|
||||
hdr.PAXRecords = make(map[string]string)
|
||||
}
|
||||
hdr.PAXRecords[PaxRecordLinuxkitSource] = ref.String()
|
||||
hdr.PAXRecords[PaxRecordLinuxkitLocation] = location
|
||||
if exclude[hdr.Name] {
|
||||
log.Debugf("image tar: %s %s exclude %s", ref, prefix, hdr.Name)
|
||||
_, err = io.Copy(io.Discard, tr)
|
||||
@ -286,6 +306,12 @@ func ImageTar(ref *reference.Spec, prefix string, tw tarWriter, resolv string, o
|
||||
continue
|
||||
}
|
||||
hdr := touch[name]
|
||||
// ensure that we record the source of the file
|
||||
if hdr.PAXRecords == nil {
|
||||
hdr.PAXRecords = make(map[string]string)
|
||||
}
|
||||
hdr.PAXRecords[PaxRecordLinuxkitSource] = ref.String()
|
||||
hdr.PAXRecords[PaxRecordLinuxkitLocation] = location
|
||||
origName := hdr.Name
|
||||
hdr.Name = prefix + origName
|
||||
hdr.Format = tar.FormatPAX
|
||||
@ -329,7 +355,7 @@ func ImageTar(ref *reference.Spec, prefix string, tw tarWriter, resolv string, o
|
||||
}
|
||||
|
||||
// ImageBundle produces an OCI bundle at the given path in a tarball, given an image and a config.json
|
||||
func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runtime, tw tarWriter, readonly bool, dupMap map[string]string, opts BuildOpts) error { // nolint: lll
|
||||
func ImageBundle(prefix, location string, ref *reference.Spec, config []byte, runtime Runtime, tw tarWriter, readonly bool, dupMap map[string]string, opts BuildOpts) error { // nolint: lll
|
||||
// if read only, just unpack in rootfs/ but otherwise set up for overlay
|
||||
rootExtract := "rootfs"
|
||||
if !readonly {
|
||||
@ -340,12 +366,12 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt
|
||||
root := path.Join(prefix, rootExtract)
|
||||
var foundElsewhere = dupMap[ref.String()] != ""
|
||||
if !foundElsewhere {
|
||||
if err := ImageTar(ref, root+"/", tw, "", opts); err != nil {
|
||||
if err := ImageTar(location, ref, root+"/", tw, "", opts); err != nil {
|
||||
return err
|
||||
}
|
||||
dupMap[ref.String()] = root
|
||||
} else {
|
||||
if err := tarPrefix(prefix+"/", tw); err != nil {
|
||||
if err := tarPrefix(prefix+"/", location, ref, tw); err != nil {
|
||||
return err
|
||||
}
|
||||
root = dupMap[ref.String()]
|
||||
@ -357,6 +383,10 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt
|
||||
Size: int64(len(config)),
|
||||
ModTime: defaultModTime,
|
||||
Format: tar.FormatPAX,
|
||||
PAXRecords: map[string]string{
|
||||
PaxRecordLinuxkitSource: ref.String(),
|
||||
PaxRecordLinuxkitLocation: location,
|
||||
},
|
||||
}
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
@ -375,6 +405,10 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt
|
||||
Typeflag: tar.TypeDir,
|
||||
ModTime: defaultModTime,
|
||||
Format: tar.FormatPAX,
|
||||
PAXRecords: map[string]string{
|
||||
PaxRecordLinuxkitSource: ref.String(),
|
||||
PaxRecordLinuxkitLocation: location,
|
||||
},
|
||||
}
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
@ -386,6 +420,10 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt
|
||||
Typeflag: tar.TypeDir,
|
||||
ModTime: defaultModTime,
|
||||
Format: tar.FormatPAX,
|
||||
PAXRecords: map[string]string{
|
||||
PaxRecordLinuxkitSource: ref.String(),
|
||||
PaxRecordLinuxkitLocation: location,
|
||||
},
|
||||
}
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
@ -406,6 +444,10 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt
|
||||
Typeflag: tar.TypeDir,
|
||||
ModTime: defaultModTime,
|
||||
Format: tar.FormatPAX,
|
||||
PAXRecords: map[string]string{
|
||||
PaxRecordLinuxkitSource: ref.String(),
|
||||
PaxRecordLinuxkitLocation: location,
|
||||
},
|
||||
}
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
@ -424,7 +466,7 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt
|
||||
// write the runtime config
|
||||
runtimeConfig, err := json.MarshalIndent(runtime, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to create runtime config for %s: %v", ref, err)
|
||||
return fmt.Errorf("failed to create runtime config for %s: %v", ref, err)
|
||||
}
|
||||
|
||||
hdr = &tar.Header{
|
||||
@ -433,6 +475,10 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt
|
||||
Size: int64(len(runtimeConfig)),
|
||||
ModTime: defaultModTime,
|
||||
Format: tar.FormatPAX,
|
||||
PAXRecords: map[string]string{
|
||||
PaxRecordLinuxkitSource: ref.String(),
|
||||
PaxRecordLinuxkitLocation: location,
|
||||
},
|
||||
}
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
|
31
test/cases/000_build/001_tarheaders/tarheaders.py
Normal file
31
test/cases/000_build/001_tarheaders/tarheaders.py
Normal file
@ -0,0 +1,31 @@
|
||||
import tarfile
|
||||
import sys
|
||||
|
||||
def list_pax_headers(archive_path):
|
||||
# Open the tar archive
|
||||
try:
|
||||
with tarfile.open(archive_path, 'r') as tar:
|
||||
# Iterate over each member in the tar archive
|
||||
for member in tar.getmembers():
|
||||
# ignore the root directory, which just exists
|
||||
if member.name == '.':
|
||||
continue
|
||||
# Check if there are any PAX headers
|
||||
if member.pax_headers:
|
||||
# Check for the specific PAX header
|
||||
if 'LINUXKIT.source' not in member.pax_headers:
|
||||
print(f"File: {member.name} is missing LINUXKIT.source PAX Header.")
|
||||
if 'LINUXKIT.location' not in member.pax_headers:
|
||||
print(f"File: {member.name} is missing LINUXKIT.source PAX Header.")
|
||||
else:
|
||||
print(f"File: {member.name} has No PAX Headers.")
|
||||
except Exception as e:
|
||||
print("Failed to read tar archive:", e)
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: python list_pax_headers.py <archive.tar>")
|
||||
sys.exit(1)
|
||||
archive_filename = sys.argv[1]
|
||||
list_pax_headers(archive_filename)
|
38
test/cases/000_build/001_tarheaders/test.sh
Normal file
38
test/cases/000_build/001_tarheaders/test.sh
Normal file
@ -0,0 +1,38 @@
|
||||
#!/bin/sh
|
||||
# SUMMARY: Check that tar output format build contains proper headers for each file
|
||||
# LABELS:
|
||||
|
||||
set -e
|
||||
|
||||
# Source libraries. Uncomment if needed/defined
|
||||
#. "${RT_LIB}"
|
||||
. "${RT_PROJECT_ROOT}/_lib/lib.sh"
|
||||
|
||||
NAME=tarheaders
|
||||
|
||||
clean_up() {
|
||||
rm -f ${NAME}*
|
||||
}
|
||||
|
||||
trap clean_up EXIT
|
||||
|
||||
# do not include the sbom, because the SBoM unique IDs per file/package are *not* deterministic,
|
||||
# (currently based upon syft), and thus will make the file non-reproducible
|
||||
linuxkit build --no-sbom --format tar --name "${NAME}" ./test.yml
|
||||
|
||||
# Check that the tarball contains the expected headers
|
||||
# see that python is installed
|
||||
PYTHON=
|
||||
if which python ; then PYTHON=python ; elif which python3; then PYTHON=python3 ; else
|
||||
echo "Failed to find any executable python or python3"
|
||||
exit 1
|
||||
fi
|
||||
FAILED=$(python ./tarheaders.py "${NAME}.tar")
|
||||
|
||||
if [ -n "${FAILED}" ]; then
|
||||
echo "Failed to find linuxkit.packagesource headers for the following files:"
|
||||
echo "${FAILED}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
46
test/cases/000_build/001_tarheaders/test.yml
Normal file
46
test/cases/000_build/001_tarheaders/test.yml
Normal file
@ -0,0 +1,46 @@
|
||||
# NOTE: Images build from this file likely do not run
|
||||
kernel:
|
||||
image: linuxkit/kernel:6.6.13
|
||||
cmdline: "console=ttyS0"
|
||||
init:
|
||||
- linuxkit/init:45a1ad5919f0b6acf0f0cf730e9434abfae11fe6
|
||||
- linuxkit/runc:6062483d748609d505f2bcde4e52ee64a3329f5f
|
||||
- linuxkit/containerd:e7a92d9f3282039eac5fb1b07cac2b8664cbf0ad
|
||||
|
||||
onboot:
|
||||
- name: dhcpcd
|
||||
image: linuxkit/dhcpcd:e9e3580f2de00e73e7b316a007186d22fea056ee
|
||||
command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"]
|
||||
# Add some random unsorted caps
|
||||
capabilities:
|
||||
- CAP_SETGID
|
||||
- CAP_DAC_OVERRIDE
|
||||
|
||||
services:
|
||||
- name: testservice
|
||||
image: linuxkit/ip:bb250017b05de5e16ac436b1eb19a39c87b5a252
|
||||
# Some environments
|
||||
env:
|
||||
- BENV=true
|
||||
- ARANDOMENV=foobar
|
||||
# Some mounts
|
||||
mounts:
|
||||
- type: cgroup
|
||||
options: ["rw","nosuid","noexec","nodev","relatime"]
|
||||
- type: overlay
|
||||
source: overlay
|
||||
destination: writeable-host-etc
|
||||
options: ["rw", "lowerdir=/etc", "upperdir=/run/hostetc/upper", "workdir=/run/hostetc/work"]
|
||||
# Some binds
|
||||
binds:
|
||||
- /var/run:/var/run
|
||||
- /foobar:/foobar
|
||||
- /etc/foobar:/etc/foobar
|
||||
- /etc/aaa:/etc/aaa
|
||||
# And some runtime settings
|
||||
runtime:
|
||||
mkdir: ["/var/lib/docker","/var/lib/aaa"]
|
||||
|
||||
files:
|
||||
- path: etc/linuxkit-config
|
||||
metadata: yaml
|
Loading…
Reference in New Issue
Block a user