mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-07-19 09:16:29 +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
|
// 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,
|
// it can be called multiple times and will do the union of all such databases,
|
||||||
// rather than overwriting the previous one.
|
// 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"
|
const apkInstalledPath = "lib/apk/db/installed"
|
||||||
|
|
||||||
type apkTarWriter struct {
|
type apkTarWriter struct {
|
||||||
*tar.Writer
|
*tar.Writer
|
||||||
dbs [][]byte
|
dbs [][]byte
|
||||||
current *bytes.Buffer
|
current *bytes.Buffer
|
||||||
|
location string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAPKTarWriter(w *tar.Writer) *apkTarWriter {
|
func newAPKTarWriter(w *tar.Writer, location string) *apkTarWriter {
|
||||||
return &apkTarWriter{
|
return &apkTarWriter{
|
||||||
Writer: w,
|
Writer: w,
|
||||||
|
location: location,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,6 +71,10 @@ func (a *apkTarWriter) WriteAPKDB() error {
|
|||||||
Gid: 0,
|
Gid: 0,
|
||||||
Typeflag: tar.TypeReg,
|
Typeflag: tar.TypeReg,
|
||||||
Size: int64(size),
|
Size: int64(size),
|
||||||
|
PAXRecords: map[string]string{
|
||||||
|
PaxRecordLinuxkitSource: "LINUXKIT.apkinit",
|
||||||
|
PaxRecordLinuxkitLocation: a.location,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if err := a.Writer.WriteHeader(hdr); err != nil {
|
if err := a.Writer.WriteHeader(hdr); err != nil {
|
||||||
return err
|
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)
|
src, err := imagePull(&ref, opts.Pull, opts.CacheDir, opts.DockerCache, opts.Arch)
|
||||||
if err != nil {
|
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()
|
configRaw, err := src.Config()
|
||||||
if err != nil {
|
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)
|
oci, runtime, err := ConfigToOCI(image, configRaw, idMap)
|
||||||
if err != nil {
|
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, "", " ")
|
config, err := json.MarshalIndent(oci, "", " ")
|
||||||
if err != nil {
|
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)
|
path := path.Join("containers", section, prefix+image.Name)
|
||||||
readonly := oci.Root.Readonly
|
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 {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
@ -153,14 +153,14 @@ func Build(m Moby, w io.Writer, opts BuildOpts) error {
|
|||||||
if m.Kernel.ref != nil {
|
if m.Kernel.ref != nil {
|
||||||
// get kernel and initrd tarball and ucode cpio archive from container
|
// get kernel and initrd tarball and ucode cpio archive from container
|
||||||
log.Infof("Extract kernel image: %s", m.Kernel.ref)
|
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)
|
kf := newKernelFilter(m.Kernel.ref, iw, m.Kernel.Cmdline, m.Kernel.Binary, m.Kernel.Tar, m.Kernel.UCode, opts.DecompressKernel)
|
||||||
err := ImageTar(m.Kernel.ref, "", kf, "", opts)
|
err := ImageTar("kernel", m.Kernel.ref, "", kf, "", opts)
|
||||||
if err != nil {
|
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()
|
err = kf.Close()
|
||||||
if err != nil {
|
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 {
|
if len(m.Init) != 0 {
|
||||||
log.Infof("Add init containers:")
|
log.Infof("Add init containers:")
|
||||||
}
|
}
|
||||||
apkTar := newAPKTarWriter(iw)
|
apkTar := newAPKTarWriter(iw, "init")
|
||||||
for _, ii := range m.initRefs {
|
for _, ii := range m.initRefs {
|
||||||
log.Infof("Process init image: %s", ii)
|
log.Infof("Process init image: %s", ii)
|
||||||
err := ImageTar(ii, "", apkTar, resolvconfSymlink, opts)
|
err := ImageTar("init", ii, "", apkTar, resolvconfSymlink, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to build init tarball from %s: %v", ii, err)
|
return fmt.Errorf("failed to build init tarball from %s: %v", ii, err)
|
||||||
}
|
}
|
||||||
@ -252,9 +252,10 @@ type kernelFilter struct {
|
|||||||
foundKernel bool
|
foundKernel bool
|
||||||
foundKTar bool
|
foundKTar bool
|
||||||
foundUCode 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", ""
|
tarName, kernelName, ucodeName := "kernel.tar", "kernel", ""
|
||||||
if tar != nil {
|
if tar != nil {
|
||||||
tarName = *tar
|
tarName = *tar
|
||||||
@ -268,7 +269,7 @@ func newKernelFilter(tw *tar.Writer, cmdline string, kernel string, tar, ucode *
|
|||||||
if ucode != nil {
|
if ucode != nil {
|
||||||
ucodeName = *ucode
|
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 {
|
func (k *kernelFilter) finishTar() error {
|
||||||
@ -299,7 +300,7 @@ func (k *kernelFilter) finishTar() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tr := tar.NewReader(k.buffer)
|
tr := tar.NewReader(k.buffer)
|
||||||
err := tarAppend(k.tw, tr)
|
err := tarAppend(k.ref, k.tw, tr)
|
||||||
k.buffer = nil
|
k.buffer = nil
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -353,6 +354,7 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error {
|
|||||||
Typeflag: tar.TypeDir,
|
Typeflag: tar.TypeDir,
|
||||||
ModTime: defaultModTime,
|
ModTime: defaultModTime,
|
||||||
Format: tar.FormatPAX,
|
Format: tar.FormatPAX,
|
||||||
|
PAXRecords: hdr.PAXRecords,
|
||||||
}
|
}
|
||||||
if err := tw.WriteHeader(whdr); err != nil {
|
if err := tw.WriteHeader(whdr); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -365,6 +367,7 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error {
|
|||||||
Size: int64(len(k.cmdline)),
|
Size: int64(len(k.cmdline)),
|
||||||
ModTime: defaultModTime,
|
ModTime: defaultModTime,
|
||||||
Format: tar.FormatPAX,
|
Format: tar.FormatPAX,
|
||||||
|
PAXRecords: hdr.PAXRecords,
|
||||||
}
|
}
|
||||||
if err := tw.WriteHeader(whdr); err != nil {
|
if err := tw.WriteHeader(whdr); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -380,6 +383,7 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error {
|
|||||||
Size: hdr.Size,
|
Size: hdr.Size,
|
||||||
ModTime: defaultModTime,
|
ModTime: defaultModTime,
|
||||||
Format: tar.FormatPAX,
|
Format: tar.FormatPAX,
|
||||||
|
PAXRecords: hdr.PAXRecords,
|
||||||
}
|
}
|
||||||
k.buffer = new(bytes.Buffer)
|
k.buffer = new(bytes.Buffer)
|
||||||
case k.tar:
|
case k.tar:
|
||||||
@ -397,6 +401,7 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error {
|
|||||||
Typeflag: tar.TypeDir,
|
Typeflag: tar.TypeDir,
|
||||||
ModTime: defaultModTime,
|
ModTime: defaultModTime,
|
||||||
Format: tar.FormatPAX,
|
Format: tar.FormatPAX,
|
||||||
|
PAXRecords: hdr.PAXRecords,
|
||||||
}
|
}
|
||||||
if err := tw.WriteHeader(whdr); err != nil {
|
if err := tw.WriteHeader(whdr); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -408,6 +413,7 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error {
|
|||||||
Size: hdr.Size,
|
Size: hdr.Size,
|
||||||
ModTime: defaultModTime,
|
ModTime: defaultModTime,
|
||||||
Format: tar.FormatPAX,
|
Format: tar.FormatPAX,
|
||||||
|
PAXRecords: hdr.PAXRecords,
|
||||||
}
|
}
|
||||||
if err := tw.WriteHeader(whdr); err != nil {
|
if err := tw.WriteHeader(whdr); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -419,7 +425,7 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func tarAppend(iw *tar.Writer, tr *tar.Reader) error {
|
func tarAppend(ref *reference.Spec, iw *tar.Writer, tr *tar.Reader) error {
|
||||||
for {
|
for {
|
||||||
hdr, err := tr.Next()
|
hdr, err := tr.Next()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
@ -428,6 +434,12 @@ func tarAppend(iw *tar.Writer, tr *tar.Reader) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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)
|
err = iw.WriteHeader(hdr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -478,7 +490,7 @@ func decompressKernel(src *bytes.Buffer) (*bytes.Buffer, error) {
|
|||||||
versionMajor := int(s[versionIdx])
|
versionMajor := int(s[versionIdx])
|
||||||
versionMinor := int(s[versionIdx+1])
|
versionMinor := int(s[versionIdx+1])
|
||||||
if versionMajor < 2 && versionMinor < 8 {
|
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])
|
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)
|
log.Debugf("bzImage: Payload at Offset: %d Length: %d", payloadOff, payloadLen)
|
||||||
|
|
||||||
if len(s) < int(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)) {
|
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]))
|
return gunzip(bytes.NewBuffer(s[payloadOff : payloadOff+payloadLen]))
|
||||||
}
|
}
|
||||||
// TODO(rn): Add more supported formats
|
// 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) {
|
func gunzip(src *bytes.Buffer) (*bytes.Buffer, error) {
|
||||||
@ -529,7 +541,7 @@ func metadata(m Moby, md string) ([]byte, error) {
|
|||||||
case "yaml":
|
case "yaml":
|
||||||
return yaml.Marshal(m)
|
return yaml.Marshal(m)
|
||||||
default:
|
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 {
|
if len(m.Files) != 0 {
|
||||||
log.Infof("Add files:")
|
log.Infof("Add files:")
|
||||||
}
|
}
|
||||||
for _, f := range m.Files {
|
for filecount, f := range m.Files {
|
||||||
log.Infof(" %s", f.Path)
|
log.Infof(" %s", f.Path)
|
||||||
if 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
|
// tar archives should not have absolute paths
|
||||||
if f.Path[0] == '/' {
|
if f.Path[0] == '/' {
|
||||||
@ -557,7 +569,7 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error {
|
|||||||
var err error
|
var err error
|
||||||
mode, err = strconv.ParseInt(f.Mode, 8, 32)
|
mode, err = strconv.ParseInt(f.Mode, 8, 32)
|
||||||
if err != nil {
|
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
|
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.Directory && f.Symlink == "" && f.Contents == nil {
|
||||||
if f.Source == "" && f.Metadata == "" {
|
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 != "" {
|
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 != "" {
|
if f.Source != "" {
|
||||||
source := 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)
|
_, err := os.Stat(source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// skip if not found or readable
|
// 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
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -617,10 +629,10 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if f.Metadata != "" {
|
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 != "" {
|
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
|
// 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),
|
Uid: int(uid),
|
||||||
Gid: int(gid),
|
Gid: int(gid),
|
||||||
Format: tar.FormatPAX,
|
Format: tar.FormatPAX,
|
||||||
|
PAXRecords: map[string]string{
|
||||||
|
PaxRecordLinuxkitSource: "linuxkit.files",
|
||||||
|
PaxRecordLinuxkitLocation: fmt.Sprintf("files[%d]", filecount),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
err := tw.WriteHeader(hdr)
|
err := tw.WriteHeader(hdr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -660,10 +676,14 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error {
|
|||||||
Uid: int(uid),
|
Uid: int(uid),
|
||||||
Gid: int(gid),
|
Gid: int(gid),
|
||||||
Format: tar.FormatPAX,
|
Format: tar.FormatPAX,
|
||||||
|
PAXRecords: map[string]string{
|
||||||
|
PaxRecordLinuxkitSource: "linuxkit.files",
|
||||||
|
PaxRecordLinuxkitLocation: fmt.Sprintf("files[%d]", filecount),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if f.Directory {
|
if f.Directory {
|
||||||
if f.Contents != nil {
|
if f.Contents != nil {
|
||||||
return errors.New("Directory with contents not allowed")
|
return errors.New("directory with contents not allowed")
|
||||||
}
|
}
|
||||||
hdr.Typeflag = tar.TypeDir
|
hdr.Typeflag = tar.TypeDir
|
||||||
err := tw.WriteHeader(hdr)
|
err := tw.WriteHeader(hdr)
|
||||||
|
@ -14,6 +14,14 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
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 {
|
type tarWriter interface {
|
||||||
Close() error
|
Close() error
|
||||||
Flush() error
|
Flush() error
|
||||||
@ -140,7 +148,8 @@ var touch = map[string]tar.Header{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// tarPrefix creates the leading directories for a path
|
// 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 == "" {
|
if path == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -160,6 +169,10 @@ func tarPrefix(path string, tw tarWriter) error {
|
|||||||
ModTime: defaultModTime,
|
ModTime: defaultModTime,
|
||||||
Typeflag: tar.TypeDir,
|
Typeflag: tar.TypeDir,
|
||||||
Format: tar.FormatPAX,
|
Format: tar.FormatPAX,
|
||||||
|
PAXRecords: map[string]string{
|
||||||
|
PaxRecordLinuxkitSource: ref.String(),
|
||||||
|
PaxRecordLinuxkitLocation: location,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if err := tw.WriteHeader(hdr); err != nil {
|
if err := tw.WriteHeader(hdr); err != nil {
|
||||||
return err
|
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
|
// 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)
|
log.Debugf("image tar: %s %s", ref, prefix)
|
||||||
if prefix != "" && prefix[len(prefix)-1] != '/' {
|
if prefix != "" && prefix[len(prefix)-1] != '/' {
|
||||||
return fmt.Errorf("prefix does not end with /: %s", prefix)
|
return fmt.Errorf("prefix does not end with /: %s", prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := tarPrefix(prefix, tw)
|
err := tarPrefix(prefix, location, ref, tw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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.
|
// If pull==true, then it always tries to pull from registry.
|
||||||
src, err := imagePull(ref, opts.Pull, opts.CacheDir, opts.DockerCache, opts.Arch)
|
src, err := imagePull(ref, opts.Pull, opts.CacheDir, opts.DockerCache, opts.Arch)
|
||||||
if err != nil {
|
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()
|
contents, err := src.TarReader()
|
||||||
if err != nil {
|
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()
|
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
|
// force PAX format, since it allows for unlimited Name/Linkname
|
||||||
// and we move all files below prefix.
|
// and we move all files below prefix.
|
||||||
hdr.Format = tar.FormatPAX
|
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] {
|
if exclude[hdr.Name] {
|
||||||
log.Debugf("image tar: %s %s exclude %s", ref, prefix, hdr.Name)
|
log.Debugf("image tar: %s %s exclude %s", ref, prefix, hdr.Name)
|
||||||
_, err = io.Copy(io.Discard, tr)
|
_, err = io.Copy(io.Discard, tr)
|
||||||
@ -286,6 +306,12 @@ func ImageTar(ref *reference.Spec, prefix string, tw tarWriter, resolv string, o
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
hdr := touch[name]
|
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
|
origName := hdr.Name
|
||||||
hdr.Name = prefix + origName
|
hdr.Name = prefix + origName
|
||||||
hdr.Format = tar.FormatPAX
|
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
|
// 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
|
// if read only, just unpack in rootfs/ but otherwise set up for overlay
|
||||||
rootExtract := "rootfs"
|
rootExtract := "rootfs"
|
||||||
if !readonly {
|
if !readonly {
|
||||||
@ -340,12 +366,12 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt
|
|||||||
root := path.Join(prefix, rootExtract)
|
root := path.Join(prefix, rootExtract)
|
||||||
var foundElsewhere = dupMap[ref.String()] != ""
|
var foundElsewhere = dupMap[ref.String()] != ""
|
||||||
if !foundElsewhere {
|
if !foundElsewhere {
|
||||||
if err := ImageTar(ref, root+"/", tw, "", opts); err != nil {
|
if err := ImageTar(location, ref, root+"/", tw, "", opts); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
dupMap[ref.String()] = root
|
dupMap[ref.String()] = root
|
||||||
} else {
|
} else {
|
||||||
if err := tarPrefix(prefix+"/", tw); err != nil {
|
if err := tarPrefix(prefix+"/", location, ref, tw); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
root = dupMap[ref.String()]
|
root = dupMap[ref.String()]
|
||||||
@ -357,6 +383,10 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt
|
|||||||
Size: int64(len(config)),
|
Size: int64(len(config)),
|
||||||
ModTime: defaultModTime,
|
ModTime: defaultModTime,
|
||||||
Format: tar.FormatPAX,
|
Format: tar.FormatPAX,
|
||||||
|
PAXRecords: map[string]string{
|
||||||
|
PaxRecordLinuxkitSource: ref.String(),
|
||||||
|
PaxRecordLinuxkitLocation: location,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if err := tw.WriteHeader(hdr); err != nil {
|
if err := tw.WriteHeader(hdr); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -375,6 +405,10 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt
|
|||||||
Typeflag: tar.TypeDir,
|
Typeflag: tar.TypeDir,
|
||||||
ModTime: defaultModTime,
|
ModTime: defaultModTime,
|
||||||
Format: tar.FormatPAX,
|
Format: tar.FormatPAX,
|
||||||
|
PAXRecords: map[string]string{
|
||||||
|
PaxRecordLinuxkitSource: ref.String(),
|
||||||
|
PaxRecordLinuxkitLocation: location,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if err := tw.WriteHeader(hdr); err != nil {
|
if err := tw.WriteHeader(hdr); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -386,6 +420,10 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt
|
|||||||
Typeflag: tar.TypeDir,
|
Typeflag: tar.TypeDir,
|
||||||
ModTime: defaultModTime,
|
ModTime: defaultModTime,
|
||||||
Format: tar.FormatPAX,
|
Format: tar.FormatPAX,
|
||||||
|
PAXRecords: map[string]string{
|
||||||
|
PaxRecordLinuxkitSource: ref.String(),
|
||||||
|
PaxRecordLinuxkitLocation: location,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if err := tw.WriteHeader(hdr); err != nil {
|
if err := tw.WriteHeader(hdr); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -406,6 +444,10 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt
|
|||||||
Typeflag: tar.TypeDir,
|
Typeflag: tar.TypeDir,
|
||||||
ModTime: defaultModTime,
|
ModTime: defaultModTime,
|
||||||
Format: tar.FormatPAX,
|
Format: tar.FormatPAX,
|
||||||
|
PAXRecords: map[string]string{
|
||||||
|
PaxRecordLinuxkitSource: ref.String(),
|
||||||
|
PaxRecordLinuxkitLocation: location,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if err := tw.WriteHeader(hdr); err != nil {
|
if err := tw.WriteHeader(hdr); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -424,7 +466,7 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt
|
|||||||
// write the runtime config
|
// write the runtime config
|
||||||
runtimeConfig, err := json.MarshalIndent(runtime, "", " ")
|
runtimeConfig, err := json.MarshalIndent(runtime, "", " ")
|
||||||
if err != nil {
|
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{
|
hdr = &tar.Header{
|
||||||
@ -433,6 +475,10 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt
|
|||||||
Size: int64(len(runtimeConfig)),
|
Size: int64(len(runtimeConfig)),
|
||||||
ModTime: defaultModTime,
|
ModTime: defaultModTime,
|
||||||
Format: tar.FormatPAX,
|
Format: tar.FormatPAX,
|
||||||
|
PAXRecords: map[string]string{
|
||||||
|
PaxRecordLinuxkitSource: ref.String(),
|
||||||
|
PaxRecordLinuxkitLocation: location,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if err := tw.WriteHeader(hdr); err != nil {
|
if err := tw.WriteHeader(hdr); err != nil {
|
||||||
return err
|
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