Repository: firecracker-microvm/firecracker-containerd
Pull request #109 (2019-02-21 16:44:01)

More firecracker opts
*Issue #, if available:* #76

*Description of changes:* Added protobuf message types for all of the options listed in #76. The `taskworkflow` example has also been updated to get rid of the manual kernel args override step with these changes.

*Testing Done:* Added additional unit tests. Verified the the `taskworkflow` example executes without errors as well.

```bash
#### Verify that there's no kernel arg override for ip, gateway, netmask in `/etc/containerd/firecracker-runtime.json`
$ cat /etc/containerd/firecracker-runtime.json | grep kernel_args
  "kernel_args": "console=ttyS0 noapic reboot=k panic=1 pci=off nomodules rw",

#### Execute the example
$ sudo ./taskworkflow -ip 172.16.0.2 -gw 172.16.0.1 -mask 255.255.255.0
2019/02/21 16:42:57.390384 Creating containerd client
2019/02/21 16:42:57.391192 Created containerd client
2019/02/21 16:43:06.090317 Successfully pulled docker.io/library/nginx:latest image
2019/02/21 16:43:08.279340 Successfully created task: demo for the container
2019/02/21 16:43:08.279363 Completed waiting for the container task
2019/02/21 16:43:08.307723 Successfully started the container task
2019/02/21 16:43:11.307840 Executing http GET on 172.16.0.2
2019/02/21 16:43:11.309085 Response from [172.16.0.2]:
[<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
]
172.16.0.1 - - [21/Feb/2019:16:43:11 +0000] "GET / HTTP/1.1" 200 612 "-" "Go-http-client/1.1" "-"
```


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.


samuelkarp: @aaithal We just added CI to this repo.  Would you be able to rebase on top of master, and then add an entry to the .buildkite/pipeline.yml similar to https://github.com/firecracker-microvm/firecracker-containerd/pull/113 to run the new tests you're adding?
aaithal: > @aaithal We just added CI to this repo. Would you be able to rebase on top of master, and then add an entry to the .buildkite/pipeline.yml similar to #113 to run the new tests you're adding?

Yeah, looking into doing that!

Patch:
diff --git a/examples/Makefile b/examples/Makefile
index 86ede77..f581d62 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -21,4 +21,6 @@ taskworkflow: taskworkflow.go
 clean:
 	- rm -f taskworkflow
 
