mirror of
https://github.com/linka-cloud/d2vm.git
synced 2024-11-25 09:06:24 +00:00
d2vm: flatten docker image using github.com/google/go-containerregistry
This allows to preserve files like /etc/hostname or /etc/resolv.conf that will otherwise be overriden by running the container to extract rootfs wip img entrypoint script Signed-off-by: Adphi <philippe.adrien.nousse@gmail.com>
This commit is contained in:
parent
0c9bfb6dd8
commit
20ba409039
54
builder.go
54
builder.go
@ -18,7 +18,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"os"
|
"os"
|
||||||
exec2 "os/exec"
|
exec2 "os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -85,10 +84,15 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
perm os.FileMode = 0644
|
||||||
|
)
|
||||||
|
|
||||||
type builder struct {
|
type builder struct {
|
||||||
osRelease OSRelease
|
osRelease OSRelease
|
||||||
|
|
||||||
src string
|
src string
|
||||||
|
img *image
|
||||||
diskRaw string
|
diskRaw string
|
||||||
diskOut string
|
diskOut string
|
||||||
format string
|
format string
|
||||||
@ -103,7 +107,7 @@ type builder struct {
|
|||||||
diskUUD string
|
diskUUD string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBuilder(workdir, src, disk string, size int64, osRelease OSRelease, format string) (*builder, error) {
|
func NewBuilder(ctx context.Context, workdir, imgTag, disk string, size int64, osRelease OSRelease, format string) (*builder, error) {
|
||||||
if err := checkDependencies(); err != nil {
|
if err := checkDependencies(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -134,18 +138,22 @@ func NewBuilder(workdir, src, disk string, size int64, osRelease OSRelease, form
|
|||||||
if disk == "" {
|
if disk == "" {
|
||||||
disk = "disk0"
|
disk = "disk0"
|
||||||
}
|
}
|
||||||
i, err := os.Stat(src)
|
img, err := NewImage(ctx, imgTag, workdir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if i.Size() > size {
|
// i, err := os.Stat(imgTar)
|
||||||
s := datasize.ByteSize(math.Ceil(datasize.ByteSize(i.Size()).GBytes())) * datasize.GB
|
// if err != nil {
|
||||||
logrus.Warnf("%s is smaller than rootfs size, using %s", datasize.ByteSize(size), s)
|
// return nil, err
|
||||||
size = int64(s)
|
// }
|
||||||
}
|
// if i.Size() > size {
|
||||||
|
// s := datasize.ByteSize(math.Ceil(datasize.ByteSize(i.Size()).GBytes())) * datasize.GB
|
||||||
|
// logrus.Warnf("%s is smaller than rootfs size, using %s", datasize.ByteSize(size), s)
|
||||||
|
// size = int64(s)
|
||||||
|
// }
|
||||||
b := &builder{
|
b := &builder{
|
||||||
osRelease: osRelease,
|
osRelease: osRelease,
|
||||||
src: src,
|
img: img,
|
||||||
diskRaw: filepath.Join(workdir, disk+".raw"),
|
diskRaw: filepath.Join(workdir, disk+".raw"),
|
||||||
diskOut: filepath.Join(workdir, disk+".qcow2"),
|
diskOut: filepath.Join(workdir, disk+".qcow2"),
|
||||||
format: f,
|
format: f,
|
||||||
@ -271,7 +279,7 @@ func (b *builder) unmountImg(ctx context.Context) error {
|
|||||||
|
|
||||||
func (b *builder) copyRootFS(ctx context.Context) error {
|
func (b *builder) copyRootFS(ctx context.Context) error {
|
||||||
logrus.Infof("copying rootfs to raw image")
|
logrus.Infof("copying rootfs to raw image")
|
||||||
if err := exec.Run(ctx, "tar", "-xvf", b.src, "-C", b.mntPoint); err != nil {
|
if err := b.img.Flatten(ctx, b.mntPoint); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -285,18 +293,19 @@ func (b *builder) setupRootFS(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
b.diskUUD = strings.TrimSuffix(o, "\n")
|
b.diskUUD = strings.TrimSuffix(o, "\n")
|
||||||
fstab := fmt.Sprintf("UUID=%s / ext4 errors=remount-ro 0 1\n", b.diskUUD)
|
fstab := fmt.Sprintf("UUID=%s / ext4 errors=remount-ro 0 1\n", b.diskUUD)
|
||||||
if err := b.chWriteFile("/etc/fstab", fstab, 0644); err != nil {
|
if err := b.chWriteFile("/etc/fstab", fstab, perm); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := b.chWriteFile("/etc/resolv.conf", "nameserver 8.8.8.8", 0644); err != nil {
|
if err := b.chWriteFileIfNotExist("/etc/resolv.conf", "nameserver 8.8.8.8", 0644); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := b.chWriteFile("/etc/hostname", "localhost", 0644); err != nil {
|
if err := b.chWriteFileIfNotExist("/etc/hostname", "localhost", perm); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := b.chWriteFile("/etc/hosts", hosts, 0644); err != nil {
|
if err := b.chWriteFileIfNotExist("/etc/hosts", hosts, perm); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// TODO(adphi): is it the righ fix ?
|
||||||
if err := os.RemoveAll("/ur/sbin/policy-rc.d"); err != nil {
|
if err := os.RemoveAll("/ur/sbin/policy-rc.d"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -311,10 +320,10 @@ func (b *builder) setupRootFS(ctx context.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
by = append(by, []byte("\n"+"ttyS0::respawn:/sbin/getty -L ttyS0 115200 vt100\n")...)
|
by = append(by, []byte("\n"+"ttyS0::respawn:/sbin/getty -L ttyS0 115200 vt100\n")...)
|
||||||
if err := b.chWriteFile("/etc/inittab", string(by), 0644); err != nil {
|
if err := b.chWriteFile("/etc/inittab", string(by), perm); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := b.chWriteFile("/etc/network/interfaces", "", 0644); err != nil {
|
if err := b.chWriteFileIfNotExist("/etc/network/interfaces", "", perm); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -338,7 +347,7 @@ func (b *builder) installKernel(ctx context.Context) error {
|
|||||||
default:
|
default:
|
||||||
return fmt.Errorf("%s: distribution not supported", b.osRelease.ID)
|
return fmt.Errorf("%s: distribution not supported", b.osRelease.ID)
|
||||||
}
|
}
|
||||||
if err := b.chWriteFile("/boot/syslinux.cfg", fmt.Sprintf(sysconfig, b.diskUUD), 0644); err != nil {
|
if err := b.chWriteFile("/boot/syslinux.cfg", fmt.Sprintf(sysconfig, b.diskUUD), perm); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -361,10 +370,21 @@ func (b *builder) chWriteFile(path string, content string, perm os.FileMode) err
|
|||||||
return os.WriteFile(b.chPath(path), []byte(content), perm)
|
return os.WriteFile(b.chPath(path), []byte(content), perm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *builder) chWriteFileIfNotExist(path string, content string, perm os.FileMode) error {
|
||||||
|
if i, err := os.Stat(b.chPath(path)); err == nil && i.Size() != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return os.WriteFile(b.chPath(path), []byte(content), perm)
|
||||||
|
}
|
||||||
|
|
||||||
func (b *builder) chPath(path string) string {
|
func (b *builder) chPath(path string) string {
|
||||||
return fmt.Sprintf("%s%s", b.mntPoint, path)
|
return fmt.Sprintf("%s%s", b.mntPoint, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *builder) Close() error {
|
||||||
|
return b.img.Close()
|
||||||
|
}
|
||||||
|
|
||||||
func block(path string, size int64) error {
|
func block(path string, size int64) error {
|
||||||
f, err := os.Create(path)
|
f, err := os.Create(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
17
convert.go
17
convert.go
@ -60,24 +60,13 @@ func Convert(ctx context.Context, img string, size int64, password string, outpu
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer docker.Cmd(ctx, "image", "rm", imgUUID)
|
defer docker.Cmd(ctx, "image", "rm", imgUUID)
|
||||||
archive := imgUUID + ".tar"
|
|
||||||
archivePath := filepath.Join(tmpPath, archive)
|
|
||||||
logrus.Infof("creating root file system archive")
|
|
||||||
if err := docker.Cmd(ctx, "run", "-d", "--name", imgUUID, imgUUID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := docker.Cmd(ctx, "export", "--output", archivePath, imgUUID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := docker.Cmd(ctx, "rm", "-f", imgUUID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logrus.Infof("creating vm image")
|
|
||||||
|
|
||||||
b, err := NewBuilder(tmpPath, archivePath, "", size, r, format)
|
logrus.Infof("creating vm image")
|
||||||
|
b, err := NewBuilder(ctx, tmpPath, imgUUID, "", size, r, format)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer b.Close()
|
||||||
if err := b.Build(ctx); err != nil {
|
if err := b.Build(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
125
docker_image.go
125
docker_image.go
@ -15,58 +15,127 @@
|
|||||||
package d2vm
|
package d2vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/google/go-containerregistry/cmd/crane/cmd"
|
||||||
|
"github.com/google/go-containerregistry/pkg/name"
|
||||||
|
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||||
|
"github.com/google/go-containerregistry/pkg/v1/daemon"
|
||||||
|
"github.com/google/go-containerregistry/pkg/v1/mutate"
|
||||||
|
|
||||||
|
"go.linka.cloud/d2vm/pkg/exec"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
dockerImageRun = `
|
dockerImageRun = `
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
{{- range .Config.Env }}
|
{{- range .DockerImageConfig.Env }}
|
||||||
export {{ . }}
|
export {{ . }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
cd {{- if .Config.WorkingDir }}{{ .Config.WorkingDir }}{{- else }}/{{- end }}
|
{{ if .DockerImageConfig.WorkingDir }}cd {{ .DockerImageConfig.WorkingDir }}{{ end }}
|
||||||
|
|
||||||
{{ .Config.Entrypoint }} {{ .Config.Args }}
|
{{ if .DockerImageConfig.User }}su {{ .DockerImageConfig.User }} -p -s /bin/sh -c '{{ end }}{{ if .DockerImageConfig.Entrypoint}}{{ format .DockerImageConfig.Entrypoint }} {{ end}}{{ if .DockerImageConfig.Cmd }}{{ format .DockerImageConfig.Cmd }}{{ end }}{{ if .DockerImageConfig.User }}'{{- end }}
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
dockerImageRunTemplate = template.Must(template.New("docker-run.sh").Parse(dockerImageRun))
|
dockerImageRunTemplate = template.Must(template.New("docker-run.sh").Funcs(map[string]interface{}{"format": func(a []string) string {
|
||||||
|
var o []string
|
||||||
|
for _, v := range a {
|
||||||
|
o = append(o, fmt.Sprintf("\"%s\"", v))
|
||||||
|
}
|
||||||
|
return strings.Join(o, " ")
|
||||||
|
}}).Parse(dockerImageRun))
|
||||||
|
|
||||||
|
_ = cmd.NewCmdFlatten
|
||||||
)
|
)
|
||||||
|
|
||||||
type DockerImage struct {
|
type DockerImage struct {
|
||||||
Config struct {
|
DockerImageConfig `json:"Config"`
|
||||||
Hostname string `json:"Hostname"`
|
|
||||||
Domainname string `json:"Domainname"`
|
|
||||||
User string `json:"User"`
|
|
||||||
AttachStdin bool `json:"AttachStdin"`
|
|
||||||
AttachStdout bool `json:"AttachStdout"`
|
|
||||||
AttachStderr bool `json:"AttachStderr"`
|
|
||||||
ExposedPorts struct {
|
|
||||||
Tcp struct {
|
|
||||||
} `json:"3000/tcp"`
|
|
||||||
} `json:"ExposedPorts"`
|
|
||||||
Tty bool `json:"Tty"`
|
|
||||||
OpenStdin bool `json:"OpenStdin"`
|
|
||||||
StdinOnce bool `json:"StdinOnce"`
|
|
||||||
Env []string `json:"Env"`
|
|
||||||
Cmd []string `json:"Cmd"`
|
|
||||||
Image string `json:"Image"`
|
|
||||||
Volumes interface{} `json:"Volumes"`
|
|
||||||
WorkingDir string `json:"WorkingDir"`
|
|
||||||
Entrypoint []string `json:"Entrypoint"`
|
|
||||||
OnBuild interface{} `json:"OnBuild"`
|
|
||||||
Labels interface{} `json:"Labels"`
|
|
||||||
} `json:"Config"`
|
|
||||||
Architecture string `json:"Architecture"`
|
Architecture string `json:"Architecture"`
|
||||||
Os string `json:"Os"`
|
Os string `json:"Os"`
|
||||||
Size int `json:"Size"`
|
Size int `json:"Size"`
|
||||||
VirtualSize int `json:"VirtualSize"`
|
}
|
||||||
|
|
||||||
|
type DockerImageConfig struct {
|
||||||
|
Image string `json:"Image"`
|
||||||
|
Hostname string `json:"Hostname"`
|
||||||
|
Domainname string `json:"Domainname"`
|
||||||
|
User string `json:"User"`
|
||||||
|
Env []string `json:"Env"`
|
||||||
|
Cmd []string `json:"Cmd"`
|
||||||
|
WorkingDir string `json:"WorkingDir"`
|
||||||
|
Entrypoint []string `json:"Entrypoint"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i DockerImage) AsRunScript(w io.Writer) error {
|
func (i DockerImage) AsRunScript(w io.Writer) error {
|
||||||
return dockerImageRunTemplate.Execute(w, i)
|
return dockerImageRunTemplate.Execute(w, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
whiteoutPrefix = ".wh."
|
||||||
|
manifest = "manifest.json"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewImage(ctx context.Context, tag string, imageTmpPath string) (*image, error) {
|
||||||
|
ref, err := name.ParseReference(tag)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
img, err := daemon.Image(ref)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(imageTmpPath, perm); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
i := &image{
|
||||||
|
img: img,
|
||||||
|
dir: imageTmpPath,
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type image struct {
|
||||||
|
tag string
|
||||||
|
img v1.Image
|
||||||
|
dir string
|
||||||
|
Config string `json:"Config"`
|
||||||
|
RepoTags []string `json:"RepoTags"`
|
||||||
|
Layers []string `json:"Layers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i image) Flatten(ctx context.Context, out string) error {
|
||||||
|
if err := os.MkdirAll(out, perm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tar := filepath.Join(i.dir, "img.tar")
|
||||||
|
f, err := os.Create(tar)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
if _, err := io.Copy(f, mutate.Extract(i.img)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := exec.Run(ctx, "tar", "xvf", tar, "-C", out); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i image) Close() error {
|
||||||
|
return os.RemoveAll(i.dir)
|
||||||
|
}
|
||||||
|
171
docker_image_test.go
Normal file
171
docker_image_test.go
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
package d2vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"go.linka.cloud/d2vm/pkg/docker"
|
||||||
|
"go.linka.cloud/d2vm/pkg/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDockerImageAsRunSript(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
image DockerImage
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nothing",
|
||||||
|
image: DockerImage{
|
||||||
|
DockerImageConfig: DockerImageConfig{
|
||||||
|
User: "",
|
||||||
|
WorkingDir: "",
|
||||||
|
Env: nil,
|
||||||
|
Entrypoint: nil,
|
||||||
|
Cmd: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: `
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tail -f /dev/null",
|
||||||
|
image: DockerImage{
|
||||||
|
DockerImageConfig: DockerImageConfig{
|
||||||
|
User: "root",
|
||||||
|
Cmd: []string{"tail", "-f", "/dev/null"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: `
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
su root -p -s /bin/sh -c '"tail" "-f" "/dev/null"'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tail -f /dev/null inside home",
|
||||||
|
image: DockerImage{
|
||||||
|
DockerImageConfig: DockerImageConfig{
|
||||||
|
User: "root",
|
||||||
|
WorkingDir: "/root",
|
||||||
|
Cmd: []string{"tail", "-f", "/dev/null"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: `
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
cd /root
|
||||||
|
|
||||||
|
su root -p -s /bin/sh -c '"tail" "-f" "/dev/null"'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "subshell tail -f /dev/null",
|
||||||
|
image: DockerImage{
|
||||||
|
DockerImageConfig: DockerImageConfig{
|
||||||
|
User: "root",
|
||||||
|
Entrypoint: []string{"/bin/sh", "-c"},
|
||||||
|
Cmd: []string{"tail -f /dev/null"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: `
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
su root -p -s /bin/sh -c '"/bin/sh" "-c" "tail -f /dev/null"'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "www-data with env",
|
||||||
|
image: DockerImage{
|
||||||
|
DockerImageConfig: DockerImageConfig{
|
||||||
|
User: "www-data",
|
||||||
|
Cmd: []string{"tail", "-f", "/dev/null"},
|
||||||
|
Env: []string{"ENV=PROD", "DB=mysql://user:password@localhost"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: `
|
||||||
|
#!/bin/sh
|
||||||
|
export ENV=PROD
|
||||||
|
export DB=mysql://user:password@localhost
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
su www-data -p -s /bin/sh -c '"tail" "-f" "/dev/null"'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
var w bytes.Buffer
|
||||||
|
require.NoError(t, tt.image.AsRunScript(&w))
|
||||||
|
assert.Equal(t, tt.want, w.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestImageFlatten(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
const (
|
||||||
|
img = "d2vm-flatten-test"
|
||||||
|
dockerfile = `FROM alpine
|
||||||
|
|
||||||
|
COPY resolv.conf /etc/
|
||||||
|
COPY hostname /etc/
|
||||||
|
|
||||||
|
RUN rm -rf /etc/apk
|
||||||
|
`
|
||||||
|
)
|
||||||
|
exec.Run = exec.RunStdout
|
||||||
|
tmp := filepath.Join(os.TempDir(), "d2vm-tests", "image-flatten")
|
||||||
|
require.NoError(t, os.MkdirAll(tmp, perm))
|
||||||
|
defer os.RemoveAll(tmp)
|
||||||
|
|
||||||
|
require.NoError(t, os.WriteFile(filepath.Join(tmp, "hostname"), []byte("d2vm-flatten-test"), perm))
|
||||||
|
require.NoError(t, os.WriteFile(filepath.Join(tmp, "resolv.conf"), []byte("nameserver 8.8.8.8"), perm))
|
||||||
|
require.NoError(t, os.WriteFile(filepath.Join(tmp, "Dockerfile"), []byte(dockerfile), perm))
|
||||||
|
require.NoError(t, docker.Cmd(ctx, "image", "build", "-t", img, tmp))
|
||||||
|
defer docker.Cmd(ctx, "image", "rm", img)
|
||||||
|
|
||||||
|
// imgTar := filepath.Join(tmp, img+".tar")
|
||||||
|
// require.NoError(t, docker.Cmd(ctx, "image", "save", img, "-o", imgTar))
|
||||||
|
//
|
||||||
|
imgTmp := filepath.Join(tmp, "image")
|
||||||
|
|
||||||
|
i, err := NewImage(ctx, img, imgTmp)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
rootfs := filepath.Join(tmp, "rootfs")
|
||||||
|
require.NoError(t, i.Flatten(ctx, rootfs))
|
||||||
|
|
||||||
|
b, err := os.ReadFile(filepath.Join(rootfs, "etc", "resolv.conf"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "nameserver 8.8.8.8", string(b))
|
||||||
|
|
||||||
|
b, err = os.ReadFile(filepath.Join(rootfs, "etc", "hostname"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "d2vm-flatten-test", string(b))
|
||||||
|
|
||||||
|
_, err = os.Stat(filepath.Join(rootfs, "etc", "apk"))
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
require.NoError(t, i.Close())
|
||||||
|
_, err = os.Stat(imgTmp)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
30
go.mod
30
go.mod
@ -4,16 +4,44 @@ go 1.17
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/c2h5oh/datasize v0.0.0-20200825124411-48ed595a09d2
|
github.com/c2h5oh/datasize v0.0.0-20200825124411-48ed595a09d2
|
||||||
|
github.com/google/go-containerregistry v0.8.0
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/joho/godotenv v1.4.0
|
github.com/joho/godotenv v1.4.0
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/spf13/cobra v1.4.0
|
github.com/spf13/cobra v1.4.0
|
||||||
|
github.com/stretchr/testify v1.7.0
|
||||||
go.uber.org/multierr v1.8.0
|
go.uber.org/multierr v1.8.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/Microsoft/go-winio v0.5.1 // indirect
|
||||||
|
github.com/containerd/containerd v1.5.8 // indirect
|
||||||
|
github.com/containerd/stargz-snapshotter/estargz v0.10.1 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/docker/cli v20.10.12+incompatible // indirect
|
||||||
|
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||||
|
github.com/docker/docker v20.10.12+incompatible // indirect
|
||||||
|
github.com/docker/docker-credential-helpers v0.6.4 // indirect
|
||||||
|
github.com/docker/go-connections v0.4.0 // indirect
|
||||||
|
github.com/docker/go-units v0.4.0 // indirect
|
||||||
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
|
github.com/google/go-cmp v0.5.6 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||||
|
github.com/klauspost/compress v1.13.6 // indirect
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
|
github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
github.com/vbatts/tar-split v0.11.2 // indirect
|
||||||
go.uber.org/atomic v1.7.0 // indirect
|
go.uber.org/atomic v1.7.0 // indirect
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 // indirect
|
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f // indirect
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
|
||||||
|
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
|
||||||
|
google.golang.org/grpc v1.43.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.27.1 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||||
)
|
)
|
||||||
|
2
virtinst
2
virtinst
@ -3,4 +3,4 @@
|
|||||||
|
|
||||||
IMG=${1:-disk0.qcow2}
|
IMG=${1:-disk0.qcow2}
|
||||||
|
|
||||||
virt-install --disk $IMG --import --memory 4096 --vcpus 4 --nographics --cpu host --channel unix,target.type=virtio,target.name='org.qemu.guest_agent.0'
|
virt-install --disk $IMG --import --memory 4096 --vcpus 4 --nographics --cpu host --channel unix,target.type=virtio,target.name='org.qemu.guest_agent.0' --transient
|
||||||
|
Loading…
Reference in New Issue
Block a user