1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
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'
})
}
|