-.PHONY: all examples clean
+install:
+
+.PHONY: all examples clean install
diff --git a/examples/taskworkflow.go b/examples/taskworkflow.go
index 42a53ec..baf2a0c 100644
--- a/examples/taskworkflow.go
+++ b/examples/taskworkflow.go
@@ -16,6 +16,7 @@ package main
 import (
 	"context"
 	"flag"
+	"fmt"
 	"io/ioutil"
 	"log"
 	"net/http"
@@ -31,16 +32,27 @@ import (
 	"github.com/pkg/errors"
 )
 
+const (
+	kernelArgsFormat = "console=ttyS0 noapic reboot=k panic=1 pci=off nomodules rw ip=%s::%s:%s:::off::::"
+	macAddress       = "AA:FC:00:00:00:01"
+	hostDevName      = "tap0"
+)
+
 func main() {
-	var ip = flag.String("ip", "", "ip address assigned to the container")
+	var ip = flag.String("ip", "", "ip address assigned to the container. Example: -ip 172.16.0.1")
+	var gateway = flag.String("gw", "", "gateway ip address. Example: -gw 172.16.0.1")
+	var netMask = flag.String("mask", "", "subnet gatway mask. Example: -mask 255.255.255.0")
 	log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)
 	flag.Parse()
-	if err := taskWorkflow(*ip); err != nil {
+	if *ip != "" && (*gateway == "" || *netMask == "") {
+		log.Fatal("Incorrect usage. 'gw' and 'mask' need to be specified when 'ip' is specified")
+	}
+	if err := taskWorkflow(*ip, *gateway, *netMask); err != nil {
 		log.Fatal(err)
 	}
 }
 
-func taskWorkflow(containerIP string) error {
+func taskWorkflow(containerIP string, gateway string, netMask string) error {
 	log.Println("Creating containerd client")
 	client, err := containerd.New("/run/containerd/containerd.sock")
 	if err != nil {
@@ -80,8 +92,24 @@ func taskWorkflow(containerIP string) error {
 
 	task, err := container.NewTask(ctx,
 		cio.NewCreator(cio.WithStdio),
-		withFirecrackerConfig(containerIP, "AA:FC:00:00:00:01", "tap0"),
-	)
+		func(ctx context.Context, _ *containerd.Client, ti *containerd.TaskInfo) error {
+			if containerIP == "" {
+				return nil
+			}
+			// An IP address for the container has been provided. Configure
+			// the VM opts accordingly.
+			firecrackerConfig := &proto.FirecrackerConfig{
+				NetworkInterfaces: []*proto.FirecrackerNetworkInterface{
+					{
+						MacAddress:  macAddress,
+						HostDevName: hostDevName,
+					},
+				},
+				KernelArgs: fmt.Sprintf(kernelArgsFormat, containerIP, gateway, netMask),
+			}
+			ti.Options = firecrackerConfig
+			return nil
+		})
 	if err != nil {
 		return errors.Wrapf(err, "creating task")
 
@@ -123,23 +151,6 @@ func taskWorkflow(containerIP string) error {
 	return nil
 }
 
-func withFirecrackerConfig(containerIP string, mac string, hostDevName string) containerd.NewTaskOpts {
-	return func(ctx context.Context, _ *containerd.Client, ti *containerd.TaskInfo) error {
-		if containerIP != "" {
-			firecrackerConfig := &proto.FirecrackerConfig{
-				NetworkInterfaces: []*proto.FirecrackerNetworkInterface{
-					{
-						MacAddress:  mac,
-						HostDevName: hostDevName,
-					},
-				},
-			}
-			ti.Options = firecrackerConfig
-		}
-		return nil
-	}
-}
-
 func getResponse(containerIP string) {
 	response, err := http.Get("http://" + containerIP)
 	if err != nil {
diff --git a/examples/taskworkflow.md b/examples/taskworkflow.md
index 9db7144..641a4a5 100644
--- a/examples/taskworkflow.md
+++ b/examples/taskworkflow.md
@@ -17,31 +17,24 @@ $ sudo /path/to/firecracker-containerd/examples/taskworkflow
 For a workflow with networking setup for the container, create a tap device
 for the VM by following [these instructions](https://github.com/firecracker-microvm/firecracker/blob/master/docs/network-setup.md).
 
-This will create a tap device named `tap0`. Now, edit the kernel args in the
-firecracker-containerd config to set a static ip address from the
-`172.16.0.1/24` subnet. The following example sets it to `172.16.0.2`.
+This creates a tap device named `tap0`, in the local `172.16.0.1/24` subnet.
+Since the example does not rely on a DHCP client running within the VM to
+initialize the network interface, `gw` and `mask` flags should be used to
+specify the gateway and subnet mask values.
+
+The following example sets:
+* The IP address to `172.16.0.2`
+* The gateway IP address to `172.16.0.1`
+* The subnet mask to `255.255.255.0` (`/24`)
+
 ** NOTE: This example will not work if you're running more than 1 container
 on a host at the same time **
-```bash
-$ cat /etc/containerd/firecracker-runtime.json
-{
-    "firecracker_binary_path": "/usr/local/bin/firecracker",
-    "socket_path": "./firecracker.sock",
-    "kernel_image_path": "/var/lib/firecracker-containerd/runtime/vmlinux-2018-07-03",
-    "kernel_args": "console=ttyS0 noapic reboot=k panic=1 pci=off nomodules rw ip=172.16.0.2::172.16.0.1:255.255.255.0:::off::::",
-    "root_drive": "/var/lib/firecracker-containerd/runtime/amzn2-firecracker-fc-ctrd-2.0.20190130.runc-x86_64.ext4",
-    "cpu_count": 1,
-    "cpu_template": "T2",
-    "console": "stdio",
-    "log_fifo": "/tmp/fc-logs.fifo",
-    "log_level": "Debug",
-    "metrics_fifo": "/tmp/fc-metrics.fifo"
-}
-```
 
 Now, run the example by passing the `-ip` argument:
 ```bash
-$ sudo /path/to/firecracker-containerd/examples/taskworkflow -ip 172.16.0.2
+$ sudo /path/to/firecracker-containerd/examples/taskworkflow -ip 172.16.0.2 \
+    -gw 172.16.0.1 \
+    -mask 255.255.255.0
 ```
 
 You should see output similar to this:
diff --git a/go.mod b/go.mod
index d140bc4..08ff353 100644
--- a/go.mod
+++ b/go.mod
@@ -18,7 +18,7 @@ require (
 	github.com/docker/docker v1.13.1 // indirect
 	github.com/docker/go-events v0.0.0-20170721190031-9461782956ad // indirect
 	github.com/docker/go-units v0.3.3
-	github.com/firecracker-microvm/firecracker-go-sdk v0.0.0-20190111234345-e4d80c16bbb3
+	github.com/firecracker-microvm/firecracker-go-sdk v0.0.0-20190219211450-78fe329f36bd
 	github.com/godbus/dbus v0.0.0-20181025153459-66d97aec3384 // indirect
 	github.com/gogo/googleapis v1.1.0 // indirect
 	github.com/gogo/protobuf v1.1.1
diff --git a/go.sum b/go.sum
index c4ee08e..429a048 100644
--- a/go.sum
+++ b/go.sum
@@ -44,6 +44,8 @@ github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk
 github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 github.com/firecracker-microvm/firecracker-go-sdk v0.0.0-20190111234345-e4d80c16bbb3 h1:gddXvvDqATNsbsLjEEUUJQa/yiqWDMjmt/q8o8lESDU=
 github.com/firecracker-microvm/firecracker-go-sdk v0.0.0-20190111234345-e4d80c16bbb3/go.mod h1:UOsA6IaI52MQntB9PrarJFReY1nA9iZg2DI04kxPNMY=
+github.com/firecracker-microvm/firecracker-go-sdk v0.0.0-20190219211450-78fe329f36bd h1:V3QbB4QSxJqMpezOWJVJ+65+FK8yTYOzz2s/jcOLWWg=
+github.com/firecracker-microvm/firecracker-go-sdk v0.0.0-20190219211450-78fe329f36bd/go.mod h1:UOsA6IaI52MQntB9PrarJFReY1nA9iZg2DI04kxPNMY=
 github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb h1:D4uzjWwKYQ5XnAvUbuvHW93esHg7F8N/OYeBBcJoTr0=
 github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
 github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
diff --git a/proto/types.pb.go b/proto/types.pb.go
index 8c800de..c6af90e 100644
--- a/proto/types.pb.go
+++ b/proto/types.pb.go
@@ -32,7 +32,7 @@ func (m *ExtraData) Reset()         { *m = ExtraData{} }
 func (m *ExtraData) String() string { return proto.CompactTextString(m) }
 func (*ExtraData) ProtoMessage()    {}
 func (*ExtraData) Descriptor() ([]byte, []int) {
-	return fileDescriptor_types_cfec9a3218eb42c5, []int{0}
+	return fileDescriptor_types_80133946767b2ac8, []int{0}
 }
 func (m *ExtraData) XXX_Unmarshal(b []byte) error {
 	return xxx_messageInfo_ExtraData.Unmarshal(m, b)
@@ -66,20 +66,114 @@ func (m *ExtraData) GetRuncOptions() *types.Any {
 	return nil
 }
 
+// Message to specify the various Firecracker VM configurations for a task
+type FirecrackerConfig struct {
+	RuncOptions       *types.Any                       `protobuf:"bytes,1,opt,name=RuncOptions,proto3" json:"RuncOptions,omitempty"`
+	NetworkInterfaces []*FirecrackerNetworkInterface   `protobuf:"bytes,2,rep,name=NetworkInterfaces,proto3" json:"NetworkInterfaces,omitempty"`
+	KernelImagePath   string                           `protobuf:"bytes,3,opt,name=KernelImagePath,proto3" json:"KernelImagePath,omitempty"`
+	KernelArgs        string                           `protobuf:"bytes,4,opt,name=KernelArgs,proto3" json:"KernelArgs,omitempty"`
+	MachineCfg        *FirecrackerMachineConfiguration `protobuf:"bytes,5,opt,name=MachineCfg,proto3" json:"MachineCfg,omitempty"`
+	RootDrive         *FirecrackerDrive                `protobuf:"bytes,6,opt,name=RootDrive,proto3" json:"RootDrive,omitempty"`
+	// Specifies the additional block device config for the VM. Note that
+	// the snapshotter used might itself present block devices to the be
+	// attached to the VM. This fields allows additional block devices to
+	// that list.
+	AdditionalDrives     []*FirecrackerDrive `protobuf:"bytes,7,rep,name=AdditionalDrives,proto3" json:"AdditionalDrives,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}            `json:"-"`
+	XXX_unrecognized     []byte              `json:"-"`
+	XXX_sizecache        int32               `json:"-"`
+}
+
+func (m *FirecrackerConfig) Reset()         { *m = FirecrackerConfig{} }
+func (m *FirecrackerConfig) String() string { return proto.CompactTextString(m) }
+func (*FirecrackerConfig) ProtoMessage()    {}
+func (*FirecrackerConfig) Descriptor() ([]byte, []int) {
+	return fileDescriptor_types_80133946767b2ac8, []int{1}
+}
+func (m *FirecrackerConfig) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_FirecrackerConfig.Unmarshal(m, b)
+}
+func (m *FirecrackerConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_FirecrackerConfig.Marshal(b, m, deterministic)
+}
+func (dst *FirecrackerConfig) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_FirecrackerConfig.Merge(dst, src)
+}
+func (m *FirecrackerConfig) XXX_Size() int {
+	return xxx_messageInfo_FirecrackerConfig.Size(m)
+}
+func (m *FirecrackerConfig) XXX_DiscardUnknown() {
+	xxx_messageInfo_FirecrackerConfig.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_FirecrackerConfig proto.InternalMessageInfo
+
+func (m *FirecrackerConfig) GetRuncOptions() *types.Any {
+	if m != nil {
+		return m.RuncOptions
+	}
+	return nil
+}
+
+func (m *FirecrackerConfig) GetNetworkInterfaces() []*FirecrackerNetworkInterface {
+	if m != nil {
+		return m.NetworkInterfaces
+	}
+	return nil
+}
+
+func (m *FirecrackerConfig) GetKernelImagePath() string {
+	if m != nil {
+		return m.KernelImagePath
+	}
+	return ""
+}
+
+func (m *FirecrackerConfig) GetKernelArgs() string {
+	if m != nil {
+		return m.KernelArgs
+	}
+	return ""
+}
+
+func (m *FirecrackerConfig) GetMachineCfg() *FirecrackerMachineConfiguration {
+	if m != nil {
+		return m.MachineCfg
+	}
+	return nil
+}
+
+func (m *FirecrackerConfig) GetRootDrive() *FirecrackerDrive {
+	if m != nil {
+		return m.RootDrive
+	}
+	return nil
+}
+
+func (m *FirecrackerConfig) GetAdditionalDrives() []*FirecrackerDrive {
+	if m != nil {
+		return m.AdditionalDrives
+	}
+	return nil
+}
+
 // Message to specify network config for a Firecracker VM
 type FirecrackerNetworkInterface struct {
-	MacAddress           string   `protobuf:"bytes,1,opt,name=MacAddress,proto3" json:"MacAddress,omitempty"`
-	HostDevName          string   `protobuf:"bytes,2,opt,name=HostDevName,proto3" json:"HostDevName,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	MacAddress           string                  `protobuf:"bytes,1,opt,name=MacAddress,proto3" json:"MacAddress,omitempty"`
+	HostDevName          string                  `protobuf:"bytes,2,opt,name=HostDevName,proto3" json:"HostDevName,omitempty"`
+	AllowMMDS            bool                    `protobuf:"varint,3,opt,name=AllowMMDS,proto3" json:"AllowMMDS,omitempty"`
+	InRateLimiter        *FirecrackerRateLimiter `protobuf:"bytes,4,opt,name=InRateLimiter,proto3" json:"InRateLimiter,omitempty"`
+	OutRateLimiter       *FirecrackerRateLimiter `protobuf:"bytes,5,opt,name=OutRateLimiter,proto3" json:"OutRateLimiter,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}                `json:"-"`
+	XXX_unrecognized     []byte                  `json:"-"`
+	XXX_sizecache        int32                   `json:"-"`
 }
 
 func (m *FirecrackerNetworkInterface) Reset()         { *m = FirecrackerNetworkInterface{} }
 func (m *FirecrackerNetworkInterface) String() string { return proto.CompactTextString(m) }
 func (*FirecrackerNetworkInterface) ProtoMessage()    {}
 func (*FirecrackerNetworkInterface) Descriptor() ([]byte, []int) {
-	return fileDescriptor_types_cfec9a3218eb42c5, []int{1}
+	return fileDescriptor_types_80133946767b2ac8, []int{2}
 }
 func (m *FirecrackerNetworkInterface) XXX_Unmarshal(b []byte) error {
 	return xxx_messageInfo_FirecrackerNetworkInterface.Unmarshal(m, b)
@@ -113,79 +207,321 @@ func (m *FirecrackerNetworkInterface) GetHostDevName() string {
 	return ""
 }
 
-// Message to specify the various Firecracker VM configurations for a task
-type FirecrackerConfig struct {
-	RuncOptions          *types.Any                     `protobuf:"bytes,1,opt,name=RuncOptions,proto3" json:"RuncOptions,omitempty"`
-	NetworkInterfaces    []*FirecrackerNetworkInterface `protobuf:"bytes,2,rep,name=NetworkInterfaces,proto3" json:"NetworkInterfaces,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}                       `json:"-"`
-	XXX_unrecognized     []byte                         `json:"-"`
-	XXX_sizecache        int32                          `json:"-"`
+func (m *FirecrackerNetworkInterface) GetAllowMMDS() bool {
+	if m != nil {
+		return m.AllowMMDS
+	}
+	return false
 }
 
-func (m *FirecrackerConfig) Reset()         { *m = FirecrackerConfig{} }
-func (m *FirecrackerConfig) String() string { return proto.CompactTextString(m) }
-func (*FirecrackerConfig) ProtoMessage()    {}
-func (*FirecrackerConfig) Descriptor() ([]byte, []int) {
-	return fileDescriptor_types_cfec9a3218eb42c5, []int{2}
+func (m *FirecrackerNetworkInterface) GetInRateLimiter() *FirecrackerRateLimiter {
+	if m != nil {
+		return m.InRateLimiter
+	}
+	return nil
 }
-func (m *FirecrackerConfig) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_FirecrackerConfig.Unmarshal(m, b)
+
+func (m *FirecrackerNetworkInterface) GetOutRateLimiter() *FirecrackerRateLimiter {
+	if m != nil {
+		return m.OutRateLimiter
+	}
+	return nil
 }
-func (m *FirecrackerConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_FirecrackerConfig.Marshal(b, m, deterministic)
+
+// Message to set the machine config for a Firecracker VM
+type FirecrackerMachineConfiguration struct {
+	CPUTemplate string `protobuf:"bytes,1,opt,name=CPUTemplate,proto3" json:"CPUTemplate,omitempty"`
+	HtEnabled   bool   `protobuf:"varint,2,opt,name=HtEnabled,proto3" json:"HtEnabled,omitempty"`
+	// Specifies the memory size of VM
+	// This lets us create a Firecracker VM of up to 4096 TiB, which
+	// for a mircroVM should be large enough
+	MemSizeMib           uint32   `protobuf:"varint,3,opt,name=MemSizeMib,proto3" json:"MemSizeMib,omitempty"`
+	VcpuCount            uint32   `protobuf:"varint,4,opt,name=VcpuCount,proto3" json:"VcpuCount,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
 }
-func (dst *FirecrackerConfig) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_FirecrackerConfig.Merge(dst, src)
+
+func (m *FirecrackerMachineConfiguration) Reset()         { *m = FirecrackerMachineConfiguration{} }
+func (m *FirecrackerMachineConfiguration) String() string { return proto.CompactTextString(m) }
+func (*FirecrackerMachineConfiguration) ProtoMessage()    {}
+func (*FirecrackerMachineConfiguration) Descriptor() ([]byte, []int) {
+	return fileDescriptor_types_80133946767b2ac8, []int{3}
 }
-func (m *FirecrackerConfig) XXX_Size() int {
-	return xxx_messageInfo_FirecrackerConfig.Size(m)
+func (m *FirecrackerMachineConfiguration) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_FirecrackerMachineConfiguration.Unmarshal(m, b)
 }
-func (m *FirecrackerConfig) XXX_DiscardUnknown() {
-	xxx_messageInfo_FirecrackerConfig.DiscardUnknown(m)
+func (m *FirecrackerMachineConfiguration) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_FirecrackerMachineConfiguration.Marshal(b, m, deterministic)
+}
+func (dst *FirecrackerMachineConfiguration) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_FirecrackerMachineConfiguration.Merge(dst, src)
+}
+func (m *FirecrackerMachineConfiguration) XXX_Size() int {
+	return xxx_messageInfo_FirecrackerMachineConfiguration.Size(m)
+}
+func (m *FirecrackerMachineConfiguration) XXX_DiscardUnknown() {
+	xxx_messageInfo_FirecrackerMachineConfiguration.DiscardUnknown(m)
 }
 
-var xxx_messageInfo_FirecrackerConfig proto.InternalMessageInfo
+var xxx_messageInfo_FirecrackerMachineConfiguration proto.InternalMessageInfo
 
-func (m *FirecrackerConfig) GetRuncOptions() *types.Any {
+func (m *FirecrackerMachineConfiguration) GetCPUTemplate() string {
 	if m != nil {
-		return m.RuncOptions
+		return m.CPUTemplate
+	}
+	return ""
+}
+
+func (m *FirecrackerMachineConfiguration) GetHtEnabled() bool {
+	if m != nil {
+		return m.HtEnabled
+	}
+	return false
+}
+
+func (m *FirecrackerMachineConfiguration) GetMemSizeMib() uint32 {
+	if m != nil {
+		return m.MemSizeMib
+	}
+	return 0
+}
+
+func (m *FirecrackerMachineConfiguration) GetVcpuCount() uint32 {
+	if m != nil {
+		return m.VcpuCount
+	}
+	return 0
+}
+
+// Message to specify the block device config for a Firecracker VM
+type FirecrackerDrive struct {
+	IsReadOnly           bool                    `protobuf:"varint,1,opt,name=IsReadOnly,proto3" json:"IsReadOnly,omitempty"`
+	IsRootDevice         bool                    `protobuf:"varint,2,opt,name=IsRootDevice,proto3" json:"IsRootDevice,omitempty"`
+	Partuuid             string                  `protobuf:"bytes,3,opt,name=Partuuid,proto3" json:"Partuuid,omitempty"`
+	PathOnHost           string                  `protobuf:"bytes,4,opt,name=PathOnHost,proto3" json:"PathOnHost,omitempty"`
+	RateLimiter          *FirecrackerRateLimiter `protobuf:"bytes,5,opt,name=RateLimiter,proto3" json:"RateLimiter,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}                `json:"-"`
+	XXX_unrecognized     []byte                  `json:"-"`
+	XXX_sizecache        int32                   `json:"-"`
+}
+
+func (m *FirecrackerDrive) Reset()         { *m = FirecrackerDrive{} }
+func (m *FirecrackerDrive) String() string { return proto.CompactTextString(m) }
+func (*FirecrackerDrive) ProtoMessage()    {}
+func (*FirecrackerDrive) Descriptor() ([]byte, []int) {
+	return fileDescriptor_types_80133946767b2ac8, []int{4}
+}
+func (m *FirecrackerDrive) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_FirecrackerDrive.Unmarshal(m, b)
+}
+func (m *FirecrackerDrive) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_FirecrackerDrive.Marshal(b, m, deterministic)
+}
+func (dst *FirecrackerDrive) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_FirecrackerDrive.Merge(dst, src)
+}
+func (m *FirecrackerDrive) XXX_Size() int {
+	return xxx_messageInfo_FirecrackerDrive.Size(m)
+}
+func (m *FirecrackerDrive) XXX_DiscardUnknown() {
+	xxx_messageInfo_FirecrackerDrive.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_FirecrackerDrive proto.InternalMessageInfo
+
+func (m *FirecrackerDrive) GetIsReadOnly() bool {
+	if m != nil {
+		return m.IsReadOnly
+	}
+	return false
+}
+
+func (m *FirecrackerDrive) GetIsRootDevice() bool {
+	if m != nil {
+		return m.IsRootDevice
+	}
+	return false
+}
+
+func (m *FirecrackerDrive) GetPartuuid() string {
+	if m != nil {
+		return m.Partuuid
+	}
+	return ""
+}
+
+func (m *FirecrackerDrive) GetPathOnHost() string {
+	if m != nil {
+		return m.PathOnHost
+	}
+	return ""
+}
+
+func (m *FirecrackerDrive) GetRateLimiter() *FirecrackerRateLimiter {
+	if m != nil {
+		return m.RateLimiter
 	}
 	return nil
 }
 
-func (m *FirecrackerConfig) GetNetworkInterfaces() []*FirecrackerNetworkInterface {
+// Message to specify an IO rate limiter with bytes/s and ops/s limits
+type FirecrackerRateLimiter struct {
+	Bandwidth            *FirecrackerTokenBucket `protobuf:"bytes,1,opt,name=Bandwidth,proto3" json:"Bandwidth,omitempty"`
+	Ops                  *FirecrackerTokenBucket `protobuf:"bytes,2,opt,name=Ops,proto3" json:"Ops,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}                `json:"-"`
+	XXX_unrecognized     []byte                  `json:"-"`
+	XXX_sizecache        int32                   `json:"-"`
+}
+
+func (m *FirecrackerRateLimiter) Reset()         { *m = FirecrackerRateLimiter{} }
+func (m *FirecrackerRateLimiter) String() string { return proto.CompactTextString(m) }
+func (*FirecrackerRateLimiter) ProtoMessage()    {}
+func (*FirecrackerRateLimiter) Descriptor() ([]byte, []int) {
+	return fileDescriptor_types_80133946767b2ac8, []int{5}
+}
+func (m *FirecrackerRateLimiter) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_FirecrackerRateLimiter.Unmarshal(m, b)
+}
+func (m *FirecrackerRateLimiter) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_FirecrackerRateLimiter.Marshal(b, m, deterministic)
+}
+func (dst *FirecrackerRateLimiter) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_FirecrackerRateLimiter.Merge(dst, src)
+}
+func (m *FirecrackerRateLimiter) XXX_Size() int {
+	return xxx_messageInfo_FirecrackerRateLimiter.Size(m)
+}
+func (m *FirecrackerRateLimiter) XXX_DiscardUnknown() {
+	xxx_messageInfo_FirecrackerRateLimiter.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_FirecrackerRateLimiter proto.InternalMessageInfo
+
+func (m *FirecrackerRateLimiter) GetBandwidth() *FirecrackerTokenBucket {
 	if m != nil {
-		return m.NetworkInterfaces
+		return m.Bandwidth
+	}
+	return nil
+}
+
+func (m *FirecrackerRateLimiter) GetOps() *FirecrackerTokenBucket {
+	if m != nil {
+		return m.Ops
 	}
 	return nil
 }
 
+// Message to specify a token buicket used to rate limit disk and network IO for a Firecracker VM
+type FirecrackerTokenBucket struct {
+	OneTimeBurst         int64    `protobuf:"varint,1,opt,name=OneTimeBurst,proto3" json:"OneTimeBurst,omitempty"`
+	RefillTime           int64    `protobuf:"varint,2,opt,name=RefillTime,proto3" json:"RefillTime,omitempty"`
+	Capacity             int64    `protobuf:"varint,3,opt,name=Capacity,proto3" json:"Capacity,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *FirecrackerTokenBucket) Reset()         { *m = FirecrackerTokenBucket{} }
+func (m *FirecrackerTokenBucket) String() string { return proto.CompactTextString(m) }
+func (*FirecrackerTokenBucket) ProtoMessage()    {}
+func (*FirecrackerTokenBucket) Descriptor() ([]byte, []int) {
+	return fileDescriptor_types_80133946767b2ac8, []int{6}
+}
+func (m *FirecrackerTokenBucket) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_FirecrackerTokenBucket.Unmarshal(m, b)
+}
+func (m *FirecrackerTokenBucket) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_FirecrackerTokenBucket.Marshal(b, m, deterministic)
+}
+func (dst *FirecrackerTokenBucket) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_FirecrackerTokenBucket.Merge(dst, src)
+}
+func (m *FirecrackerTokenBucket) XXX_Size() int {
+	return xxx_messageInfo_FirecrackerTokenBucket.Size(m)
+}
+func (m *FirecrackerTokenBucket) XXX_DiscardUnknown() {
+	xxx_messageInfo_FirecrackerTokenBucket.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_FirecrackerTokenBucket proto.InternalMessageInfo
+
+func (m *FirecrackerTokenBucket) GetOneTimeBurst() int64 {
+	if m != nil {
+		return m.OneTimeBurst
+	}
+	return 0
+}
+
+func (m *FirecrackerTokenBucket) GetRefillTime() int64 {
+	if m != nil {
+		return m.RefillTime
+	}
+	return 0
+}
+
+func (m *FirecrackerTokenBucket) GetCapacity() int64 {
+	if m != nil {
+		return m.Capacity
+	}
+	return 0
+}
+
 func init() {
 	proto.RegisterType((*ExtraData)(nil), "firecracker.containerd.ExtraData")
-	proto.RegisterType((*FirecrackerNetworkInterface)(nil), "firecracker.containerd.FirecrackerNetworkInterface")
 	proto.RegisterType((*FirecrackerConfig)(nil), "firecracker.containerd.FirecrackerConfig")
+	proto.RegisterType((*FirecrackerNetworkInterface)(nil), "firecracker.containerd.FirecrackerNetworkInterface")
+	proto.RegisterType((*FirecrackerMachineConfiguration)(nil), "firecracker.containerd.FirecrackerMachineConfiguration")
+	proto.RegisterType((*FirecrackerDrive)(nil), "firecracker.containerd.FirecrackerDrive")
+	proto.RegisterType((*FirecrackerRateLimiter)(nil), "firecracker.containerd.FirecrackerRateLimiter")
+	proto.RegisterType((*FirecrackerTokenBucket)(nil), "firecracker.containerd.FirecrackerTokenBucket")
 }
 
-func init() { proto.RegisterFile("proto/types.proto", fileDescriptor_types_cfec9a3218eb42c5) }
-
-var fileDescriptor_types_cfec9a3218eb42c5 = []byte{
-	// 288 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0xc1, 0x4b, 0xc3, 0x30,
-	0x18, 0xc5, 0xa9, 0x82, 0xd8, 0xd4, 0x4b, 0x83, 0xc8, 0x9c, 0x20, 0xa5, 0xa7, 0x5e, 0x4c, 0x61,
-	0x03, 0x2f, 0xe2, 0x61, 0x3a, 0x45, 0x05, 0x27, 0xc4, 0x9b, 0x97, 0x91, 0xa5, 0x5f, 0x6b, 0x98,
-	0xcd, 0x57, 0x92, 0x74, 0xda, 0x7f, 0xca, 0xbf, 0x51, 0xec, 0xd4, 0xd5, 0x29, 0xe2, 0x29, 0xc9,
-	0xe3, 0xcb, 0x7b, 0xbf, 0x97, 0x90, 0xb0, 0x32, 0xe8, 0x30, 0x75, 0x4d, 0x05, 0x96, 0xb5, 0x7b,
-	0xba, 0x97, 0x2b, 0x03, 0xd2, 0x08, 0x39, 0x07, 0xc3, 0x24, 0x6a, 0x27, 0x94, 0x06, 0x93, 0xf5,
-	0xf7, 0x0b, 0xc4, 0xe2, 0x09, 0xd2, 0x76, 0x6a, 0x56, 0xe7, 0xa9, 0xd0, 0xcd, 0xf2, 0x4a, 0x3c,
-	0x25, 0xfe, 0xc5, 0x8b, 0x33, 0x62, 0x2c, 0x9c, 0xa0, 0x7d, 0xb2, 0x7d, 0x63, 0x51, 0xdf, 0x57,
-	0x20, 0x7b, 0x5e, 0xe4, 0x25, 0x3b, 0xfc, 0xeb, 0x4c, 0x8f, 0x49, 0xc0, 0x6b, 0x2d, 0xef, 0x2a,
-	0xa7, 0x50, 0xdb, 0xde, 0x46, 0xe4, 0x25, 0xc1, 0x60, 0x97, 0x2d, 0x9d, 0xd9, 0xa7, 0x33, 0x1b,
-	0xe9, 0x86, 0x77, 0x07, 0xe3, 0x29, 0x39, 0xb8, 0x5c, 0x51, 0x4d, 0xc0, 0x3d, 0xa3, 0x99, 0x5f,
-	0x6b, 0x07, 0x26, 0x17, 0x12, 0xe8, 0x21, 0x21, 0xb7, 0x42, 0x8e, 0xb2, 0xcc, 0x80, 0xb5, 0x6d,
-	0xa8, 0xcf, 0x3b, 0x0a, 0x8d, 0x48, 0x70, 0x85, 0xd6, 0x8d, 0x61, 0x31, 0x11, 0x25, 0xb4, 0xb1,
-	0x3e, 0xef, 0x4a, 0xf1, 0xab, 0x47, 0xc2, 0x4e, 0xc2, 0x39, 0xea, 0x5c, 0x15, 0xeb, 0xb8, 0xde,
-	0x3f, 0x71, 0xa9, 0x20, 0xe1, 0x3a, 0xe3, 0x7b, 0xd9, 0xcd, 0x24, 0x18, 0x0c, 0xd9, 0xef, 0xcf,
-	0xcb, 0xfe, 0xe8, 0xc7, 0x7f, 0xba, 0x9d, 0x9d, 0x3e, 0x9c, 0x14, 0xca, 0x3d, 0xd6, 0x33, 0x26,
-	0xb1, 0x4c, 0x3b, 0x9e, 0x47, 0xa5, 0x92, 0x06, 0x17, 0xdf, 0xb5, 0x55, 0xce, 0xc7, 0xf7, 0x6d,
-	0xb5, 0xcb, 0xf0, 0x2d, 0x00, 0x00, 0xff, 0xff, 0x5a, 0xb5, 0x62, 0x79, 0x00, 0x02, 0x00, 0x00,
+func init() { proto.RegisterFile("proto/types.proto", fileDescriptor_types_80133946767b2ac8) }
+
+var fileDescriptor_types_80133946767b2ac8 = []byte{
+	// 683 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x95, 0x4f, 0x6f, 0x12, 0x41,
+	0x18, 0xc6, 0xb3, 0xc5, 0x56, 0x18, 0x5a, 0x6d, 0x27, 0xa6, 0xc1, 0x6a, 0x94, 0xec, 0x89, 0x8b,
+	0x4b, 0xd2, 0x26, 0x7a, 0x30, 0x26, 0xf2, 0xa7, 0x4d, 0xd1, 0x52, 0xc8, 0x14, 0x6b, 0xe2, 0xc5,
+	0x0c, 0xbb, 0x2f, 0x30, 0x61, 0x77, 0x66, 0x33, 0x3b, 0x4b, 0x8b, 0x5f, 0xc6, 0xab, 0x89, 0x5f,
+	0xcb, 0xbb, 0x5f, 0xc1, 0xcc, 0x00, 0xdd, 0x81, 0x36, 0x8a, 0x7a, 0x82, 0x79, 0x98, 0xf7, 0xb7,
+	0xef, 0xfb, 0x3c, 0x3b, 0x03, 0xda, 0x8b, 0xa5, 0x50, 0xa2, 0xaa, 0xa6, 0x31, 0x24, 0x9e, 0xf9,
+	0x8e, 0xf7, 0x07, 0x4c, 0x82, 0x2f, 0xa9, 0x3f, 0x06, 0xe9, 0xf9, 0x82, 0x2b, 0xca, 0x38, 0xc8,
+	0xe0, 0xe0, 0xf1, 0x50, 0x88, 0x61, 0x08, 0x55, 0xb3, 0xab, 0x9f, 0x0e, 0xaa, 0x94, 0x4f, 0x67,
+	0x25, 0xee, 0x67, 0x54, 0x38, 0xbe, 0x56, 0x92, 0x36, 0xa9, 0xa2, 0xf8, 0x00, 0xe5, 0xdf, 0x25,
+	0x82, 0x5f, 0xc4, 0xe0, 0x97, 0x9c, 0xb2, 0x53, 0xd9, 0x26, 0x37, 0x6b, 0xfc, 0x12, 0x15, 0x49,
+	0xca, 0xfd, 0x4e, 0xac, 0x98, 0xe0, 0x49, 0x69, 0xa3, 0xec, 0x54, 0x8a, 0x87, 0x8f, 0xbc, 0x19,
+	0xd9, 0x5b, 0x90, 0xbd, 0x1a, 0x9f, 0x12, 0x7b, 0xa3, 0xfb, 0x33, 0x87, 0xf6, 0x4e, 0xb2, 0xb6,
+	0x1a, 0x82, 0x0f, 0xd8, 0x70, 0x95, 0xe6, 0xac, 0x49, 0xc3, 0x14, 0xed, 0x9d, 0x83, 0xba, 0x12,
+	0x72, 0xdc, 0xe2, 0x0a, 0xe4, 0x80, 0xfa, 0xa0, 0x7b, 0xc9, 0x55, 0x8a, 0x87, 0x47, 0xde, 0xdd,
+	0xd3, 0x7b, 0xd6, 0xd3, 0x57, 0x6b, 0xc9, 0x6d, 0x1a, 0xae, 0xa0, 0x87, 0xef, 0x41, 0x72, 0x08,
+	0x5b, 0x11, 0x1d, 0x42, 0x97, 0xaa, 0x51, 0x29, 0x57, 0x76, 0x2a, 0x05, 0xb2, 0x2a, 0xe3, 0x67,
+	0x08, 0xcd, 0xa4, 0x9a, 0x1c, 0x26, 0xa5, 0x7b, 0x66, 0x93, 0xa5, 0xe0, 0x8f, 0x08, 0xb5, 0xa9,
+	0x3f, 0x62, 0x1c, 0x1a, 0x83, 0x61, 0x69, 0xd3, 0xcc, 0xf8, 0x6a, 0x8d, 0x2e, 0x17, 0x45, 0xc6,
+	0xaa, 0x54, 0x52, 0x3d, 0x3a, 0xb1, 0x50, 0xf8, 0x04, 0x15, 0x88, 0x10, 0xaa, 0x29, 0xd9, 0x04,
+	0x4a, 0x5b, 0x86, 0x5b, 0x59, 0x83, 0x6b, 0xf6, 0x93, 0xac, 0x14, 0xf7, 0xd0, 0x6e, 0x2d, 0x08,
+	0x98, 0xe6, 0xd3, 0xd0, 0x48, 0x49, 0xe9, 0xbe, 0x31, 0x73, 0x7d, 0xdc, 0x2d, 0x82, 0xfb, 0x7d,
+	0x03, 0x3d, 0xf9, 0x8d, 0xe7, 0xda, 0xb6, 0x36, 0xf5, 0x6b, 0x41, 0x20, 0x21, 0x99, 0x45, 0x5f,
+	0x20, 0x96, 0x82, 0xcb, 0xa8, 0x78, 0x2a, 0x12, 0xd5, 0x84, 0xc9, 0x39, 0x8d, 0xc0, 0xbc, 0x69,
+	0x05, 0x62, 0x4b, 0xf8, 0x29, 0x2a, 0xd4, 0xc2, 0x50, 0x5c, 0xb5, 0xdb, 0xcd, 0x0b, 0x13, 0x4e,
+	0x9e, 0x64, 0x02, 0xee, 0xa1, 0x9d, 0x16, 0x27, 0x54, 0xc1, 0x19, 0x8b, 0x98, 0x02, 0x69, 0x92,
+	0x29, 0x1e, 0x7a, 0x6b, 0x8c, 0x64, 0x55, 0x91, 0x65, 0x08, 0xbe, 0x44, 0x0f, 0x3a, 0xa9, 0xb2,
+	0xb1, 0x9b, 0xff, 0x84, 0x5d, 0xa1, 0xb8, 0x5f, 0x1d, 0xf4, 0xfc, 0x0f, 0xd9, 0x6b, 0x47, 0x1a,
+	0xdd, 0x0f, 0x3d, 0x88, 0xe2, 0x90, 0x2a, 0x98, 0x5b, 0x66, 0x4b, 0xda, 0x91, 0x53, 0x75, 0xcc,
+	0x69, 0x3f, 0x84, 0xc0, 0x38, 0x96, 0x27, 0x99, 0x60, 0x1c, 0x87, 0xe8, 0x82, 0x7d, 0x81, 0x36,
+	0xeb, 0x1b, 0xc3, 0x76, 0x88, 0xa5, 0xe8, 0xea, 0x4b, 0x3f, 0x4e, 0x1b, 0x22, 0xe5, 0xca, 0xb8,
+	0xb5, 0x43, 0x32, 0xc1, 0xfd, 0xe1, 0xa0, 0xdd, 0xd5, 0xd8, 0x35, 0xb2, 0x95, 0x10, 0xa0, 0x41,
+	0x87, 0x87, 0x53, 0xd3, 0x51, 0x9e, 0x58, 0x0a, 0x76, 0xd1, 0x76, 0x2b, 0x31, 0x6f, 0x1a, 0x4c,
+	0x98, 0x0f, 0xf3, 0x9e, 0x96, 0x34, 0x7d, 0xdd, 0x74, 0xa9, 0x54, 0x69, 0xca, 0x82, 0xf9, 0x11,
+	0xbb, 0x59, 0x6b, 0xbe, 0x3e, 0x63, 0x1d, 0xae, 0x73, 0x5f, 0x9c, 0xad, 0x4c, 0xc1, 0x5d, 0x54,
+	0xfc, 0xff, 0x2c, 0x6c, 0x84, 0xfb, 0xcd, 0x41, 0xfb, 0x77, 0xef, 0xc3, 0x67, 0xa8, 0x50, 0xa7,
+	0x3c, 0xb8, 0x62, 0x81, 0x1a, 0xcd, 0xef, 0xaa, 0x75, 0x1e, 0xd5, 0x13, 0x63, 0xe0, 0xf5, 0xd4,
+	0x1f, 0x83, 0x22, 0x19, 0x00, 0xbf, 0x45, 0xb9, 0x4e, 0xbc, 0xb8, 0x41, 0xff, 0x96, 0xa3, 0x4b,
+	0xdd, 0xeb, 0xa5, 0x4e, 0xad, 0x9f, 0xb5, 0xed, 0x1d, 0x0e, 0x3d, 0x16, 0x41, 0x3d, 0x95, 0x89,
+	0x32, 0xcd, 0xe6, 0xc8, 0x92, 0xa6, 0xad, 0x25, 0x30, 0x60, 0x61, 0xa8, 0x25, 0xd3, 0x46, 0x8e,
+	0x58, 0x8a, 0x8e, 0xa5, 0x41, 0x63, 0xea, 0x33, 0x35, 0x35, 0xb1, 0xe4, 0xc8, 0xcd, 0xba, 0xfe,
+	0xe6, 0xd3, 0xeb, 0x21, 0x53, 0xa3, 0xb4, 0xef, 0xf9, 0x22, 0xaa, 0x5a, 0xad, 0xbf, 0x88, 0x98,
+	0x2f, 0xc5, 0x64, 0x59, 0xcb, 0xc6, 0x99, 0xff, 0xf5, 0x6c, 0x99, 0x8f, 0xa3, 0x5f, 0x01, 0x00,
+	0x00, 0xff, 0xff, 0xee, 0xb9, 0xd8, 0x97, 0xbc, 0x06, 0x00, 0x00,
 }
diff --git a/proto/types.proto b/proto/types.proto
index b78bb36..16e191d 100644
--- a/proto/types.proto
+++ b/proto/types.proto
@@ -12,14 +12,59 @@ message ExtraData {
 	google.protobuf.Any RuncOptions = 2;
 }
 
+// Message to specify the various Firecracker VM configurations for a task
+message FirecrackerConfig {
+	google.protobuf.Any RuncOptions = 1; // Specifies Runc options that need to relayed to the CreateTask request
+	repeated FirecrackerNetworkInterface NetworkInterfaces = 2; // Specifies the networking configuration for a VM
+	string KernelImagePath = 3; // Specifies the file path where the kernel image is located
+	string KernelArgs = 4; // Specifies the commandline arguments that should be passed to the kernel
+	FirecrackerMachineConfiguration MachineCfg = 5; // Specifies the machine configuration for the VM
+	FirecrackerDrive RootDrive = 6; // Specifies the root block device for the VM
+	// Specifies the additional block device config for the VM. Note that
+	// the snapshotter used might itself present block devices to the be
+	// attached to the VM. This fields allows additional block devices to
+	// that list.
+	repeated FirecrackerDrive AdditionalDrives = 7;
+}
+
 // Message to specify network config for a Firecracker VM
 message FirecrackerNetworkInterface {
-	string MacAddress = 1;
-	string HostDevName = 2;
+	string MacAddress = 1; // Specifies the mac address for the the device
+	string HostDevName = 2; // Specifies the name of the tap device on the host
+	bool AllowMMDS = 3; // Specifies if metadata service should be available on this network interface
+	FirecrackerRateLimiter InRateLimiter = 4; // Specifies a rate limiter for incoming bytes
+	FirecrackerRateLimiter OutRateLimiter = 5; // Specifies a rate limiter for outgoing bytes
 }
 
-// Message to specify the various Firecracker VM configurations for a task
-message FirecrackerConfig {
-	google.protobuf.Any RuncOptions = 1; // Can be used by the CreateTask request needs to relay any Runc options to the agent
-	repeated FirecrackerNetworkInterface NetworkInterfaces = 2; // Can be used to set the networking configuration for a VM
+// Message to set the machine config for a Firecracker VM
+message FirecrackerMachineConfiguration {
+	string CPUTemplate = 1; // Specifies the cpu template. Example: "T2" or "C3"
+	bool HtEnabled = 2; // Specifies if hyper-threading should be enabled
+	 // Specifies the memory size of VM
+	 // This lets us create a Firecracker VM of up to 4096 TiB, which
+	 // for a mircroVM should be large enough
+	uint32 MemSizeMib = 3;
+	uint32 VcpuCount = 4; // Specifies the number of vCPUs for the VM
+}
+
+// Message to specify the block device config for a Firecracker VM
+message FirecrackerDrive {
+	bool IsReadOnly = 1; // Specifies if the drive is read only
+	bool IsRootDevice = 2; // Specifies if the drive is the root device
+	string Partuuid = 3; // Specifies the unique id of the boot partition on this device
+	string PathOnHost = 4; // Specifies the host level path for the guest drive
+	FirecrackerRateLimiter RateLimiter = 5; // Specifies a rate limiter for block IO
+}
+
+// Message to specify an IO rate limiter with bytes/s and ops/s limits
+message FirecrackerRateLimiter {
+	FirecrackerTokenBucket Bandwidth = 1; // Specifies a token bucket with bytes as tokens
+	FirecrackerTokenBucket Ops = 2; // Specifies a token bucket with operations as tokens
+}
+
+// Message to specify a token buicket used to rate limit disk and network IO for a Firecracker VM
+message FirecrackerTokenBucket {
+	int64 OneTimeBurst = 1; // Specifies the initial size of the token bucket
+	int64 RefillTime = 2; // Specifies the amount of millis it takes for the bucket to fill
+	int64 Capacity = 3; // Specifies the number of tokens this bucket can hold
 }
diff --git a/runtime/service.go b/runtime/service.go
index 0b1f6f9..c423f8e 100644
--- a/runtime/service.go
+++ b/runtime/service.go
@@ -602,6 +602,24 @@ func findNextAvailableVsockCID(ctx context.Context) (uint32, error) {
 	return 0, errors.New("couldn't find any available vsock context id")
 }
 
+func parseCreateTaskOpts(ctx context.Context, opts *ptypes.Any) (*proto.FirecrackerConfig, *ptypes.Any, error) {
+	cfg, err := typeurl.UnmarshalAny(opts)
+	if err != nil {
+		return nil, nil, errors.Wrapf(err, "unmarshaling task create request options")
+	}
+	// We've verified that this is a valid prototype at this point of time.
+	// Check if it's the FirecrackerConfig type
+	firecrackerConfig, ok := cfg.(*proto.FirecrackerConfig)
+	if ok {
+		// We've verified that the proto message was of FirecrackerConfig type.
+		// Get runc options based on what was set in FirecrackerConfig.
+		return firecrackerConfig, firecrackerConfig.RuncOptions, nil
+	}
+	// This is a valid proto message, but is not FirecrackerConfig type.
+	// Treat the message as runc opts
+	return nil, opts, nil
+}
+
 func (s *service) startVM(ctx context.Context,
 	request *taskAPI.CreateTaskRequest,
 	vmConfig *proto.FirecrackerConfig,
@@ -638,13 +656,16 @@ func (s *service) startVM(ctx context.Context,
 
 		driveBuilder = driveBuilder.AddDrive(mnt.Source, false)
 	}
-	cfg.Drives = driveBuilder.Build()
 	// Override config provided in task's create opts if any.
 	// Note: We've chosen to override here instead of merging in order to
 	// provide a cleaner, simpler interface to reason about for clients.
 	// Any config provided by clients for create task opts will always override
 	// the default cofig generated by the runtime.
-	cfg = overrideVMConfigFromTaskOpts(cfg, vmConfig)
+	cfg, driveBuilder, err = overrideVMConfigFromTaskOpts(cfg, vmConfig, driveBuilder)
+	if err != nil {
+		return nil, err
+	}
+	cfg.Drives = driveBuilder.Build()
 	cmd := firecracker.VMCommandBuilder{}.
 		WithBin(s.config.FirecrackerBinaryPath).
 		WithSocketPath(s.config.SocketPath).
@@ -681,47 +702,10 @@ func (s *service) startVM(ctx context.Context,
 	return apiClient, nil
 }
 
-func overrideVMConfigFromTaskOpts(cfg firecracker.Config, vmConfig *proto.FirecrackerConfig) firecracker.Config {
-	if vmConfig == nil {
-		return cfg
-	}
-	// 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] = firecracker.NetworkInterface{
-				MacAddress:  nw.MacAddress,
-				HostDevName: nw.HostDevName,
-			}
-		}
-		cfg.NetworkInterfaces = nwIfaces
-	}
-
-	return cfg
-}
-
 func (s *service) stopVM() error {
 	return s.machine.StopVMM()
 }
 
-func parseCreateTaskOpts(ctx context.Context, opts *ptypes.Any) (*proto.FirecrackerConfig, *ptypes.Any, error) {
-	cfg, err := typeurl.UnmarshalAny(opts)
-	if err != nil {
-		return nil, nil, errors.Wrapf(err, "unmarshaling task create request options")
-	}
-	// We've verified that this is a valid prototype at this point of time.
-	// Check if it's the FirecrackerConfig type
-	firecrackerConfig, ok := cfg.(*proto.FirecrackerConfig)
-	if ok {
-		// We've verified that the proto message was of FirecrackerConfig type.
-		// Get runc options based on what was set in FirecrackerConfig.
-		return firecrackerConfig, firecrackerConfig.RuncOptions, nil
-	}
-	// This is a valid proto message, but is not FirecrackerConfig type.
-	// Treat the message as runc opts
-	return nil, opts, nil
-}
-
 func packBundle(path string, options *ptypes.Any) (*ptypes.Any, error) {
 	// Add the bundle/config.json to the request so it can be recreated
 	// inside the vm:
diff --git a/runtime/taskfirecrackeropts.go b/runtime/taskfirecrackeropts.go
new file mode 100644
index 0000000..8d70e4d
--- /dev/null
+++ b/runtime/taskfirecrackeropts.go
@@ -0,0 +1,161 @@
+// 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.
+
+package main
+
+import (
+	"time"
+
+	"github.com/firecracker-microvm/firecracker-containerd/proto"
+	firecracker "github.com/firecracker-microvm/firecracker-go-sdk"
+	models "github.com/firecracker-microvm/firecracker-go-sdk/client/models"
+	"github.com/pkg/errors"
+)
+
+// 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
+	}
+	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
+		}
+	}
+	// Overwrite the root drive config if specified.
+	if vmConfig.RootDrive != nil {
+		drivesBuilder = drivesBuilderFromProto(vmConfig.RootDrive, drivesBuilder, true)
+	}
+	// Add additional drives if specified.
+	if len(vmConfig.AdditionalDrives) > 0 {
+		for _, drive := range vmConfig.AdditionalDrives {
+			drivesBuilder = drivesBuilderFromProto(drive, drivesBuilder, false)
+		}
+	}
+
+	return cfg, drivesBuilder, nil
+}
+
+// networkConfigFromProto creates a firecracker NetworkInterface object from
+// the protobuf FirecrackerNetworkInterface message.
+func networkConfigFromProto(nwIface *proto.FirecrackerNetworkInterface) firecracker.NetworkInterface {
+	result := firecracker.NetworkInterface{
+		MacAddress:  nwIface.MacAddress,
+		HostDevName: nwIface.HostDevName,
+		AllowMMDS:   nwIface.AllowMMDS,
+	}
+	if nwIface.InRateLimiter != nil {
+		result.InRateLimiter = rateLimiterFromProto(nwIface.InRateLimiter)
+	}
+	if nwIface.OutRateLimiter != nil {
+		result.OutRateLimiter = rateLimiterFromProto(nwIface.OutRateLimiter)
+	}
+
+	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 {
+	opt := func(d *models.Drive) {
+		d.Partuuid = drive.Partuuid
+		if drive.RateLimiter != nil {
+			d.RateLimiter = rateLimiterFromProto(drive.RateLimiter)
+		}
+	}
+	if isRoot {
+		return drivesBuilder.WithRootDrive(drive.PathOnHost, opt)
+	}
+	return drivesBuilder.AddDrive(drive.PathOnHost, drive.IsReadOnly, opt)
+}
+
+// rateLimiterFromProto creates a firecracker RateLimiter object from the
+// protobuf message.
+func rateLimiterFromProto(rl *proto.FirecrackerRateLimiter) *models.RateLimiter {
+	result := models.RateLimiter{}
+	if rl.Bandwidth != nil {
+		result.Bandwidth = tokenBucketFromProto(rl.Bandwidth)
+	}
+	if rl.Ops != nil {
+		result.Ops = tokenBucketFromProto(rl.Ops)
+	}
+
+	return &result
+}
+
+// tokenBucketFromProto creates a firecracker TokenBucket object from the
+// protobuf message.
+func tokenBucketFromProto(bucket *proto.FirecrackerTokenBucket) *models.TokenBucket {
+	builder := firecracker.TokenBucketBuilder{}
+	if bucket.OneTimeBurst > 0 {
+		builder = builder.WithInitialSize(bucket.OneTimeBurst)
+	}
+	if bucket.RefillTime > 0 {
+		builder = builder.WithRefillDuration(time.Duration(bucket.RefillTime) * time.Millisecond)
+	}
+	if bucket.Capacity > 0 {
+		builder = builder.WithBucketSize(bucket.Capacity)
+	}
+
+	res := builder.Build()
+	return &res
+}


Test patch:
diff --git a/runtime/service_test.go b/runtime/service_test.go
index 8d7fdac..65669f6 100644
--- a/runtime/service_test.go
+++ b/runtime/service_test.go
@@ -20,7 +20,6 @@ import (
 
 	"github.com/containerd/typeurl"
 	"github.com/firecracker-microvm/firecracker-containerd/proto"
-	firecracker "github.com/firecracker-microvm/firecracker-go-sdk"
 	"github.com/stretchr/testify/require"
 	"gotest.tools/assert"
 )
@@ -92,42 +91,3 @@ func TestParseCreateTaskLeavesNonFirecrackerConfigAlong(t *testing.T) {
 	}
 	assert.Equal(t, protoIn, outOpts)
 }
-
-func TestOverrideVMConfigFromTaskOpts(t *testing.T) {
-	var testCases = []struct {
-		name        string
-		in          firecracker.Config
-		vmConfig    *proto.FirecrackerConfig
-		expectedOut firecracker.Config
-	}{
-		{
-			name: "network config",
-			in:   firecracker.Config{},
-			vmConfig: &proto.FirecrackerConfig{
-				NetworkInterfaces: []*proto.FirecrackerNetworkInterface{
-					{
-						MacAddress:  mac,
-						HostDevName: hostDevName,
-					},
-				},
-			},
-			expectedOut: firecracker.Config{
-				NetworkInterfaces: []firecracker.NetworkInterface{
-					{
-						MacAddress:  mac,
-						HostDevName: hostDevName,
-					},
-				},
-			},
-		},
-	}
-	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 := overrideVMConfigFromTaskOpts(test.in, test.vmConfig)
-			assert.DeepEqual(t, test.expectedOut, out)
-		})
-	}
-}
diff --git a/runtime/taskfirecrackeropts_test.go b/runtime/taskfirecrackeropts_test.go
new file mode 100644
index 0000000..ec00232
--- /dev/null
+++ b/runtime/taskfirecrackeropts_test.go
@@ -0,0 +1,221 @@
+// 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.
+
+package main
+
+import (
+	"testing"
+
+	"github.com/firecracker-microvm/firecracker-containerd/proto"
+	firecracker "github.com/firecracker-microvm/firecracker-go-sdk"
+	models "github.com/firecracker-microvm/firecracker-go-sdk/client/models"
+	"github.com/stretchr/testify/assert"
+)
+
+const (
+	oneTimeBurst  = 800
+	refillTime    = 1000
+	capacity      = 400
+	rootDrivePath = "/path/to/root/drive"
+	loopDrivePath = "/path/to/loop/drive"
+	loopDriveRO   = false
+	memSize       = 4096
+	vcpuCount     = 2
+)
+
+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 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(),
+		},
+	}
+	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())
+			}
+		})
+	}
+}