aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFranck Cuny <franck@fcuny.net>2021-04-22 14:03:04 -0700
committerFranck Cuny <franck@fcuny.net>2022-06-11 13:57:41 -0700
commit8686cd5698e42dd1a328f4fed6f77dc103c34275 (patch)
tree10e688f6080ce45ac115c31d3ff9d5f3372078b0
parentadd a lease to the Go context (diff)
downloadinfra-8686cd5698e42dd1a328f4fed6f77dc103c34275.tar.gz
extract layers to a mounted loop device
We create a loop device by pre-allocating space to a file on disk. This file is then converted to an ext4 partition, which we mount to a temporary path on the host. Once this is done, we extract all the layers from the given container on that mounted path. We also add a few extra files to the image (`/etc/hosts` and `/etc/resolv.conf`). When we're done extracting and writing, we run resize2fs in order to resize the image to a more reasonable size.
-rw-r--r--users/fcuny/exp/containerd-to-vm/cmd/c2vm/main.go120
-rw-r--r--users/fcuny/exp/containerd-to-vm/go.mod3
-rw-r--r--users/fcuny/exp/containerd-to-vm/go.sum6
3 files changed, 128 insertions, 1 deletions
diff --git a/users/fcuny/exp/containerd-to-vm/cmd/c2vm/main.go b/users/fcuny/exp/containerd-to-vm/cmd/c2vm/main.go
index 5e9fbb0..c416e47 100644
--- a/users/fcuny/exp/containerd-to-vm/cmd/c2vm/main.go
+++ b/users/fcuny/exp/containerd-to-vm/cmd/c2vm/main.go
@@ -4,11 +4,18 @@ import (
"context"
"flag"
"fmt"
+ "io/ioutil"
"log"
+ "os/exec"
+ "path/filepath"
"github.com/containerd/containerd"
+ "github.com/containerd/containerd/content"
+ "github.com/containerd/containerd/images"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/platforms"
+ "github.com/docker/docker/pkg/archive"
+ "github.com/google/renameio"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
@@ -27,6 +34,7 @@ var (
func main() {
var (
containerName = flag.String("container", "", "Name of the container")
+ outFile = flag.String("out", "container.img", "firecracker output to create")
)
flag.Parse()
@@ -58,5 +66,115 @@ func main() {
log.Fatalf("failed to get the size of the image: %v", err)
}
- fmt.Printf("pulled %s (%d bytes)\n", image.Name(), imageSize)
+ log.Printf("pulled %s (%d bytes)\n", image.Name(), imageSize)
+
+ mntDir, err := ioutil.TempDir("", "c2vm")
+ if err != nil {
+ log.Fatalf("Failed to create mount temp dir: %v\n", err)
+ }
+
+ if err := createLoopDevice(*outFile, mntDir); err != nil {
+ log.Fatalf("%v\n", err)
+ }
+
+ if err := extract(ctx, client, image, mntDir); err != nil {
+ log.Fatalf("failed to extract the container: %v\n", err)
+ }
+
+ if err = extraFiles(mntDir); err != nil {
+ log.Fatalf("failed to add extra files to the image: %v\n", err)
+ }
+
+ if err := detachLoopDevice(mntDir); err != nil {
+ log.Fatalf("failed to umount %s: %v\n", mntDir, err)
+ }
+
+ if err := resizeImage(*outFile); err != nil {
+ log.Fatalf("failed to resize the image %s: %s\n", *outFile, err)
+ }
+}
+
+func extract(ctx context.Context, client *containerd.Client, image containerd.Image, mntDir string) error {
+ manifest, err := images.Manifest(ctx, client.ContentStore(), image.Target(), platform)
+ if err != nil {
+ log.Fatalf("failed to get the manifest: %v\n", err)
+ }
+
+ for _, desc := range manifest.Layers {
+ log.Printf("extracting layer %s\n", desc.Digest.String())
+ layer, err := client.ContentStore().ReaderAt(ctx, desc)
+ if err != nil {
+ return err
+ }
+ if err := archive.Untar(content.NewReader(layer), mntDir, &archive.TarOptions{NoLchown: true}); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func createLoopDevice(rawFile, mntDir string) error {
+ f, err := renameio.TempFile("", rawFile)
+ if err != nil {
+ return err
+ }
+ defer f.Cleanup()
+
+ command := exec.Command("fallocate", "-l", "2G", f.Name())
+ if err := command.Run(); err != nil {
+ return fmt.Errorf("fallocate error: %s", err)
+ }
+
+ command = exec.Command("mkfs.ext4", "-F", f.Name())
+ if err := command.Run(); err != nil {
+ return fmt.Errorf("mkfs.ext4 error: %s", err)
+ }
+
+ f.CloseAtomicallyReplace()
+
+ command = exec.Command("mount", "-o", "loop", rawFile, mntDir)
+ if err := command.Run(); err != nil {
+ return fmt.Errorf("mount error: %s", err)
+ }
+ log.Printf("mounted %s on %s\n", rawFile, mntDir)
+ return nil
+}
+
+func detachLoopDevice(mntDir string) error {
+ log.Printf("umount %s\n", mntDir)
+ command := exec.Command("umount", mntDir)
+ return command.Run()
+}
+
+func resizeImage(rawFile string) error {
+ // let's bring the image to a more reasonable size. We do this by
+ // first running e2fsck on the image then we can resize the image.
+ command := exec.Command("/usr/bin/e2fsck", "-p", "-f", rawFile)
+ if err := command.Run(); err != nil {
+ return fmt.Errorf("e2fsck error: %s", err)
+ }
+
+ command = exec.Command("resize2fs", "-M", rawFile)
+ if err := command.Run(); err != nil {
+ return fmt.Errorf("resize2fs error: %s", err)
+ }
+ return nil
+}
+
+func extraFiles(mntDir string) error {
+ if err := writeToFile(filepath.Join(mntDir, "etc", "hosts"), "127.0.0.1\tlocalhost\n"); err != nil {
+ return err
+ }
+ if err := writeToFile(filepath.Join(mntDir, "etc", "resolv.conf"), "nameserver 192.168.0.1\n"); err != nil {
+ return err
+ }
+ return nil
+}
+
+func writeToFile(filepath string, content string) error {
+ if err := ioutil.WriteFile(filepath, []byte(content), 0644); err != nil {
+ return fmt.Errorf("writeToFile %s: %v", filepath, err)
+ }
+ return nil
}
diff --git a/users/fcuny/exp/containerd-to-vm/go.mod b/users/fcuny/exp/containerd-to-vm/go.mod
index f4d6398..b033d54 100644
--- a/users/fcuny/exp/containerd-to-vm/go.mod
+++ b/users/fcuny/exp/containerd-to-vm/go.mod
@@ -9,9 +9,12 @@ require (
github.com/containerd/continuity v0.0.0-20210315143101-93e15499afd5 // indirect
github.com/containerd/fifo v0.0.0-20210331061852-650e8a8a179d // indirect
github.com/containerd/typeurl v1.0.2 // indirect
+ github.com/docker/docker v20.10.6+incompatible
github.com/gogo/googleapis v1.4.1 // indirect
github.com/google/go-cmp v0.5.5 // indirect
+ github.com/google/renameio v0.1.0
github.com/google/uuid v1.2.0 // indirect
+ github.com/moby/sys/mount v0.2.0 // indirect
github.com/opencontainers/image-spec v1.0.1
github.com/sirupsen/logrus v1.8.1 // indirect
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1 // indirect
diff --git a/users/fcuny/exp/containerd-to-vm/go.sum b/users/fcuny/exp/containerd-to-vm/go.sum
index e25329d..1d54d89 100644
--- a/users/fcuny/exp/containerd-to-vm/go.sum
+++ b/users/fcuny/exp/containerd-to-vm/go.sum
@@ -199,11 +199,14 @@ github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyG
github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
+github.com/docker/docker v20.10.6+incompatible h1:oXI3Vas8TI8Eu/EjH4srKHJBVqraSzJybhxY7Om9faQ=
+github.com/docker/docker v20.10.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
+github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
@@ -307,6 +310,7 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -384,6 +388,8 @@ github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
+github.com/moby/sys/mount v0.2.0 h1:WhCW5B355jtxndN5ovugJlMFJawbUODuW8fSnEH6SSM=
+github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7sxOougM=
github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
github.com/moby/sys/mountinfo v0.4.1 h1:1O+1cHA1aujwEwwVMa2Xm2l+gIpUHyd3+D+d7LZh1kM=
github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=