mirror of https://github.com/containers/image.git
235 lines
5.7 KiB
Go
235 lines
5.7 KiB
Go
//go:build linux
|
|
// +build linux
|
|
|
|
package internal
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/sylabs/sif/v2/pkg/sif"
|
|
|
|
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
|
)
|
|
|
|
type SifImage struct {
|
|
fimg *sif.FileImage
|
|
rootfs sif.Descriptor
|
|
deffile *sif.Descriptor
|
|
defReader io.Reader
|
|
cmdlist []string
|
|
runscript *bytes.Buffer
|
|
env *sif.Descriptor
|
|
envReader io.Reader
|
|
envlist []string
|
|
}
|
|
|
|
func LoadSIFImage(path string) (image SifImage, err error) {
|
|
// open up the SIF file and get its header
|
|
image.fimg, err = sif.LoadContainerFromPath(path, sif.OptLoadWithFlag(os.O_RDONLY))
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// check for a system partition and save it
|
|
image.rootfs, err = image.fimg.GetDescriptor(sif.WithPartitionType(sif.PartPrimSys))
|
|
if err != nil {
|
|
return SifImage{}, errors.Wrap(err, "looking up rootfs from SIF file")
|
|
}
|
|
|
|
// look for a definition file object
|
|
resultDesc, err := image.fimg.GetDescriptor(sif.WithDataType(sif.DataDeffile))
|
|
if err == nil {
|
|
// we assume in practice that typical SIF files don't hold multiple deffiles
|
|
image.deffile = &resultDesc
|
|
image.defReader = resultDesc.GetReader()
|
|
}
|
|
if err = image.generateConfig(); err != nil {
|
|
return SifImage{}, err
|
|
}
|
|
|
|
// look for an environment variable set object
|
|
resultDesc, err = image.fimg.GetDescriptor(sif.WithDataType(sif.DataEnvVar))
|
|
if err == nil {
|
|
// we assume in practice that typical SIF files don't hold multiple EnvVar sets
|
|
image.env = &resultDesc
|
|
image.envReader = resultDesc.GetReader()
|
|
}
|
|
|
|
return image, nil
|
|
}
|
|
|
|
func (image *SifImage) parseEnvironment(scanner *bufio.Scanner) error {
|
|
for scanner.Scan() {
|
|
s := strings.TrimSpace(scanner.Text())
|
|
if s == "" || strings.HasPrefix(s, "#") {
|
|
continue
|
|
}
|
|
if strings.HasPrefix(s, "%") {
|
|
return nil
|
|
}
|
|
image.envlist = append(image.envlist, s)
|
|
}
|
|
if err := scanner.Err(); err != nil {
|
|
return errors.Wrap(err, "parsing environment from SIF definition file object")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (image *SifImage) parseRunscript(scanner *bufio.Scanner) error {
|
|
for scanner.Scan() {
|
|
s := strings.TrimSpace(scanner.Text())
|
|
if strings.HasPrefix(s, "%") {
|
|
return nil
|
|
}
|
|
image.cmdlist = append(image.cmdlist, s)
|
|
}
|
|
if err := scanner.Err(); err != nil {
|
|
return errors.Wrap(err, "parsing runscript from SIF definition file object")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (image *SifImage) generateRunscript() error {
|
|
base := `#!/bin/bash
|
|
`
|
|
image.runscript = bytes.NewBufferString(base)
|
|
for _, s := range image.envlist {
|
|
_, err := image.runscript.WriteString(fmt.Sprintln(s))
|
|
if err != nil {
|
|
return errors.Wrap(err, "writing to runscript buffer")
|
|
}
|
|
}
|
|
for _, s := range image.cmdlist {
|
|
_, err := image.runscript.WriteString(fmt.Sprintln(s))
|
|
if err != nil {
|
|
return errors.Wrap(err, "writing to runscript buffer")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (image *SifImage) generateConfig() error {
|
|
if image.deffile == nil {
|
|
image.cmdlist = append(image.cmdlist, "bash")
|
|
return nil
|
|
}
|
|
|
|
// extract %environment/%runscript from definition file
|
|
var err error
|
|
scanner := bufio.NewScanner(image.defReader)
|
|
for scanner.Scan() {
|
|
s := strings.TrimSpace(scanner.Text())
|
|
again:
|
|
if s == `%environment` {
|
|
if err = image.parseEnvironment(scanner); err != nil {
|
|
return err
|
|
}
|
|
} else if s == `%runscript` {
|
|
if err = image.parseRunscript(scanner); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
s = strings.TrimSpace(scanner.Text())
|
|
if s == `%environment` || s == `%runscript` {
|
|
goto again
|
|
}
|
|
}
|
|
if err := scanner.Err(); err != nil {
|
|
return errors.Wrap(err, "reading lines from SIF definition file object")
|
|
}
|
|
|
|
if len(image.cmdlist) == 0 && len(image.envlist) == 0 {
|
|
image.cmdlist = append(image.cmdlist, "bash")
|
|
} else {
|
|
if err = image.generateRunscript(); err != nil {
|
|
return errors.Wrap(err, "generating runscript")
|
|
}
|
|
image.cmdlist = []string{"/podman/runscript"}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (image SifImage) GetConfig(config *imgspecv1.Image) error {
|
|
config.Config.Cmd = append(config.Config.Cmd, image.cmdlist...)
|
|
return nil
|
|
}
|
|
|
|
func (image SifImage) UnloadSIFImage() (err error) {
|
|
err = image.fimg.UnloadContainer()
|
|
return
|
|
}
|
|
|
|
func (image SifImage) GetSIFID() string {
|
|
return image.fimg.ID()
|
|
}
|
|
|
|
func (image SifImage) GetSIFArch() string {
|
|
return image.fimg.PrimaryArch()
|
|
}
|
|
|
|
const squashFilename = "rootfs.squashfs"
|
|
const tarFilename = "rootfs.tar"
|
|
|
|
func runUnSquashFSTar(tempdir string) (err error) {
|
|
script := `
|
|
#!/bin/sh
|
|
unsquashfs -f ` + squashFilename + ` && tar --acls --xattrs -C ./squashfs-root -cpf ` + tarFilename + ` ./
|
|
`
|
|
|
|
if err = ioutil.WriteFile(filepath.Join(tempdir, "script"), []byte(script), 0755); err != nil {
|
|
return err
|
|
}
|
|
cmd := []string{"fakeroot", "--", "./script"}
|
|
|
|
xcmd := exec.Command(cmd[0], cmd[1:]...)
|
|
xcmd.Stderr = os.Stderr
|
|
xcmd.Dir = tempdir
|
|
err = xcmd.Run()
|
|
return
|
|
}
|
|
|
|
func (image *SifImage) writeRunscript(tempdir string) (err error) {
|
|
if image.runscript == nil {
|
|
return nil
|
|
}
|
|
rsPath := filepath.Join(tempdir, "squashfs-root", "podman")
|
|
if err = os.MkdirAll(rsPath, 0755); err != nil {
|
|
return
|
|
}
|
|
if err = ioutil.WriteFile(filepath.Join(rsPath, "runscript"), image.runscript.Bytes(), 0755); err != nil {
|
|
return errors.Wrap(err, "writing /podman/runscript")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (image SifImage) SquashFSToTarLayer(tempdir string) (tarpath string, err error) {
|
|
f, err := os.Create(filepath.Join(tempdir, squashFilename))
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer f.Close()
|
|
if _, err = io.CopyN(f, image.rootfs.GetReader(), image.rootfs.Size()); err != nil {
|
|
return
|
|
}
|
|
if err = f.Sync(); err != nil {
|
|
return
|
|
}
|
|
if err = image.writeRunscript(tempdir); err != nil {
|
|
return
|
|
}
|
|
if err = runUnSquashFSTar(tempdir); err != nil {
|
|
return
|
|
}
|
|
return filepath.Join(tempdir, tarFilename), nil
|
|
}
|