Repository: firecracker-microvm/firecracker-containerd
Pull request #168 (2019-04-18 19:12:28)

Use default VM configuration if not specified in request
Signed-off-by: Maksym Pavlenko <makpav@amazon.com>

*Issue #, if available:*
https://github.com/firecracker-microvm/firecracker-containerd/issues/159
*Description of changes:*
- Use default configuration if some parameters weren't specified in create request.
I've reworked helpers to 'map or use defaults' instead of override logic.
- Small namespace fix when publishing stop event

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.



Patch:
diff --git a/.gitignore b/.gitignore
index 1639997..c875fbd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,2 @@
 .idea/
 bin/
-*stamp
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index 6570354..0000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "runc"]
-	path = _submodules/runc
-	url = https://github.com/opencontainers/runc
diff --git a/Makefile b/Makefile
index 2a1f2cf..f2216b3 100644
--- a/Makefile
+++ b/Makefile
@@ -19,10 +19,6 @@ DOCKER_IMAGE_TAG?=latest
 
 GOPATH:=$(shell go env GOPATH)
 BINPATH:=$(abspath ./bin)
-SUBMODULES=_submodules
-RUNC_DIR=$(SUBMODULES)/runc
-RUNC_BIN=$(RUNC_DIR)/runc
-UID:=$(shell id -u)
 
 all: $(SUBDIRS)
 
@@ -35,16 +31,9 @@ proto:
 clean:
 	for d in $(SUBDIRS); do $(MAKE) -C $$d clean; done
 	- rm -rf $(BINPATH)/
-	$(MAKE) -C $(RUNC_DIR) clean
-	rm -f *stamp
-	$(MAKE) -C tools/image-builder clean-in-docker
-
-distclean: clean
-	docker rmi localhost/runc-builder:latest
-	$(MAKE) -C tools/image-builder distclean
 
 lint:
-	$(BINPATH)/ltag -t ./.headers -excludes "tools $(SUBMODULES)" -check -v
+	$(BINPATH)/ltag -t ./.headers -check -v
 	$(BINPATH)/git-validation -run DCO,dangling-whitespace,short-subject -range HEAD~20..HEAD
 	$(BINPATH)/golangci-lint run
 
@@ -54,34 +43,6 @@ deps:
 	GOBIN=$(BINPATH) GO111MODULE=off go get -u github.com/vbatts/git-validation
 	GOBIN=$(BINPATH) GO111MODULE=off go get -u github.com/kunalkushwaha/ltag
 
-runc-builder: runc-builder-stamp
-
-runc-builder-stamp: tools/docker/Dockerfile.runc-builder
-	cd tools/docker && docker build -t localhost/runc-builder:latest -f Dockerfile.runc-builder .
-	touch $@
-
-$(RUNC_DIR)/VERSION:
-	git submodule update --init --recursive $(RUNC_DIR)
-
-runc: $(RUNC_BIN)
-
-$(RUNC_BIN): $(RUNC_DIR)/VERSION runc-builder-stamp
-	docker run --rm -it --user $(UID) \
-		--volume $(PWD)/$(RUNC_DIR):/gopath/src/github.com/opencontainers/runc \
-		--volume $(PWD)/deps:/target \
-		-e HOME=/tmp \
-		-e GOPATH=/gopath \
-		--workdir /gopath/src/github.com/opencontainers/runc \
-		localhost/runc-builder:latest \
-		make runc
-
-image: $(RUNC_BIN) agent
-	mkdir -p tools/image-builder/files_ephemeral/usr/local/bin
-	cp $(RUNC_BIN) tools/image-builder/files_ephemeral/usr/local/bin
-	cp agent/agent tools/image-builder/files_ephemeral/usr/local/bin
-	touch tools/image-builder/files_ephemeral
-	$(MAKE) -C tools/image-builder all-in-docker
-
 install:
 	for d in $(SUBDIRS); do $(MAKE) -C $$d install; done
 
@@ -108,4 +69,4 @@ docker-image-e2etest-naive:
 
 docker-images: | docker-image-e2etest-naive docker-image-unittest-nonroot docker-image-unittest
 
-.PHONY: all $(SUBDIRS) clean proto deps lint install docker-images docker-image-e2etest-naive docker-image-unittest-nonroot docker-image-unittest runc-builder runc
+.PHONY: all $(SUBDIRS) clean proto deps lint install docker-images docker-image-e2etest-naive docker-image-unittest-nonroot docker-image-unittest
diff --git a/_submodules/runc b/_submodules/runc
deleted file mode 160000
index 6635b4f..0000000
--- a/_submodules/runc
+++ /dev/null
@@ -1,1 +0,0 @@
-Subproject commit 6635b4f0c6af3810594d2770f662f34ddc15b40d
diff --git a/firecracker-control/common.go b/firecracker-control/common.go
index 9b7a4eb..0eef3a6 100644
--- a/firecracker-control/common.go
+++ b/firecracker-control/common.go
@@ -16,6 +16,7 @@ package service
 import (
 	"time"
 
+	models "github.com/firecracker-microvm/firecracker-go-sdk/client/models"
 	"github.com/pkg/errors"
 )
 
