luet/vendor/github.com/mudler/docker-companion/api/unpack.go
Ettore Di Giacinto 420186b7db
Switch to go mod
2019-11-10 18:05:28 +01:00

158 lines
3.7 KiB
Go

package api
import (
"fmt"
"io"
"io/ioutil"
"os"
"os/signal"
"path/filepath"
"syscall"
"github.com/docker/docker/pkg/archive"
docker "github.com/fsouza/go-dockerclient"
jww "github.com/spf13/jwalterweatherman"
)
// SEPARATOR contains system-specific separator
const SEPARATOR = string(filepath.Separator)
// ROOTFS is our temporary rootfs path
const ROOTFS = "." + SEPARATOR + "rootfs_overlay"
// Unpack unpacks a docker image into a path
func Unpack(client *docker.Client, image string, dirname string, fatal bool) error {
var err error
r, w := io.Pipe()
if dirname == "" {
dirname = ROOTFS
}
os.MkdirAll(dirname, 0777)
filename, err := ioutil.TempFile(os.TempDir(), "artemide")
if err != nil {
return fmt.Errorf("Couldn't create the temporary file")
}
os.Remove(filename.Name())
jww.INFO.Println("Creating container")
container, err := client.CreateContainer(docker.CreateContainerOptions{
Config: &docker.Config{
Image: image,
Cmd: []string{"true"},
},
})
if err != nil {
jww.FATAL.Fatalln("Couldn't export container, sorry", err)
}
defer func(*docker.Container) {
client.RemoveContainer(docker.RemoveContainerOptions{
ID: container.ID,
Force: true,
})
}(container)
signalchan := make(chan os.Signal, 1)
signal.Notify(signalchan,
syscall.SIGINT,
syscall.SIGTERM,
syscall.SIGQUIT)
go func() {
for {
s := <-signalchan
switch s {
case syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT:
jww.WARN.Println("SIGTERM/SIGINT/SIGQUIT detected, removing pending containers")
client.RemoveContainer(docker.RemoveContainerOptions{
ID: container.ID,
Force: true,
})
}
}
}()
// writing without a reader will deadlock so write in a goroutine
go func() {
// it is important to close the writer or reading from the other end of the
// pipe will never finish
defer w.Close()
err := client.ExportContainer(docker.ExportContainerOptions{ID: container.ID, OutputStream: w})
if err != nil {
jww.FATAL.Fatalln("Couldn't export container, sorry", err)
}
}()
jww.INFO.Println("Extracting to", dirname)
err = Untar(r, dirname, true)
if err != nil {
return fmt.Errorf("could not unpack to " + dirname)
}
err = prepareRootfs(dirname, fatal)
return err
}
func prepareRootfs(dirname string, fatal bool) error {
_, err := os.Stat(dirname + SEPARATOR + ".dockerenv")
if err == nil {
err = os.Remove(dirname + SEPARATOR + ".dockerenv")
if err != nil {
if fatal == true {
return fmt.Errorf("could not remove docker env file")
} else {
jww.WARN.Println("error on remove .dockerenv, extracting anyway")
}
}
}
_, err = os.Stat(dirname + SEPARATOR + ".dockerinit")
if err == nil {
err = os.Remove(dirname + SEPARATOR + ".dockerinit")
if err != nil {
if fatal == true {
return fmt.Errorf("could not remove docker init file")
} else {
jww.WARN.Println("error on remove .dockerinit, extracting anyway")
}
}
}
err = os.MkdirAll(dirname+SEPARATOR+"dev", 0751)
if err != nil {
if fatal == true {
return fmt.Errorf("could not create dev folder")
} else {
jww.WARN.Println("could not create dev folder")
}
}
// Google DNS as default
d1 := []byte("nameserver 8.8.8.8\nnameserver 8.8.4.4\n")
err = ioutil.WriteFile(dirname+SEPARATOR+"etc"+SEPARATOR+"resolv.conf", d1, 0644)
if err != nil {
if fatal == true {
return fmt.Errorf("could not write resolv.conf file")
} else {
jww.WARN.Println("could not create resolv.conf file")
}
}
return nil
}
// Untar just a wrapper around the docker functions
func Untar(in io.Reader, dest string, sameOwner bool) error {
return archive.Untar(in, dest, &archive.TarOptions{
NoLchown: !sameOwner,
ExcludePatterns: []string{"dev/"}, // prevent 'operation not permitted'
})
}