@@ -31,4 +32,11 @@ var (
 
 const (
 	firecrackerStartTimeout = 5 * time.Second
+	defaultCPUTemplate      = models.CPUTemplateT2
+	defaultCPUCount         = 1
+	defaultMemSizeMb        = 128
+	defaultKernelArgs       = "console=ttyS0 noapic reboot=k panic=1 pci=off nomodules rw"
+	defaultFilesPath        = "/var/lib/firecracker-containerd/runtime/"
+	defaultKernelPath       = defaultFilesPath + "default-vmlinux.bin"
+	defaultRootfsPath       = defaultFilesPath + "default-rootfs.img"
 )
diff --git a/firecracker-control/helpers.go b/firecracker-control/helpers.go
index 8c0af45..2a4783d 100644
--- a/firecracker-control/helpers.go
+++ b/firecracker-control/helpers.go
@@ -14,7 +14,6 @@
 package service
 
 import (
-	"errors"
 	"time"
 
 	"github.com/firecracker-microvm/firecracker-go-sdk"
@@ -23,51 +22,28 @@ import (
 	"github.com/firecracker-microvm/firecracker-containerd/proto"
 )
 
-// overrideVMConfigFromTaskOpts overrides the firecracker VM config for the
-// task with values set in the protobuf message.
-func overrideVMConfigFromTaskOpts(
-	cfg firecracker.Config,
-	vmConfig *proto.FirecrackerConfig,
-	drivesBuilder firecracker.DrivesBuilder,
-) (firecracker.Config, firecracker.DrivesBuilder, error) {
-	if vmConfig == nil {
-		return cfg, drivesBuilder, nil
+func machineConfigurationFromProto(req *proto.FirecrackerMachineConfiguration) models.MachineConfiguration {
+	config := models.MachineConfiguration{
+		CPUTemplate: defaultCPUTemplate,
+		VcpuCount:   defaultCPUCount,
+		MemSizeMib:  defaultMemSizeMb,
 	}
-	var err error
-	if vmConfig.KernelImagePath != "" {
-		// If kernel image path is set, override it.
-		cfg.KernelImagePath = vmConfig.KernelImagePath
-	}
-	if vmConfig.KernelArgs != "" {
-		// If kernel args are set, override it.
-		cfg.KernelArgs = vmConfig.KernelArgs
-	}
-	// Attach network interface specified in the config.
-	if len(vmConfig.NetworkInterfaces) > 0 {
-		nwIfaces := make([]firecracker.NetworkInterface, len(vmConfig.NetworkInterfaces))
-		for i, nw := range vmConfig.NetworkInterfaces {
-			nwIfaces[i] = networkConfigFromProto(nw)
-		}
-		cfg.NetworkInterfaces = nwIfaces
-	}
-	if vmConfig.MachineCfg != nil {
-		cfg.MachineCfg, err = machineConfigFromProto(vmConfig.MachineCfg)
-		if err != nil {
-			return firecracker.Config{}, drivesBuilder, err
-		}
+
+	if name := req.GetCPUTemplate(); name != "" {
+		config.CPUTemplate = models.CPUTemplate(name)
 	}
-	// Overwrite the root drive config if specified.
-	if vmConfig.RootDrive != nil {
-		drivesBuilder = drivesBuilderFromProto(vmConfig.RootDrive, drivesBuilder, true)
+
+	if count := req.GetVcpuCount(); count > 0 {
+		config.VcpuCount = int64(count)
 	}
-	// Add additional drives if specified.
-	if len(vmConfig.AdditionalDrives) > 0 {
-		for _, drive := range vmConfig.AdditionalDrives {
-			drivesBuilder = drivesBuilderFromProto(drive, drivesBuilder, false)
-		}
+
+	if size := req.GetMemSizeMib(); size > 0 {
+		config.MemSizeMib = int64(size)
 	}
 
-	return cfg, drivesBuilder, nil
+	config.HtEnabled = req.GetHtEnabled()
+
+	return config
 }
 
 // networkConfigFromProto creates a firecracker NetworkInterface object from
@@ -90,51 +66,17 @@ func networkConfigFromProto(nwIface *proto.FirecrackerNetworkInterface) firecrac
 	return result
 }
 
-// machineConfigFromProto creates a firecracker MachineConfiguration object
-// from the protobuf FirecrackerMachineConfiguration message.
-func machineConfigFromProto(mCfg *proto.FirecrackerMachineConfiguration) (models.MachineConfiguration, error) {
-	result := models.MachineConfiguration{}
-
-	if mCfg.MemSizeMib > 0 && mCfg.VcpuCount == 0 {
-		// Only memory is being overridden.
-		return result, errors.New("both vm memory and vcpu count need to be overridden")
-	}
-
-	if mCfg.VcpuCount > 0 && mCfg.MemSizeMib == 0 {
-		// Only cpu is being overridden.
-		return result, errors.New("both vm memory and vcpu count need to be overridden")
-	}
-
-	if mCfg.CPUTemplate != "" {
-		result.CPUTemplate = models.CPUTemplate(mCfg.CPUTemplate)
-	}
-
-	result.HtEnabled = mCfg.HtEnabled
-	result.MemSizeMib = int64(mCfg.MemSizeMib)
-	result.VcpuCount = int64(mCfg.VcpuCount)
-
-	return result, nil
-}
-
-// drivesBuilderFromProto returns a new DrivesBuilder that can be used to
-// build the firecracker Drives config. The new DrivesBuilder object is
-// constructed using the information present in the protobuf message.
-func drivesBuilderFromProto(drive *proto.FirecrackerDrive,
-	drivesBuilder firecracker.DrivesBuilder,
-	isRoot bool,
-) firecracker.DrivesBuilder {
+func addDriveFromProto(builder firecracker.DrivesBuilder, drive *proto.FirecrackerDrive) firecracker.DrivesBuilder {
 	opt := func(d *models.Drive) {
-		d.Partuuid = drive.Partuuid
-		if drive.RateLimiter != nil {
-			d.RateLimiter = rateLimiterFromProto(drive.RateLimiter)
-		}
-	}
+		d.IsRootDevice = firecracker.Bool(drive.GetIsRootDevice())
+		d.Partuuid = drive.GetPartuuid()
 
-	if isRoot {
-		return drivesBuilder.WithRootDrive(drive.PathOnHost, opt)
+		if limiter := drive.GetRateLimiter(); limiter != nil {
+			d.RateLimiter = rateLimiterFromProto(limiter)
+		}
 	}
 
-	return drivesBuilder.AddDrive(drive.PathOnHost, drive.IsReadOnly, opt)
+	return builder.AddDrive(drive.GetPathOnHost(), drive.GetIsReadOnly(), opt)
 }
 
 // rateLimiterFromProto creates a firecracker RateLimiter object from the
diff --git a/firecracker-control/local.go b/firecracker-control/local.go
index aeabbc4..26a1306 100644
--- a/firecracker-control/local.go
+++ b/firecracker-control/local.go
@@ -22,10 +22,12 @@ import (
 
 	"github.com/containerd/containerd/events"
 	"github.com/containerd/containerd/log"
+	"github.com/containerd/containerd/namespaces"
 	"github.com/containerd/containerd/plugin"
-	firecracker "github.com/firecracker-microvm/firecracker-go-sdk"
+	"github.com/firecracker-microvm/firecracker-go-sdk"
 	"github.com/golang/protobuf/ptypes/empty"
 	"github.com/pkg/errors"
+	"github.com/sirupsen/logrus"
 
 	"github.com/firecracker-microvm/firecracker-containerd/proto"
 )
@@ -162,55 +164,54 @@ func (s *local) newInstance(ctx context.Context, id string, req *proto.CreateVMR
 }
 
 func (s *local) buildVMConfiguration(ctx context.Context, id string, cid uint32, req *proto.CreateVMRequest) (*firecracker.Config, error) {
-	var (
-		err    error
-		logger = log.G(ctx).WithField("vm_id", id)
-	)
-
-	vsockDevices := []firecracker.VsockDevice{
-		{Path: "root", CID: cid},
-	}
-
-	logger.Debugf("using cid: %d", cid)
+	logger := log.G(ctx).WithFields(logrus.Fields{
+		"vm_id":  id,
+		"vm_cid": cid,
+	})
 
 	cfg := &firecracker.Config{
-		SocketPath:      filepath.Join(s.rootPath, fmt.Sprintf("vm_%s.socket", id)),
-		LogFifo:         filepath.Join(s.rootPath, fmt.Sprintf("vm_%s_log.fifo", id)),
-		MetricsFifo:     filepath.Join(s.rootPath, fmt.Sprintf("vm_%s_metrics.fifo", id)),
-		KernelImagePath: req.GetKernelImagePath(),
-		KernelArgs:      req.GetKernelArgs(),
-		VsockDevices:    vsockDevices,
+		SocketPath:   filepath.Join(s.rootPath, fmt.Sprintf("vm_%s.socket", id)),
+		LogFifo:      filepath.Join(s.rootPath, fmt.Sprintf("vm_%s_log.fifo", id)),
+		MetricsFifo:  filepath.Join(s.rootPath, fmt.Sprintf("vm_%s_metrics.fifo", id)),
+		VsockDevices: []firecracker.VsockDevice{{Path: "root", CID: cid}},
+		MachineCfg:   machineConfigurationFromProto(req.GetMachineCfg()),
 	}
 
 	logger.Debugf("using socket path: %s", cfg.SocketPath)
 
-	machineCfg := req.GetMachineCfg()
-	if machineCfg == nil {
-		return nil, errors.New("invalid machine configuration")
+	// Kernel configuration
+
+	if val := req.GetKernelArgs(); val != "" {
+		cfg.KernelArgs = val
+	} else {
+		cfg.KernelArgs = defaultKernelArgs
 	}
 
-	cfg.MachineCfg, err = machineConfigFromProto(machineCfg)
-	if err != nil {
-		return nil, err
+	if val := req.GetKernelImagePath(); val != "" {
+		cfg.KernelImagePath = val
+	} else {
+		cfg.KernelImagePath = defaultKernelPath
 	}
 
-	// TODO: Specify default Firecracker configuration here?
+	// Drives configuration
 
-	rootDrive := req.GetRootDrive()
-	if rootDrive == nil {
-		return nil, errors.Errorf("root drive can't be empty")
+	var driveBuilder firecracker.DrivesBuilder
+	if root := req.GetRootDrive(); root != nil {
+		driveBuilder = firecracker.NewDrivesBuilder(root.GetPathOnHost())
+	} else {
+		driveBuilder = firecracker.NewDrivesBuilder(defaultRootfsPath)
 	}
 
-	driveBuilder := drivesBuilderFromProto(rootDrive, firecracker.DrivesBuilder{}, true)
-
 	for _, drive := range req.GetAdditionalDrives() {
-		driveBuilder = drivesBuilderFromProto(drive, driveBuilder, false)
+		driveBuilder = addDriveFromProto(driveBuilder, drive)
 	}
 
 	// TODO: Reserve fake drives here (https://github.com/firecracker-microvm/firecracker-containerd/pull/154)
 
 	cfg.Drives = driveBuilder.Build()
 
+	// Setup network interfaces
+
 	for _, ni := range req.GetNetworkInterfaces() {
 		cfg.NetworkInterfaces = append(cfg.NetworkInterfaces, networkConfigFromProto(ni))
 	}
@@ -227,6 +228,13 @@ func (s *local) startMachine(ctx context.Context, id string, machine machine) er
 	ctx, cancel := context.WithTimeout(ctx, firecrackerStartTimeout)
 	defer cancel()
 
+	ns, err := namespaces.NamespaceRequired(ctx)
+	if err != nil {
+		return err
+	}
+
+	logger.Debugf("using namespace: %s", ns)
+
 	if err := machine.Start(ctx); err != nil {
 		logger.WithError(err).Error("failed to start the VM")
 		return err
@@ -238,7 +246,7 @@ func (s *local) startMachine(ctx context.Context, id string, machine machine) er
 	}
 
 	go func() {
-		ctx := context.Background()
+		ctx := namespaces.WithNamespace(context.Background(), ns)
 
 		if err := machine.Wait(ctx); err != nil {
 			logger.WithError(err).Error("failed to wait the VM")
diff --git a/tools/docker/Dockerfile.runc-builder b/tools/docker/Dockerfile.runc-builder
deleted file mode 100644
index 9e0b3f7..0000000
--- a/tools/docker/Dockerfile.runc-builder
+++ /dev/null
@@ -1,4 +0,0 @@
-FROM golang:1.12-stretch
-
-RUN apt-get update && \
-    DEBIAN_FRONTEND=noninteractive apt-get -y install libseccomp-dev pkg-config
diff --git a/tools/image-builder/.gitignore b/tools/image-builder/.gitignore
deleted file mode 100644
index 57356b6..0000000
--- a/tools/image-builder/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-files_ephemeral
-*.img
-rootfs
-*stamp
diff --git a/tools/image-builder/Dockerfile.debian-image b/tools/image-builder/Dockerfile.debian-image
deleted file mode 100644
index 1b4687d..0000000
--- a/tools/image-builder/Dockerfile.debian-image
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"). You may
-# not use this file except in compliance with the License. A copy of the
-# License is located at
-#
-#       http://aws.amazon.com/apache2.0/
-#
-# or in the "license" file accompanying this file. This file is distributed
-# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-# express or implied. See the License for the specific language governing
-# permissions and limitations under the License.
-
-FROM debian:stretch-slim
-
-RUN apt-get update && \
-    DEBIAN_FRONTEND=noninteractive apt-get -y install \
-      --no-install-recommends \
-      make squashfs-tools debootstrap
-
-VOLUME ["/src"]
-WORKDIR "/src"
-ENTRYPOINT ["make"]
diff --git a/tools/image-builder/Makefile b/tools/image-builder/Makefile
deleted file mode 100644
index 51ee2d7..0000000
--- a/tools/image-builder/Makefile
+++ /dev/null
@@ -1,97 +0,0 @@
-# Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"). You may
-# not use this file except in compliance with the License. A copy of the
-# License is located at
-#
-#       http://aws.amazon.com/apache2.0/
-#
-# or in the "license" file accompanying this file. This file is distributed
-# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-# express or implied. See the License for the specific language governing
-# permissions and limitations under the License.
-
-UID        := $(shell id -u)
-WORKDIR    := rootfs
-WORKDIRLOC := $(shell readlink -f $(WORKDIR))
-IMAGE_DIRS := /dev /bin /etc /etc/init.d /tmp /var /run /proc /sys /container/rootfs /agent /rom /overlay
-DIRS       := $(foreach dir,$(IMAGE_DIRS),"$(WORKDIR)$(dir)")
-DEBMIRROR  ?= http://deb.debian.org/debian
-
-# When referenced in a _stamp target's recipe,
-# e.g. files_debootstrap_stamp, this function uses tar to copy the
-# contents of the base directory (e.g. files_debootstrap) to the image
-# root. In order for the file locations to be correctly represented in
-# the image root, the directory structure of the source directory
-# should match that of the root image, e.g. if files_debootstrap
-# contains usr/local/bin/foo, then 'foo' will be installed in
-# /usr/local/bin/ in the root filesystem.
-define install_dir =
-if [ -d $(subst _stamp,,$@) ]; then \
-  cd $(subst _stamp,,$@) && tar cf - . | (cd "$(WORKDIRLOC)" && tar xvf -);\
-fi
-# Reset the timestamp on $(WORKDIR), which may have been modified by the
-# creation of files in it:
-touch --reference=debootstrap_stamp --no-create "$(WORKDIR)"
-endef
-
-all: rootfs.img
-
-$(WORKDIR):
-	mkdir $(WORKDIR)
-
-image_files=$(shell find files_* -mindepth 1 -type f -print)
-files_%_stamp: debootstrap_stamp $(image_files)
-	$(install_dir)
-	touch $@
-
-debootstrap: debootstrap_stamp
-
-debootstrap_stamp: $(WORKDIR)
-ifneq ($(UID),0)
-	$(error $(@) needs to run as root, not $(UID))
-endif
-	debootstrap \
-		--variant=minbase \
-		--include=udev,systemd,systemd-sysv,procps,libseccomp2 \
-		stretch \
-		"$(WORKDIR)" $(DEBMIRROR)
-	rm -rf "$(WORKDIR)/var/cache/apt/archives" \
-	       "$(WORKDIR)/usr/share/doc" \
-	       "$(WORKDIR)/var/lib/apt/lists"
-	mkdir -p $(DIRS)
-	touch $@
-
-rootfs.img: files_common_stamp files_debootstrap_stamp files_ephemeral_stamp
-	mksquashfs "$(WORKDIR)" rootfs.img -noappend
-
-builder: builder_stamp
-
-builder_stamp:
-	docker build -t fc-image-builder -f Dockerfile.debian-image .
-	touch $@
-
-# For any given target, append "-in-docker" to it to run the build
-# recipe in a container, e.g. instead of:
-# $ make rootfs.img
-# you can use
-# $ make rootfs.img-in-docker
-%-in-docker: builder_stamp
-	docker run --rm \
-		--security-opt=apparmor=unconfined \
-		-it --volume $(CURDIR):/src \
-		--cap-add=sys_admin \
-		--cap-add=sys_chroot \
-		--env=DEBMIRROR \
-		fc-image-builder:latest $(subst -in-docker,,$@)
-
-clean:
-	-rm -f *stamp
-	-rm -rf $(WORKDIR)
-	-rm -f rootfs.img
-
-distclean: clean-in-docker
-	rm -rf files_ephemeral
-	docker rmi fc-image-builder:latest
-
-.PHONY: debootstrap clean distclean builder %-in-docker
diff --git a/tools/image-builder/README.md b/tools/image-builder/README.md
deleted file mode 100644
index eb1a7dc..0000000
--- a/tools/image-builder/README.md
+++ /dev/null
@@ -1,53 +0,0 @@
-# Create Firecracker VM images for use with firecracker-containerd #
-
-## Debian root image ##
-
-### Overview ###
-
-The image builder component of firecracker-containerd will build a
-microVM image including the necessary components to support container
-management inside the microVM. In particular, the
-firecracker-containerd runtime agent and runc binary will be installed
-in the image.
-
-The image is generated as a read-only squashfs image. A read/write
-overlay layer is supported via the /sbin/overlay-init program, which
-should be used as init (e.g. by passing `init=/sbin/overlay-init` as a
-kernel boot parameter). By default, overlay-init allocates a tmpfs
-filesystem for use as the upper layer, but a block device can be
-provided via the `overlay_root` kernel parameter,
-e.g. `overlay_root=vdc`. This device should already contain a
-(possibly empty) ext4 filesystem. By using a block device, it is
-possible to preserve the filesystem state beyond the termination of
-the VM, and potentially re-use it for subsequent VM execution.
-
-The image currently expects `vdb` to be the block device containing
-the container root filesystem, and this device is mounted on
-`/container/rootfs`.
-
-If the `vsock_srv` program from the
-[clownix](https://github.com/clownix/cloonix_vsock) github repository
-is present in `ephemeral_files/bin/` when the image is built, it will
-be embedded in the image and run at boot, allowing easy shell access
-to the VM. This can be convenient for debugging, but it is not
-embedded by default for security and licensing reasons.
-
-### Generation ###
-
-There are two alternatives for providing the build environment. You
-can perform the image build in Docker, in which case the only
-build-time dependency is that you can launch Docker container directly
-(i.e. without `sudo`, etc). To build an image in this configuration, use:
-
-`$ make rootfs.img-in-docker`
-
-Alternatively, to build outside a container, you'll need:
-
-* To run the build process as root.
-* `[debootstrap](https://salsa.debian.org/installer-team/debootstrap)`
-  (Install via the package of the same name on Debian and Ubuntu)
-* `mksquashfs`, available in the
-   `[squashfs-tools](https://packages.debian.org/stretch/squashfs-tools)
-   package on Debian and Ubuntu.
-
-Then execute `make rootfs.img`
diff --git a/tools/image-builder/files_debootstrap/etc/hostname b/tools/image-builder/files_debootstrap/etc/hostname
deleted file mode 100644
index 181fd07..0000000
--- a/tools/image-builder/files_debootstrap/etc/hostname
+++ /dev/null
@@ -1,1 +0,0 @@
-microvm
\ No newline at end of file
diff --git a/tools/image-builder/files_debootstrap/etc/systemd/system/apt-daily-upgrade.timer b/tools/image-builder/files_debootstrap/etc/systemd/system/apt-daily-upgrade.timer
deleted file mode 120000
index dc1dc0c..0000000
--- a/tools/image-builder/files_debootstrap/etc/systemd/system/apt-daily-upgrade.timer
+++ /dev/null
@@ -1,1 +0,0 @@
-/dev/null
\ No newline at end of file
diff --git a/tools/image-builder/files_debootstrap/etc/systemd/system/apt-daily.timer b/tools/image-builder/files_debootstrap/etc/systemd/system/apt-daily.timer
deleted file mode 120000
index dc1dc0c..0000000
--- a/tools/image-builder/files_debootstrap/etc/systemd/system/apt-daily.timer
+++ /dev/null
@@ -1,1 +0,0 @@
-/dev/null
\ No newline at end of file
diff --git a/tools/image-builder/files_debootstrap/etc/systemd/system/container-rootfs.mount b/tools/image-builder/files_debootstrap/etc/systemd/system/container-rootfs.mount
deleted file mode 100644
index fedf6ca..0000000
--- a/tools/image-builder/files_debootstrap/etc/systemd/system/container-rootfs.mount
+++ /dev/null
@@ -1,9 +0,0 @@
-[Unit]
-Description=Single container root filesystem
-ConditionPathIsDirectory=/container/rootfs
-DefaultDependencies=no
-Before=local-fs.target
-
-[Mount]
-What=/dev/vdb
-Where=/container/rootfs
diff --git a/tools/image-builder/files_debootstrap/etc/systemd/system/firecracker-agent.service b/tools/image-builder/files_debootstrap/etc/systemd/system/firecracker-agent.service
deleted file mode 100644
index 2c8e239..0000000
--- a/tools/image-builder/files_debootstrap/etc/systemd/system/firecracker-agent.service
+++ /dev/null
@@ -1,12 +0,0 @@
-[Unit]
-Description=Firecracker VM agent
-StartLimitIntervalSec=2
-ConditionPathIsMountPoint=/container/rootfs
-After=local-fs.target
-Requires=local-fs.target
-
-[Service]
-Type=simple
-WorkingDirectory=/container
-ExecStart=/usr/local/bin/agent -id 1
-Restart=always
diff --git a/tools/image-builder/files_debootstrap/etc/systemd/system/firecracker.target b/tools/image-builder/files_debootstrap/etc/systemd/system/firecracker.target
deleted file mode 100644
index f82ccc2..0000000
--- a/tools/image-builder/files_debootstrap/etc/systemd/system/firecracker.target
+++ /dev/null
@@ -1,6 +0,0 @@
-[Unit]
-Description=Firecracker containerd VM
-Requires=basic.target
-Conflicts=rescue.service rescue.target
-After=basic.target rescue.service rescue.target
-AllowIsolate=yes
diff --git a/tools/image-builder/files_debootstrap/etc/systemd/system/firecracker.target.wants/container-rootfs.mount b/tools/image-builder/files_debootstrap/etc/systemd/system/firecracker.target.wants/container-rootfs.mount
deleted file mode 120000
index 9481a79..0000000
--- a/tools/image-builder/files_debootstrap/etc/systemd/system/firecracker.target.wants/container-rootfs.mount
+++ /dev/null
@@ -1,1 +0,0 @@
-/etc/systemd/system/container-rootfs.mount
\ No newline at end of file
diff --git a/tools/image-builder/files_debootstrap/etc/systemd/system/firecracker.target.wants/firecracker-agent.service b/tools/image-builder/files_debootstrap/etc/systemd/system/firecracker.target.wants/firecracker-agent.service
deleted file mode 120000
index f5467fc..0000000
--- a/tools/image-builder/files_debootstrap/etc/systemd/system/firecracker.target.wants/firecracker-agent.service
+++ /dev/null
@@ -1,1 +0,0 @@
-/etc/systemd/system/firecracker-agent.service
\ No newline at end of file
diff --git a/tools/image-builder/files_debootstrap/etc/systemd/system/firecracker.target.wants/getty.target b/tools/image-builder/files_debootstrap/etc/systemd/system/firecracker.target.wants/getty.target
deleted file mode 120000
index d6896c3..0000000
--- a/tools/image-builder/files_debootstrap/etc/systemd/system/firecracker.target.wants/getty.target
+++ /dev/null
@@ -1,1 +0,0 @@
-/lib/systemd/system/getty.target
\ No newline at end of file
diff --git a/tools/image-builder/files_debootstrap/etc/systemd/system/firecracker.target.wants/vsock-cli.service b/tools/image-builder/files_debootstrap/etc/systemd/system/firecracker.target.wants/vsock-cli.service
deleted file mode 120000
index 2efa166..0000000
--- a/tools/image-builder/files_debootstrap/etc/systemd/system/firecracker.target.wants/vsock-cli.service
+++ /dev/null
@@ -1,1 +0,0 @@
-/etc/systemd/system/vsock-cli.service
\ No newline at end of file
diff --git a/tools/image-builder/files_debootstrap/etc/systemd/system/getty-static.service b/tools/image-builder/files_debootstrap/etc/systemd/system/getty-static.service
deleted file mode 120000
index dc1dc0c..0000000
--- a/tools/image-builder/files_debootstrap/etc/systemd/system/getty-static.service
+++ /dev/null
@@ -1,1 +0,0 @@
-/dev/null
\ No newline at end of file
diff --git a/tools/image-builder/files_debootstrap/etc/systemd/system/rom-dev.mount b/tools/image-builder/files_debootstrap/etc/systemd/system/rom-dev.mount
deleted file mode 120000
index dc1dc0c..0000000
--- a/tools/image-builder/files_debootstrap/etc/systemd/system/rom-dev.mount
+++ /dev/null
@@ -1,1 +0,0 @@
-/dev/null
\ No newline at end of file
diff --git a/tools/image-builder/files_debootstrap/etc/systemd/system/rom-overlay.mount b/tools/image-builder/files_debootstrap/etc/systemd/system/rom-overlay.mount
deleted file mode 120000
index dc1dc0c..0000000
--- a/tools/image-builder/files_debootstrap/etc/systemd/system/rom-overlay.mount
+++ /dev/null
@@ -1,1 +0,0 @@
-/dev/null
\ No newline at end of file
diff --git a/tools/image-builder/files_debootstrap/etc/systemd/system/rom.mount b/tools/image-builder/files_debootstrap/etc/systemd/system/rom.mount
deleted file mode 120000
index dc1dc0c..0000000
--- a/tools/image-builder/files_debootstrap/etc/systemd/system/rom.mount
+++ /dev/null
@@ -1,1 +0,0 @@
-/dev/null
\ No newline at end of file
diff --git a/tools/image-builder/files_debootstrap/etc/systemd/system/systemd-timesyncd.service b/tools/image-builder/files_debootstrap/etc/systemd/system/systemd-timesyncd.service
deleted file mode 120000
index dc1dc0c..0000000
--- a/tools/image-builder/files_debootstrap/etc/systemd/system/systemd-timesyncd.service
+++ /dev/null
@@ -1,1 +0,0 @@
-/dev/null
\ No newline at end of file
diff --git a/tools/image-builder/files_debootstrap/etc/systemd/system/vsock-cli.service b/tools/image-builder/files_debootstrap/etc/systemd/system/vsock-cli.service
deleted file mode 100644
index 57c596b..0000000
--- a/tools/image-builder/files_debootstrap/etc/systemd/system/vsock-cli.service
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"). You may
-# not use this file except in compliance with the License. A copy of the
-# License is located at
-#
-#       http://aws.amazon.com/apache2.0/
-#
-# or in the "license" file accompanying this file. This file is distributed
-# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-# express or implied. See the License for the specific language governing
-# permissions and limitations under the License.
-#
-# If the vsock_srv program from
-# https://github.com/clownix/cloonix_vsock is present, let's run it as
-# a service. It's useful for debug and test, but we don't want it
-# installed by default.
-
-[Unit]
-Description=vsock login service
-ConditionFileIsExecutable=/bin/vsock_srv
-StartLimitIntervalSec=2
-
-[Service]
-Type=forking
-ExecStart=/bin/vsock_srv 9999
-GuessMainPID=yes
-Restart=always
diff --git a/tools/image-builder/files_debootstrap/sbin/overlay-init b/tools/image-builder/files_debootstrap/sbin/overlay-init
deleted file mode 100755
index a27b845..0000000
--- a/tools/image-builder/files_debootstrap/sbin/overlay-init
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/bin/sh
-# Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"). You may
-# not use this file except in compliance with the License. A copy of the
-# License is located at
-#
-#       http://aws.amazon.com/apache2.0/
-#
-# or in the "license" file accompanying this file. This file is distributed
-# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
-# express or implied. See the License for the specific language governing
-# permissions and limitations under the License.
-
-set -x
-
-# Parameters:
-# 1. rw_root -- path where the read/write root is mounted
-# 2. work_dir -- path to the overlay workdir (must be on same filesystem as rw_root)
-# Overlay will be set up on /mnt, original root on /mnt/rom
-pivot() {
-    local rw_root work_dir
-    rw_root="$1"
-    work_dir="$2"
-    /bin/mount \
-	-o noatime,lowerdir=/,upperdir=${rw_root},workdir=${work_dir} \
-	-t overlay "overlayfs:${rw_root}" /mnt
-    pivot_root /mnt /mnt/rom
-}
-
-# Overlay is configured under /overlay
-# Global variable $overlay_root is expected to be set to either:
-# "ram", which configures a tmpfs as the rw overlay layer (this is
-# the default, if the variable is unset)
-# - or -
-# A block device name, relative to /dev, in which case it is assumed
-# to contain an ext4 filesystem suitable for use as a rw overlay
-# layer. e.g. "vdb"
-do_overlay() {
-    local overlay_dir="/overlay"
-    if [ "$overlay_root" = ram ] ||
-           [ -z "$overlay_root" ]; then
-        /bin/mount -t tmpfs -o noatime,mode=0755 tmpfs /overlay
-    else
-        /bin/mount -t ext4 "/dev/$overlay_root" /overlay
-    fi
-    mkdir -p /overlay/root /overlay/work
-    pivot /overlay/root /overlay/work
-}
-
-# If we're given an overlay, ensure that it really exists. Panic if not.
-if [ -n "$overlay_root" ] &&
-       [ "$overlay_root" != ram ] &&
-       [ ! -b "/dev/$overlay_root" ]; then
-    echo -n "FATAL: "
-    echo "Overlay root given as $overlay_root but /dev/$overlay_root does not exist"
-    exit 1
-fi
-
-do_overlay
-
-# invoke the actual system init program and procede with the boot
-# process.
-exec /sbin/init $@


Test patch:
diff --git a/firecracker-control/helpers_test.go b/firecracker-control/helpers_test.go
index 02c6a77..fcb5a6f 100644
--- a/firecracker-control/helpers_test.go
+++ b/firecracker-control/helpers_test.go
@@ -17,209 +17,87 @@ import (
 	"testing"
 
 	"github.com/firecracker-microvm/firecracker-go-sdk"
-	"github.com/stretchr/testify/assert"
-
 	models "github.com/firecracker-microvm/firecracker-go-sdk/client/models"
+	"github.com/stretchr/testify/assert"
 
 	"github.com/firecracker-microvm/firecracker-containerd/proto"
 )
 
 const (
-	oneTimeBurst  = 800
-	refillTime    = 1000
-	capacity      = 400
-	rootDrivePath = "/path/to/root/drive"
-	loopDrivePath = "/path/to/loop/drive"
-	loopDriveRO   = false
-	memSize       = 4096
-	vcpuCount     = 2
-	mac           = "AA:FC:00:00:00:01"
-	hostDevName   = "tap0"
+	oneTimeBurst = 800
+	refillTime   = 1000
+	capacity     = 400
+	memSize      = 4096
+	vcpuCount    = 2
+	mac          = "AA:FC:00:00:00:01"
+	hostDevName  = "tap0"
 )
 
-var (
-	defaultDrivesBuilder = firecracker.NewDrivesBuilder(rootDrivePath).AddDrive(
-		loopDrivePath, loopDriveRO)
-	defaultDrives             = defaultDrivesBuilder.Build()
-	overrideRootDrivesBuilder = firecracker.NewDrivesBuilder(loopDrivePath)
-	additionalDrivesBuilder   = firecracker.NewDrivesBuilder(rootDrivePath).AddDrive(
-		loopDrivePath, loopDriveRO).AddDrive(
-		loopDrivePath, loopDriveRO, func(d *models.Drive) {
-			d.RateLimiter = &models.RateLimiter{
-				Ops: &models.TokenBucket{
-					OneTimeBurst: firecracker.Int64(oneTimeBurst),
-					RefillTime:   firecracker.Int64(refillTime),
-					Size:         firecracker.Int64(capacity),
-				},
-			}
-		})
-)
+func TestMachineConfigurationFromProto(t *testing.T) {
+	config := machineConfigurationFromProto(&proto.FirecrackerMachineConfiguration{
+		CPUTemplate: string(models.CPUTemplateC3),
+		VcpuCount:   vcpuCount,
+		MemSizeMib:  memSize,
+		HtEnabled:   true,
+	})
 
-func TestOverrideVMConfigFromTaskOpts(t *testing.T) {
-	var testCases = []struct {
-		name            string
-		in              firecracker.Config
-		inDrivesBuilder firecracker.DrivesBuilder
-		vmConfig        *proto.FirecrackerConfig
-		expectedOut     firecracker.Config
-		expectedDrives  []models.Drive
-		expectedErr     bool
-	}{
-		{
-			name:            "network config",
-			in:              firecracker.Config{},
-			inDrivesBuilder: defaultDrivesBuilder,
-			vmConfig: &proto.FirecrackerConfig{
-				NetworkInterfaces: []*proto.FirecrackerNetworkInterface{
-					{
-						MacAddress:  mac,
-						HostDevName: hostDevName,
-						AllowMMDS:   true,
-						InRateLimiter: &proto.FirecrackerRateLimiter{
-							Bandwidth: &proto.FirecrackerTokenBucket{
-								OneTimeBurst: oneTimeBurst,
-								RefillTime:   refillTime,
-								Capacity:     capacity,
-							},
-						},
-						OutRateLimiter: &proto.FirecrackerRateLimiter{
-							Bandwidth: &proto.FirecrackerTokenBucket{
-								OneTimeBurst: oneTimeBurst,
-								RefillTime:   refillTime,
-								Capacity:     capacity,
-							},
-						},
-					},
-					{
-						MacAddress:  mac,
-						HostDevName: hostDevName,
-					},
-				},
-			},
-			expectedErr: false,
-			expectedOut: firecracker.Config{
-				NetworkInterfaces: []firecracker.NetworkInterface{
-					{
-						MacAddress:  mac,
-						HostDevName: hostDevName,
-						AllowMMDS:   true,
-						InRateLimiter: &models.RateLimiter{
-							Bandwidth: &models.TokenBucket{
-								OneTimeBurst: firecracker.Int64(oneTimeBurst),
-								RefillTime:   firecracker.Int64(refillTime),
-								Size:         firecracker.Int64(capacity),
-							},
-						},
-						OutRateLimiter: &models.RateLimiter{
-							Bandwidth: &models.TokenBucket{
-								OneTimeBurst: firecracker.Int64(oneTimeBurst),
-								RefillTime:   firecracker.Int64(refillTime),
-								Size:         firecracker.Int64(capacity),
-							},
-						},
-					},
-					{
-						MacAddress:  mac,
-						HostDevName: hostDevName,
-					},
-				},
-			},
-			expectedDrives: defaultDrives,
-		},
-		{
-			name:            "machine config",
-			in:              firecracker.Config{},
-			inDrivesBuilder: defaultDrivesBuilder,
-			vmConfig: &proto.FirecrackerConfig{
-				MachineCfg: &proto.FirecrackerMachineConfiguration{
-					MemSizeMib: memSize,
-					VcpuCount:  vcpuCount,
-				},
-			},
-			expectedErr: false,
-			expectedOut: firecracker.Config{
-				MachineCfg: models.MachineConfiguration{
-					MemSizeMib: memSize,
-					VcpuCount:  vcpuCount,
-				},
-			},
-			expectedDrives: defaultDrivesBuilder.Build(),
-		},
-		{
-			name:            "invalid memory in machine config",
-			in:              firecracker.Config{},
-			inDrivesBuilder: defaultDrivesBuilder,
-			vmConfig: &proto.FirecrackerConfig{
-				MachineCfg: &proto.FirecrackerMachineConfiguration{
-					VcpuCount: vcpuCount,
-				},
-			},
-			expectedErr:    true,
-			expectedOut:    firecracker.Config{},
-			expectedDrives: defaultDrivesBuilder.Build(),
-		},
-		{
-			name:            "invalid vcpu count in machine config",
-			in:              firecracker.Config{},
-			inDrivesBuilder: defaultDrivesBuilder,
-			vmConfig: &proto.FirecrackerConfig{
-				MachineCfg: &proto.FirecrackerMachineConfiguration{
-					MemSizeMib: memSize,
-				},
-			},
-			expectedErr:    true,
-			expectedOut:    firecracker.Config{},
-			expectedDrives: defaultDrivesBuilder.Build(),
-		},
-		{
-			name:            "storage config overrides root drive",
-			in:              firecracker.Config{},
-			inDrivesBuilder: overrideRootDrivesBuilder,
-			vmConfig: &proto.FirecrackerConfig{
-				RootDrive: &proto.FirecrackerDrive{
-					PathOnHost: loopDrivePath,
-				},
-			},
-			expectedErr:    false,
-			expectedOut:    firecracker.Config{},
-			expectedDrives: overrideRootDrivesBuilder.Build(),
-		},
-		{
-			name:            "storage config adds additional drives",
-			in:              firecracker.Config{},
-			inDrivesBuilder: defaultDrivesBuilder,
-			vmConfig: &proto.FirecrackerConfig{
-				AdditionalDrives: []*proto.FirecrackerDrive{
-					{
-						PathOnHost: loopDrivePath,
-						RateLimiter: &proto.FirecrackerRateLimiter{
-							Ops: &proto.FirecrackerTokenBucket{
-								OneTimeBurst: oneTimeBurst,
-								RefillTime:   refillTime,
-								Capacity:     capacity,
-							},
-						},
-					},
-				},
-			},
-			expectedErr:    false,
-			expectedOut:    firecracker.Config{},
-			expectedDrives: additionalDrivesBuilder.Build(),
-		},
+	assert.EqualValues(t, models.CPUTemplateC3, config.CPUTemplate)
+	assert.EqualValues(t, vcpuCount, config.VcpuCount)
+	assert.EqualValues(t, memSize, config.MemSizeMib)
+	assert.True(t, config.HtEnabled)
+}
+
+func TestDefaultMachineConfigurationFromProto(t *testing.T) {
+	configs := map[string]models.MachineConfiguration{
+		"Nil":          machineConfigurationFromProto(nil),
+		"Empty struct": machineConfigurationFromProto(&proto.FirecrackerMachineConfiguration{}),
 	}
-	for _, tc := range testCases {
-		// scopelint gets sad if we use tc directly. The specific complaint is
-		// "Using the variable on range scope in function literal".
-		test := tc
-		t.Run(test.name, func(t *testing.T) {
-			out, drivesBuilder, err := overrideVMConfigFromTaskOpts(test.in, test.vmConfig, test.inDrivesBuilder)
-			if test.expectedErr {
-				assert.Error(t, err)
-			} else {
-				assert.NoError(t, err)
-				assert.Equal(t, test.expectedOut, out)
-				assert.Equal(t, test.expectedDrives, drivesBuilder.Build())
-			}
+
+	for name, config := range configs {
+		cfg := config
+		t.Run(name, func(t *testing.T) {
+			assert.EqualValues(t, defaultCPUTemplate, cfg.CPUTemplate)
+			assert.EqualValues(t, defaultCPUCount, cfg.VcpuCount)
+			assert.EqualValues(t, defaultMemSizeMb, cfg.MemSizeMib)
+			assert.False(t, cfg.HtEnabled)
 		})
 	}
 }
+
+func TestNetworkConfigFromProto(t *testing.T) {
+	network := networkConfigFromProto(&proto.FirecrackerNetworkInterface{
+		MacAddress:  mac,
+		HostDevName: hostDevName,
+		AllowMMDS:   true,
+	})
+
+	assert.Equal(t, mac, network.MacAddress)
+	assert.Equal(t, hostDevName, network.HostDevName)
+	assert.True(t, network.AllowMMDS)
+	assert.Nil(t, network.InRateLimiter)
+	assert.Nil(t, network.OutRateLimiter)
+}
+
+func TestTokenBucketFromProto(t *testing.T) {
+	bucket := tokenBucketFromProto(&proto.FirecrackerTokenBucket{
+		OneTimeBurst: oneTimeBurst,
+		RefillTime:   refillTime,
+		Capacity:     capacity,
+	})
+
+	assert.EqualValues(t, oneTimeBurst, *bucket.OneTimeBurst)
+	assert.EqualValues(t, refillTime, *bucket.RefillTime)
+	assert.EqualValues(t, capacity, *bucket.Size)
+}
+
+func TestAddDriveFromProto(t *testing.T) {
+	list := addDriveFromProto(firecracker.DrivesBuilder{}, &proto.FirecrackerDrive{
+		IsReadOnly: true,
+		PathOnHost: "/a",
+		Partuuid:   "xy",
+	}).Build()
+
+	assert.Equal(t, "/a", *list[0].PathOnHost)
+	assert.Equal(t, "xy", list[0].Partuuid)
+	assert.True(t, *list[0].IsReadOnly)
+}
diff --git a/firecracker-control/local_test.go b/firecracker-control/local_test.go
index f826390..5bf35f4 100644
--- a/firecracker-control/local_test.go
+++ b/firecracker-control/local_test.go
@@ -76,22 +76,6 @@ func TestLocal_buildVMConfiguration(t *testing.T) {
 	assert.True(t, ni.AllowMMDS)
 }
 
-func TestLocal_buildInvalidConfiguration(t *testing.T) {
-	obj := &local{
-		rootPath:    "/",
-		findVsockFn: func(context.Context) (*os.File, uint32, error) { return nil, 3, nil },
-	}
-
-	request := &proto.CreateVMRequest{}
-
-	_, err := obj.buildVMConfiguration(testCtx, "1", 3, request)
-	assert.EqualError(t, err, "invalid machine configuration")
-
-	request.MachineCfg = &proto.FirecrackerMachineConfiguration{}
-	_, err = obj.buildVMConfiguration(testCtx, "1", 3, request)
-	assert.EqualError(t, err, "root drive can't be empty")
-}
-
 func TestLocal_startMachine(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()