diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml
index 562fbacd7..2688669e8 100644
--- a/.buildkite/pipeline.yml
+++ b/.buildkite/pipeline.yml
@@ -55,17 +55,6 @@ steps:
- make test-in-docker
timeout_in_minutes: 10
- - label: ":rotating_light: :hammer: snapshotter *root* tests"
- agents:
- queue: "${BUILDKITE_AGENT_META_DATA_QUEUE:-default}"
- env:
- DOCKER_IMAGE_TAG: "$BUILDKITE_BUILD_NUMBER"
- EXTRAGOARGS: "-v -count=1 -race"
- command: 'make -C snapshotter integ-test'
- timeout_in_minutes: 10
- concurrency: 1
- concurrency_group: 'loop-device test'
-
- label: ":rotating_light: :running_shirt_with_sash: runtime isolated tests"
agents:
queue: "${BUILDKITE_AGENT_META_DATA_QUEUE:-default}"
@@ -78,19 +67,6 @@ steps:
- make -C runtime integ-test FICD_SNAPSHOTTER=devmapper FICD_DM_POOL=build_${BUILDKITE_BUILD_NUMBER}_runtime
timeout_in_minutes: 10
- - label: ":rotating_light: :exclamation: example tests (naive)"
- agents:
- queue: "${BUILDKITE_AGENT_META_DATA_QUEUE:-default}"
- env:
- DOCKER_IMAGE_TAG: "$BUILDKITE_BUILD_NUMBER"
- EXTRAGOARGS: "-v -count=1"
- artifact_paths:
- - "examples/logs/*"
- command: 'make -C examples integ-test'
- timeout_in_minutes: 10
- concurrency: 1
- concurrency_group: 'loop-device test'
-
- label: ":rotating_light: :exclamation: example tests (devmapper)"
agents:
queue: "${BUILDKITE_AGENT_META_DATA_QUEUE:-default}"
diff --git a/Makefile b/Makefile
index e50a7829d..fcf3000d5 100644
--- a/Makefile
+++ b/Makefile
@@ -11,7 +11,7 @@
# express or implied. See the License for the specific language governing
# permissions and limitations under the License.
-SUBDIRS:=agent runtime snapshotter internal examples firecracker-control/cmd/containerd eventbridge
+SUBDIRS:=agent runtime internal examples firecracker-control/cmd/containerd eventbridge
TEST_SUBDIRS:=$(addprefix test-,$(SUBDIRS))
INTEG_TEST_SUBDIRS:=$(addprefix integ-test-,$(SUBDIRS))
diff --git a/README.md b/README.md
index e6d750051..ac1384bac 100644
--- a/README.md
+++ b/README.md
@@ -31,12 +31,6 @@ container standards such as the OCI image format.
There are several components in this repository that enable containerd to use
Firecracker microVMs to run containers:
-* A [snapshotter](snapshotter) that creates files used as block-devices for
- pass-through into the microVM. This snapshotter is used for providing the
- container image to the microVM. The snapshotter runs as an out-of-process
- gRPC proxy plugin. We currently have two implementations of a snapshotter: a
- [naive](snapshotter/cmd/naive) copy-ahead implementation and a
- [devmapper-based](snapshotter/cmd/devmapper) copy-on-write implementation.
* A [control plugin](firecracker-control) managing the lifecycle of the
runtime and implementing our [control API](proto/firecracker.proto) to
manage the lifecycle of microVMs. The control plugin is compiled in to the
diff --git a/docs/getting-started.md b/docs/getting-started.md
index 214b5c92b..0f5d12faf 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -78,8 +78,6 @@ GO111MODULE=on make all
Once you have built the runtime, be sure to place the following binaries on your
`$PATH`:
* `runtime/containerd-shim-aws-firecracker`
-* `snapshotter/cmd/devmapper/devmapper_snapshotter`
-* `snapshotter/cmd/naive/naive_snapshotter`
* `firecracker-control/cmd/containerd/firecracker-containerd`
* `firecracker-control/cmd/containerd/firecracker-ctr`
@@ -135,10 +133,11 @@ root = "/var/lib/firecracker-containerd/containerd"
state = "/run/firecracker-containerd"
[grpc]
address = "/run/firecracker-containerd/containerd.sock"
-[proxy_plugins]
- [proxy_plugins.firecracker-devmapper]
- type = "snapshot"
- address = "/var/run/firecracker-containerd/devmapper-snapshotter.sock"
+[plugins]
+ [plugins.devmapper]
+ pool_name = "fc-dev-thinpool"
+ base_image_size = "10GB"
+ root_path = "/var/lib/firecracker-containerd/snapshotter/devmapper"
[debug]
level = "debug"
@@ -153,7 +152,7 @@ binaries are in sync with one another. While other builds of `ctr` may work with
### Prepare and configure snapshotter
The devmapper snapshotter requires a thinpool to exist.
-Below is a script to create a thinpool as well as an example config file.
+Below is a script to create a thinpool device.
`Note: The configuration with loopback devices is slow and not intended for use in production.`
@@ -205,21 +204,6 @@ fi
```
-
-Snappshotter config file example.
-
-```json
-{
- "base_image_size": "10GB",
- "root_path": "/var/lib/firecracker-containerd/snapshotter/devmapper",
- "pool_name": "fc-dev-thinpool"
-}
-```
-
-
-
-A reasonable location for this file is at `/etc/firecracker-dm-snapshotter/config.json`.
-
### Configure containerd runtime plugin
The runtime expects a JSON-formatted configuration file to be located either in
@@ -277,17 +261,7 @@ configuration file has the following fields:
## Usage
-Start the containerd snapshotter
-
-```bash
-$ ./devmapper_snapshotter \
- -address /var/run/firecracker-containerd/devmapper-snapshotter.sock \
- -path /tmp/fc-snapshot \
- -config /etc/firecracker-dm-snapshotter/config.json
-```
-`note: The path for -config needs to match the location used when configuring the devmapper snapshotter.`
-
-In another terminal, start containerd
+Start containerd
```bash
$ sudo PATH=$PATH /usr/local/bin/firecracker-containerd \
@@ -298,7 +272,7 @@ Pull an image
```bash
$ sudo firecracker-ctr --address /run/firecracker-containerd/containerd.sock images \
- pull --snapshotter firecracker-devmapper\
+ pull --snapshotter devmapper \
docker.io/library/busybox:latest
```
@@ -306,7 +280,9 @@ And start a container!
```bash
$ sudo firecracker-ctr --address /run/firecracker-containerd/containerd.sock \
- run --snapshotter firecracker-devmapper --runtime aws.firecracker \
+ run \
+ --snapshotter devmapper \
+ --runtime aws.firecracker \
--rm --tty --net-host \
docker.io/library/busybox:latest busybox-test
```
@@ -321,7 +297,7 @@ $ sudo firecracker-ctr --address /run/firecracker-containerd/containerd.sock \
$ sudo firecracker-ctr --address /run/firecracker-containerd/containerd.sock \
namespaces label fc \
containerd.io/defaults/runtime=aws.firecracker \
- containerd.io/defaults/snapshotter=firecracker-devmapper
+ containerd.io/defaults/snapshotter=devmapper
$ sudo firecracker-ctr --address /run/firecracker-containerd/containerd.sock \
-n fc \
diff --git a/go.sum b/go.sum
index b2bb1f115..e353a3516 100644
--- a/go.sum
+++ b/go.sum
@@ -18,6 +18,7 @@ github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:l
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
+github.com/checkpoint-restore/go-criu v0.0.0-20190109184317-bdb7599cd87b/go.mod h1:TrMrLQfeENAPYPRsJuq3jsqdlRh3lvi6trTZJG8+tho=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/containerd/cgroups v0.0.0-20181105182409-82cb49fc1779 h1:j1IsLW6/hNZPIyBH1v0hhEeB1WXE2ffhHaqSuXhgknY=
github.com/containerd/cgroups v0.0.0-20181105182409-82cb49fc1779/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI=
@@ -44,6 +45,7 @@ github.com/coreos/go-iptables v0.4.2/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmeka
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20181031085051-9002847aa142 h1:3jFq2xL4ZajGK4aZY8jz+DAF0FHjI51BXjjSwCzS1Dk=
github.com/coreos/go-systemd v0.0.0-20181031085051-9002847aa142/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
@@ -147,6 +149,7 @@ github.com/miekg/dns v1.1.16 h1:iMEQ/IVHxPTtx2Q07JP/k4CKRvSjiAZjZ0hnhgYEDmE=
github.com/miekg/dns v1.1.16/go.mod h1:YNV562EiewvSmpCB6/W4c6yqjK7Z+M/aIS1JHsIVeg8=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0=
github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b h1:Ey6yH0acn50T/v6CB75bGP4EMJqnv9WvnjN7oZaj+xE=
github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a h1:KfNOeFvoAssuZLT7IntKZElKwi/5LRuxY71k+t6rfaM=
@@ -159,6 +162,7 @@ github.com/opencontainers/runc v1.0.0-rc9 h1:/k06BMULKF5hidyoZymkoDCzdJzltZpz/UU
github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runtime-spec v0.1.2-0.20181106065543-31e0d16c1cb7 h1:vg5OQBKq2D0TX7q7loKQBLZ54IUAbUvq1rlwDRdn1gY=
github.com/opencontainers/runtime-spec v0.1.2-0.20181106065543-31e0d16c1cb7/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/selinux v1.3.0/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs=
github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
diff --git a/snapshotter/.gitignore b/snapshotter/.gitignore
deleted file mode 100644
index f06028280..000000000
--- a/snapshotter/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-naive_snapshotter
-devmapper_snapshotter
diff --git a/snapshotter/Makefile b/snapshotter/Makefile
deleted file mode 100644
index abbf0ba92..000000000
--- a/snapshotter/Makefile
+++ /dev/null
@@ -1,53 +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.
-
-# Set this to pass additional commandline flags to the go compiler, e.g. "make test EXTRAGOARGS=-v"
-EXTRAGOARGS?=
-
-
-SUBDIRS:=cmd/devmapper cmd/naive
-SOURCES:=$(shell find . -name '*.go')
-GOMOD := $(shell go env GOMOD)
-GOSUM := $(GOMOD:.mod=.sum)
-DOCKER_IMAGE_TAG?=latest
-FIRECRACKER_CONTAINERD_TEST_IMAGE?=localhost/firecracker-containerd-test
-
-all: $(SUBDIRS)
-
-$(SUBDIRS):
- $(MAKE) -C $@
-
-install: $(SUBDIRS)
- for d in $(SUBDIRS); do $(MAKE) -C $$d install; done
-
-test:
- DISABLE_ROOT_TESTS=true go test ./... $(EXTRAGOARGS)
-
-integ-test:
- docker run --rm -it \
- --privileged \
- --ipc=host \
- --volume /dev:/dev \
- --volume /sys:/sys \
- --volume /run/udev/control:/run/udev/control \
- --volume $(CURDIR)/..:/src \
- --workdir="/src/snapshotter" \
- $(FIRECRACKER_CONTAINERD_TEST_IMAGE):${DOCKER_IMAGE_TAG} \
- "go test ./... $(EXTRAGOARGS)"
-
-clean:
- for d in $(SUBDIRS); do $(MAKE) -C $$d clean; done
-
-distclean: clean
-
-.PHONY: all $(SUBDIRS) clean distclean install test integ-test
diff --git a/snapshotter/README.md b/snapshotter/README.md
deleted file mode 100644
index 3d3d6fb63..000000000
--- a/snapshotter/README.md
+++ /dev/null
@@ -1,89 +0,0 @@
-## Requirements
-
-### Running
-Due to its dependency on `dmsetup`, executing the snapshotter process in an environment where a udev
-daemon is not accessible (such as a container) may result in unexpected behavior. In this case, try executing the
-snapshotter with the `DM_DISABLE_UDEV=1` environment variable, which tells `dmsetup` to ignore udev and manage devices
-itself. See [lvm(8)](http://man7.org/linux/man-pages/man8/lvm.8.html) and
-[dmsetup(8)](http://man7.org/linux/man-pages/man8/dmsetup.8.html) for more information.
-
-## How to run snapshotters benchmark
-
-- `firecracker-containerd` project contains CloudFormation template to run an EC2 instance suitable for benchmarking.
-It installs dependencies, prepares EBS volumes with same performance characteristics, and creates thin-pool device.
-You can make stack with the following command (note: there is a charge for using AWS resources):
-
-```bash
-aws cloudformation create-stack \
- --stack-name benchmark-instance \
- --template-body file://benchmark_aws.yml \
- --parameters \
- ParameterKey=Key,ParameterValue=SSH_KEY \
- ParameterKey=SecurityGroups,ParameterValue=sg-XXXXXXXX \
- ParameterKey=VolumesSize,ParameterValue=20 \
- ParameterKey=VolumesIOPS,ParameterValue=1000
-```
-
-- You can find an IP address of newly created EC2 instance in AWS Console or via AWS CLI:
-
-```bash
-$ aws ec2 describe-instances \
- --instance-ids $(aws cloudformation describe-stack-resources --stack-name benchmark-instance --query 'StackResources[*].PhysicalResourceId' --output text) \
- --query 'Reservations[*].Instances[*].PublicIpAddress' \
- --output text
-```
-
-- SSH to an instance and prepare `firecracker-containerd` project:
-
-```bash
-ssh -i SSH_KEY ec2-user@IP
-mkdir /mnt/disk1/data /mnt/disk2/data /mnt/disk3/data
-cd
-git clone https://github.com/firecracker-microvm/firecracker-containerd.git
-cd firecracker-containerd
-make
-```
-
-- Now you're ready to run the benchmark:
-
-```bash
-sudo su -
-cd snapshotter/
-go test -bench . \
- -dm.thinPoolDev=bench-docker--pool \
- -dm.rootPath=/mnt/disk1/data \
- -overlay.rootPath=/mnt/disk2/data \
- -naive.rootPath=/mnt/disk3/data
-```
-
-- The output will look like:
-
-```bash
-goos: linux
-goarch: amd64
-pkg: github.com/firecracker-microvm/firecracker-containerd/snapshotter
-
-BenchmarkNaive/run-4 1 177960054662 ns/op 0.94 MB/s
-BenchmarkNaive/prepare 1 11284453889 ns/op
-BenchmarkNaive/write 1 166035272412 ns/op
-BenchmarkNaive/commit 1 640126721 ns/op
-
-BenchmarkOverlay/run-4 1 1019730210 ns/op 164.53 MB/s
-BenchmarkOverlay/prepare 1 26799447 ns/op
-BenchmarkOverlay/write 1 968200363 ns/op
-BenchmarkOverlay/commit 1 24582560 ns/op
-
-BenchmarkDeviceMapper/run-4 1 3139232730 ns/op 53.44 MB/s
-BenchmarkDeviceMapper/prepare 1 1758640440 ns/op
-BenchmarkDeviceMapper/write 1 1356705388 ns/op
-BenchmarkDeviceMapper/commit 1 23720367 ns/op
-
-PASS
-ok github.com/firecracker-microvm/firecracker-containerd/snapshotter 185.204s
-```
-
-- Don't forget to tear down the stack so it does not continue to incur charges:
-
-```bash
-aws cloudformation delete-stack --stack-name benchmark-instance
-```
diff --git a/snapshotter/benchmark_aws.yml b/snapshotter/benchmark_aws.yml
deleted file mode 100644
index 1f02138e6..000000000
--- a/snapshotter/benchmark_aws.yml
+++ /dev/null
@@ -1,139 +0,0 @@
-AWSTemplateFormatVersion: "2010-09-09"
-
-Description: >
- This templates spin ups an EC2 instance with EBS volumes suitable for containerd snapshotters benchmarking.
- The template will create EBS volumes for benchmarking (/dev/sdb, /dev/sdc, and /dev/sdd) with same performance characteristics.
- /dev/sde volume will be created and used for device mapper thin-pool device.
-
-Parameters:
- Key:
- Type: AWS::EC2::KeyPair::KeyName
- Description: SSH key to use
-
- AMI:
- Type: AWS::EC2::Image::Id
- Description: AMI ID to use for the EC2 instance. Must be Amazon Linux 2.
- Default: "ami-032509850cf9ee54e"
-
- SecurityGroups:
- Type: List
- Description: List of security groups to add to EC2 instance
-
- InstanceType:
- Type: String
- Default: m4.xlarge
- Description: EC2 instance type to use
-
- VolumesIOPS:
- Type: Number
- Default: 1000
- MinValue: 100
- MaxValue: 20000
- Description: The number of I/O operations per second (IOPS) to reserve for EBS volumes.
-
- VolumesSize:
- Type: Number
- Default: 20
- MinValue: 4
- MaxValue: 16384
- Description: EBS volumes size, in gibibytes (GiB)
-
- VolumeType:
- Type: String
- Default: io1
- AllowedValues:
- - io1
- - gp2
- - sc1
- - st1
- Description: >
- Volume type to use for EBS volumes (io1 is recommended).
- More information on volume types https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSVolumeTypes.html
-
- ContainerStorageSetup:
- Type: String
- Default: https://github.com/projectatomic/container-storage-setup/archive/v0.6.0.tar.gz
- Description: container-storage-setup tool version to install (more details at https://github.com/projectatomic/container-storage-setup)
-
-Resources:
- Instance:
- Type: AWS::EC2::Instance
- Properties:
- EbsOptimized: true
- InstanceType: !Ref InstanceType
- KeyName: !Ref Key
- ImageId: !Ref AMI
- SecurityGroupIds: !Ref SecurityGroups
- BlockDeviceMappings:
- - DeviceName: "/dev/xvda" # Root volume
- Ebs:
- VolumeSize: 64
- VolumeType: io1
- Iops: 1000
- DeleteOnTermination: true
- - DeviceName: "/dev/sdb"
- Ebs:
- VolumeSize: !Ref VolumesSize
- VolumeType: !Ref VolumeType
- Iops: !Ref VolumesIOPS
- DeleteOnTermination: true
- - DeviceName: "/dev/sdc"
- Ebs:
- VolumeSize: !Ref VolumesSize
- VolumeType: !Ref VolumeType
- Iops: !Ref VolumesIOPS
- DeleteOnTermination: true
- - DeviceName: "/dev/sdd"
- Ebs:
- VolumeSize: !Ref VolumesSize
- VolumeType: !Ref VolumeType
- Iops: !Ref VolumesIOPS
- DeleteOnTermination: true
- - DeviceName: "/dev/sde"
- Ebs:
- VolumeSize: !Ref VolumesSize
- VolumeType: !Ref VolumeType
- Iops: !Ref VolumesIOPS
- DeleteOnTermination: true
-
- UserData:
- Fn::Base64:
- !Sub |
- #!/bin/bash
-
- set -ex
-
- yum install -y gcc git
- amazon-linux-extras install -y golang1.11
-
- # Install container-storage-setup
- mkdir -p /tmp/container-storage-setup/unpacked/
- cd /tmp/container-storage-setup/
- curl -sL ${ContainerStorageSetup} -o archive.tar.gz
- tar -xzf archive.tar.gz -C unpacked --strip 1
- cd unpacked/
- make install-core
- rm -rf /tmp/container-storage-setup/
-
- # Prepare EBS volumes
- mkdir -p /mnt/{disk1,disk2,disk3}
-
- mkfs.ext4 /dev/sdb
- mount /dev/sdb /mnt/disk1/
-
- mkfs.ext4 /dev/sdc
- mount /dev/sdc /mnt/disk2/
-
- mkfs.ext4 /dev/sdd
- mount /dev/sdd /mnt/disk3
-
- chgrp -R wheel /mnt/disk1/ /mnt/disk2/ /mnt/disk3/
- chmod -R 2775 /mnt/disk1/ /mnt/disk2/ /mnt/disk3/
-
- # Prepare thin-pool device
- touch /etc/sysconfig/docker-storage-setup
- echo DEVS=/dev/sde >> /etc/sysconfig/docker-storage-setup
- echo VG=bench >> /etc/sysconfig/docker-storage-setup
- container-storage-setup
-
- echo "Done"
diff --git a/snapshotter/benchmark_test.go b/snapshotter/benchmark_test.go
deleted file mode 100644
index cad8d37dc..000000000
--- a/snapshotter/benchmark_test.go
+++ /dev/null
@@ -1,307 +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.
-
-package snapshotter
-
-import (
- "context"
- "crypto/rand"
- "flag"
- "fmt"
- "os"
- "path/filepath"
- "sync/atomic"
- "testing"
- "time"
-
- "github.com/containerd/containerd/mount"
- "github.com/containerd/containerd/snapshots"
- "github.com/containerd/containerd/snapshots/overlay"
- "github.com/containerd/continuity/fs/fstest"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-
- "github.com/firecracker-microvm/firecracker-containerd/snapshotter/devmapper"
- "github.com/firecracker-microvm/firecracker-containerd/snapshotter/naive"
-)
-
-var (
- dmPoolDev string
- dmRootPath string
- overlayRootPath string
- naiveRootPath string
-)
-
-func init() {
- flag.StringVar(&dmPoolDev, "dm.thinPoolDev", "", "Pool device to run benchmark on")
- flag.StringVar(&dmRootPath, "dm.rootPath", "", "Root dir for devmapper snapshotter")
- flag.StringVar(&overlayRootPath, "overlay.rootPath", "", "Root dir for overlay snapshotter")
- flag.StringVar(&naiveRootPath, "naive.rootPath", "", "Root dir for naive snapshotter")
- // Avoid mixing benchmark output and INFO messages
- logrus.SetLevel(logrus.ErrorLevel)
-}
-
-func BenchmarkNaive(b *testing.B) {
- if naiveRootPath == "" {
- b.Skip("naive snapshotter root dir must be provided")
- }
-
- snapshotter, err := naive.NewSnapshotter(context.Background(), naiveRootPath)
- require.NoErrorf(b, err, "failed to create naive snapshotter")
-
- defer func() {
- err = snapshotter.Close()
- assert.NoError(b, err)
-
- err = os.RemoveAll(naiveRootPath)
- assert.NoError(b, err)
- }()
-
- benchmarkSnapshotter(b, snapshotter)
-}
-
-func BenchmarkOverlay(b *testing.B) {
- if overlayRootPath == "" {
- b.Skip("overlay root dir must be provided")
- }
-
- snapshotter, err := overlay.NewSnapshotter(overlayRootPath)
- require.NoErrorf(b, err, "failed to create overlay snapshotter")
-
- defer func() {
- err = snapshotter.Close()
- assert.NoError(b, err)
-
- err = os.RemoveAll(overlayRootPath)
- assert.NoError(b, err)
- }()
-
- benchmarkSnapshotter(b, snapshotter)
-}
-
-func BenchmarkDeviceMapper(b *testing.B) {
- if dmPoolDev == "" {
- b.Skip("devmapper benchmark requires thin-pool device to be prepared in advance and provided")
- }
-
- if dmRootPath == "" {
- b.Skip("devmapper snapshotter root dir must be provided")
- }
-
- config := &devmapper.Config{
- PoolName: dmPoolDev,
- RootPath: dmRootPath,
- BaseImageSize: "16Mb",
- }
-
- ctx := context.Background()
-
- snapshotter, err := devmapper.NewSnapshotter(ctx, config)
- require.NoError(b, err)
-
- defer func() {
- err := snapshotter.ResetPool(ctx)
- assert.NoError(b, err)
-
- err = snapshotter.Close()
- assert.NoError(b, err)
-
- err = os.RemoveAll(dmRootPath)
- assert.NoError(b, err)
- }()
-
- benchmarkSnapshotter(b, snapshotter)
-}
-
-// benchmarkSnapshotter tests snapshotter performance.
-// It writes 16 layers with randomly created, modified, or removed files.
-// Depending on layer index different sets of files are modified.
-// In addition to total snapshotter execution time, benchmark outputs a few additional
-// details - time taken to Prepare layer, mount, write data and unmount time,
-// and Commit snapshot time.
-func benchmarkSnapshotter(b *testing.B, snapshotter snapshots.Snapshotter) {
- const (
- layerCount = 16
- fileSizeBytes = int64(1 * 1024 * 1024) // 1 MB
- )
-
- var (
- total = 0
- layers = make([]fstest.Applier, 0, layerCount)
- layerIndex = int64(0)
- )
-
- for i := 1; i <= layerCount; i++ {
- appliers := makeApplier(i, fileSizeBytes)
- layers = append(layers, fstest.Apply(appliers...))
- total += len(appliers)
- }
-
- var (
- benchN int
- prepareDuration time.Duration
- writeDuration time.Duration
- commitDuration time.Duration
- )
-
- // Wrap test with Run so additional details output will be added right below the benchmark result
- b.Run("run", func(b *testing.B) {
- var (
- ctx = context.Background()
- parent string
- current string
- )
-
- // Reset durations since test might be ran multiple times
- prepareDuration = 0
- writeDuration = 0
- commitDuration = 0
- benchN = b.N
-
- b.SetBytes(int64(total) * fileSizeBytes)
-
- var timer time.Time
- for i := 0; i < b.N; i++ {
- for l := 0; l < layerCount; l++ {
- current = fmt.Sprintf("prepare-layer-%d", atomic.AddInt64(&layerIndex, 1))
-
- timer = time.Now()
- mounts, err := snapshotter.Prepare(ctx, current, parent)
- require.NoError(b, err)
- prepareDuration += time.Since(timer)
-
- timer = time.Now()
- err = mount.WithTempMount(ctx, mounts, layers[l].Apply)
- require.NoError(b, err)
- writeDuration += time.Since(timer)
-
- parent = fmt.Sprintf("committed-%d", atomic.AddInt64(&layerIndex, 1))
-
- timer = time.Now()
- err = snapshotter.Commit(ctx, parent, current)
- require.NoError(b, err)
- commitDuration += time.Since(timer)
- }
- }
- })
-
- // Output extra measurements - total time taken to Prepare, mount and write data, and Commit
- const outputFormat = "%-25s\t%s\n"
- fmt.Fprintf(os.Stdout,
- outputFormat,
- b.Name()+"/prepare",
- testing.BenchmarkResult{N: benchN, T: prepareDuration})
-
- fmt.Fprintf(os.Stdout,
- outputFormat,
- b.Name()+"/write",
- testing.BenchmarkResult{N: benchN, T: writeDuration})
-
- fmt.Fprintf(os.Stdout,
- outputFormat,
- b.Name()+"/commit",
- testing.BenchmarkResult{N: benchN, T: commitDuration})
-
- fmt.Fprintln(os.Stdout)
-}
-
-// applierFn represents helper func that implements fstest.Applier
-type applierFn func(root string) error
-
-func (fn applierFn) Apply(root string) error {
- return fn(root)
-}
-
-// updateFile modifies a few bytes in the middle in order to demonstrate the difference in performance
-// for block-based snapshotters (like devicemapper) against file-based snapshotters (like overlay, which need to
-// perform a copy-up of the full file any time a single bit is modified).
-func updateFile(name string) applierFn {
- return func(root string) error {
- path := filepath.Join(root, name)
- file, err := os.OpenFile(path, os.O_WRONLY, 0600)
- if err != nil {
- return errors.Wrapf(err, "failed to open %q", path)
- }
-
- info, err := file.Stat()
- if err != nil {
- return err
- }
-
- var (
- offset = info.Size() / 2
- buf = make([]byte, 4)
- )
-
- if _, err := rand.Read(buf); err != nil {
- return err
- }
-
- if _, err := file.WriteAt(buf, offset); err != nil {
- return errors.Wrapf(err, "failed to write %q at offset %d", path, offset)
- }
-
- return file.Close()
- }
-}
-
-// makeApplier returns a slice of fstest.Applier where files are written randomly.
-// Depending on layer index, the returned layers will overwrite some files with the
-// same generated names with new contents or deletions.
-func makeApplier(layerIndex int, fileSizeBytes int64) []fstest.Applier {
- seed := time.Now().UnixNano()
-
- switch {
- case layerIndex%3 == 0:
- return []fstest.Applier{
- updateFile("/a"),
- updateFile("/b"),
- fstest.CreateRandomFile("/c", seed, fileSizeBytes, 0777),
- updateFile("/d"),
- fstest.CreateRandomFile("/f", seed, fileSizeBytes, 0777),
- updateFile("/e"),
- fstest.RemoveAll("/g"),
- fstest.CreateRandomFile("/h", seed, fileSizeBytes, 0777),
- updateFile("/i"),
- fstest.CreateRandomFile("/j", seed, fileSizeBytes, 0777),
- }
- case layerIndex%2 == 0:
- return []fstest.Applier{
- updateFile("/a"),
- fstest.CreateRandomFile("/b", seed, fileSizeBytes, 0777),
- fstest.RemoveAll("/c"),
- fstest.CreateRandomFile("/d", seed, fileSizeBytes, 0777),
- updateFile("/e"),
- fstest.RemoveAll("/f"),
- fstest.CreateRandomFile("/g", seed, fileSizeBytes, 0777),
- updateFile("/h"),
- fstest.CreateRandomFile("/i", seed, fileSizeBytes, 0777),
- updateFile("/j"),
- }
- default:
- return []fstest.Applier{
- fstest.CreateRandomFile("/a", seed, fileSizeBytes, 0777),
- fstest.CreateRandomFile("/b", seed, fileSizeBytes, 0777),
- fstest.CreateRandomFile("/c", seed, fileSizeBytes, 0777),
- fstest.CreateRandomFile("/d", seed, fileSizeBytes, 0777),
- fstest.CreateRandomFile("/e", seed, fileSizeBytes, 0777),
- fstest.CreateRandomFile("/f", seed, fileSizeBytes, 0777),
- fstest.CreateRandomFile("/g", seed, fileSizeBytes, 0777),
- fstest.CreateRandomFile("/h", seed, fileSizeBytes, 0777),
- fstest.CreateRandomFile("/i", seed, fileSizeBytes, 0777),
- fstest.CreateRandomFile("/j", seed, fileSizeBytes, 0777),
- }
- }
-}
diff --git a/snapshotter/cmd/devmapper/.gitignore b/snapshotter/cmd/devmapper/.gitignore
deleted file mode 100644
index 0637db1d5..000000000
--- a/snapshotter/cmd/devmapper/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-devmapper_snapshotter
diff --git a/snapshotter/cmd/devmapper/Makefile b/snapshotter/cmd/devmapper/Makefile
deleted file mode 100644
index 1dd7d7e94..000000000
--- a/snapshotter/cmd/devmapper/Makefile
+++ /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.
-
-GOMOD := $(shell go env GOMOD)
-GOSUM := $(GOMOD:.mod=.sum)
-
-all: devmapper_snapshotter
-
-devmapper_snapshotter: *.go $(GOMOD) $(GOSUM)
- go build -o devmapper_snapshotter
-
-install: devmapper_snapshotter
- install -D -o root -g root -m755 -t $(INSTALLROOT)/bin devmapper_snapshotter
-
-clean:
- - rm -f devmapper_snapshotter
-
-.PHONY: all clean install
diff --git a/snapshotter/cmd/devmapper/README.md b/snapshotter/cmd/devmapper/README.md
deleted file mode 100644
index 2d02df25b..000000000
--- a/snapshotter/cmd/devmapper/README.md
+++ /dev/null
@@ -1,138 +0,0 @@
-# devmapper snapshotter for containerd and Firecracker
-
-This component is a
-[snapshotter](https://github.com/containerd/containerd/blob/master/design/snapshots.md)
-[plugin](https://github.com/containerd/containerd/blob/master/PLUGINS.md) for
-containerd that stores snapshots in ext4-formatted filesystem images in a
-devicemapper thin pool. The snapshots created by this snapshotter are usable
-with the containerd-firecracker-runtime to run microVM-backed containers with
-the Firecracker VMM.
-
-## Installation
-
-To make containerd aware of this plugin, you need to register it in
-containerd's configuration file. This file is typically located at
-`/etc/containerd/config.toml`.
-
-Here's a sample entry that can be made in the configuration file:
-
-```toml
-[proxy_plugins]
- [proxy_plugins.firecracker-dm-snapshotter]
- type = "snapshot"
- address = "/var/run/firecracker-dm-snapshotter.sock"
-```
-
-The name of the plugin in this example is "firecracker-dm-snapshotter". The
-`address` entry points to a socket file exposed by the snapshotter, which is
-determined when you run it.
-
-## Usage
-
-```
-./devmapper_snapshotter -address UNIX-DOMAIN-SOCKET -config CONFIG -debug
-```
-
-To run the snapshotter, you must specify the path to a Unix domain socket and a
-path to a JSON configuration file. The config file must contain the following
-fields:
-
-* `RootPath` - a directory where the metadata will be available
-* `PoolName` - a name to use for the devicemapper thin pool
-* `BaseImageSize` - defines how much space to allocate when creating the base
- device
-
-Here's a sample entry that can be made in the configuration file `/etc/firecracker-dm-snapshotter/config.json`:
-```json
-{
- "base_image_size" : "20GB",
- "root_path" : "/tmp/test",
- "pool_name" : "test"
-}
-```
-Note: The `test` thin-pool needs to be prepared in advance.
-
-For example, to run the snapshotter with its domain socket at
-`/var/run/firecracker-dm-snapshotter.sock` and its configuration file at
-`/etc/firecracker-dm-snapshotter/config.json` you would run the snapshotter
-plugin process as follows:
-
-```
-./devmapper_snapshotter -address /var/run/firecracker-dm-snapshotter.sock -config /etc/firecracker-dm-snapshotter/config.json
-```
-
-Now you can use snapshotter with containerd:
-
-```
-CONTAINERD_SNAPSHOTTER=firecracker-dm-snapshotter ctr images pull docker.io/library/alpine:latest
-```
-
-## Using container storage setup tool
-
-The device mapper snapshotter is compatible with `container_storage_setup` [utility](https://github.com/projectatomic/container-storage-setup)
-(formerly known as `docker-storage-setup`). `container-storage-setup` is a script to
-configure COW file systems like devicemapper and overlayfs. It is usually run via a systemd service.
-
-The snapshotter supports a subset of `dm.*` command line flags generated by `container_storage_setup`.
-
-Here is a quick tutorial how it can be used togather.
-
-- Run an EC2 instance with additional EBS volume `/dev/sdb` (this example uses CentOS 7)
-- Install container storage setup tool
-
- ```bash
- $ yum update -y
- $ yum install -y container-storage-setup
- ```
-
-- Create thin-pool device using `container-storage-setup` tool
-
- ```bash
- $ touch /etc/sysconfig/docker-storage-setup
- $ echo DEVS=/dev/xvdb >> /etc/sysconfig/docker-storage-setup
- $ echo VG=test >> /etc/sysconfig/docker-storage-setup
- $ container-storage-setup
- INFO: Volume group backing root filesystem could not be determined
- INFO: Writing zeros to first 4MB of device /dev/xvdb
- 4+0 records in
- 4+0 records out
- 4194304 bytes (4.2 MB) copied, 0.0162157 s, 259 MB/s
- INFO: Device node /dev/xvdb1 exists.
- Physical volume "/dev/xvdb1" successfully created.
- Volume group "test" successfully created
- Rounding up size to full physical extent 20.00 MiB
- Thin pool volume with chunk size 512.00 KiB can address at most 126.50 TiB of data.
- Logical volume "docker-pool" created.
- Logical volume test/docker-pool changed.
- ```
-
- Run `man container-storage-setup` to get more information how to setup storage for container runtimes.
-
-- `container-storage-setup` creates a runtime storage file which looks as follows:
-
- ```bash
- $ cat /etc/sysconfig/docker-storage
- DOCKER_STORAGE_OPTIONS="--storage-driver devicemapper --storage-opt dm.fs=xfs --storage-opt dm.thinpooldev=/dev/mapper/test-docker--pool "
- ```
-
-- Now you can create a service script to run the device mapper snapshotter
-
- ```
- EnvironmentFile=/etc/sysconfig/docker-storage
- ...
- ExecStart=./devmapper_snapshotter -address /var/run/firecracker-dm-snapshotter.sock -debug $DOCKER_STORAGE_OPTIONS
- ...
- ```
-
- The output should look like:
- ```
- INFO[0000] loaded configuration file "/etc/containerd/devmapper-snapshotter.json"
- INFO[0000] applying storage opt: dm.fs=xfs
- WARN[0000] "xfs" not supported, defaulting to ext4
- INFO[0000] applying storage opt: dm.thinpooldev=/dev/mapper/test-docker--pool
- INFO[0000] initializing pool device "test-docker--pool"
- INFO[0000] using dmsetup:
- Library version: 1.02.110 (2015-10-30)
- Driver version: 4.37.0
- INFO[0000] running gRPC server unix_addr=/var/run/firecracker-dm-snapshotter.sock
- ```
diff --git a/snapshotter/cmd/devmapper/flag.go b/snapshotter/cmd/devmapper/flag.go
deleted file mode 100644
index 9448d1abb..000000000
--- a/snapshotter/cmd/devmapper/flag.go
+++ /dev/null
@@ -1,66 +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.
-
-package main
-
-import (
- "fmt"
- "os"
- "strings"
-)
-
-// parseKeyValueOpt splits a string to key=value
-func parseKeyValueOpt(opt string) (string, string, error) {
- pair := strings.SplitN(opt, "=", 2)
- if len(pair) != 2 {
- return "", "", fmt.Errorf("failed to split option: %q", opt)
- }
-
- key := strings.TrimSpace(pair[0])
- if key == "" {
- return "", "", fmt.Errorf("option %q has no key", opt)
- }
-
- value := strings.TrimSpace(pair[1])
-
- return key, value, nil
-}
-
-// visitKeyValueOpts finds all --optName key=value in command line string and invokes callback for each pair
-func visitKeyValueOpts(optName string, optFn func(key, value string) error) error {
- // Nothing to visit
- if len(os.Args) < 3 {
- return nil
- }
-
- // Skip exec path
- args := os.Args[1:]
-
- for i := 0; i < len(args)-1; i++ {
- if optName != args[i] {
- continue
- }
-
- // Next element must be key=value pair
- key, value, err := parseKeyValueOpt(args[i+1])
- if err != nil {
- return err
- }
-
- if err := optFn(key, value); err != nil {
- return err
- }
- }
-
- return nil
-}
diff --git a/snapshotter/cmd/devmapper/flag_test.go b/snapshotter/cmd/devmapper/flag_test.go
deleted file mode 100644
index 4c5f57880..000000000
--- a/snapshotter/cmd/devmapper/flag_test.go
+++ /dev/null
@@ -1,130 +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.
-
-package main
-
-import (
- "errors"
- "os"
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestParseKeyValueOpt(t *testing.T) {
- key, value, err := parseKeyValueOpt("dm.baseSize=1")
- assert.NoError(t, err)
- assert.Equal(t, "dm.baseSize", key)
- assert.Equal(t, "1", value)
-
- key, value, err = parseKeyValueOpt(" dm.device = /dev/mapper/test ")
- assert.NoError(t, err)
- assert.Equal(t, "dm.device", key)
- assert.Equal(t, "/dev/mapper/test", value)
-
- key, value, err = parseKeyValueOpt(" dm.device = ")
- assert.NoError(t, err)
- assert.Equal(t, "dm.device", key)
- assert.Equal(t, "", value)
-}
-
-func TestParseInvalidKeyValueOpt(t *testing.T) {
- _, _, err := parseKeyValueOpt("dm.baseSize")
- assert.Error(t, err)
-
- _, _, err = parseKeyValueOpt("=value")
- assert.Error(t, err)
-}
-
-func TestVisitKeyValueOpts(t *testing.T) {
- var (
- args = os.Args
- called = false
- )
-
- os.Args = []string{"exec_path", "--opt", "a=b"}
- err := visitKeyValueOpts("--opt", func(key, value string) error {
- called = true
- assert.Equal(t, "a", key)
- assert.Equal(t, "b", value)
- return nil
- })
-
- assert.NoError(t, err)
- assert.True(t, called)
-
- defer func() { os.Args = args }()
-}
-
-func TestVisitKeyValueOptCallbackErr(t *testing.T) {
- var (
- args = os.Args
- count = 0
- cbErr = errors.New("test")
- )
-
- os.Args = []string{"exec_path", "--opt", "a=b", "--opt", "c=d"}
- err := visitKeyValueOpts("--opt", func(key, value string) error {
- count++
- return cbErr
- })
-
- assert.Equal(t, cbErr, err)
- assert.Equalf(t, 1, count, "callback should be called exactly once")
-
- defer func() { os.Args = args }()
-}
-
-func TestVisitInvalidOpts(t *testing.T) {
- args := os.Args
-
- tests := []struct {
- name string
- args []string
- err error
- }{
- {"Empty args", []string{}, nil},
- {"Nil", nil, nil},
- {"Empty string", []string{""}, nil},
- {"Empty strings", []string{"", "", "", "", "", ""}, nil},
- {"Just reboot flag", []string{"-reboot"}, nil},
- {"Single opt flag", []string{"--opt"}, nil},
- {"Opt flag at end", []string{"--type", "devmapper", "--opt"}, nil},
- {"Opt flag at start", []string{"--opt", "--type", "devmapper"}, errors.New("failed to split option: \"--type\"")},
- {"Opt with invalid kv pair", []string{"--opt", "1+1"}, errors.New("failed to split option: \"1+1\"")},
- {"Opt with empty kv", []string{"--opt", ""}, errors.New("failed to split option: \"\"")},
- }
-
- for _, test := range tests {
- var (
- expectedErr = test.err
- args = test.args
- )
-
- t.Run(test.name, func(t *testing.T) {
- os.Args = append([]string{"exec_path_to_be_skipped"}, args...)
- err := visitKeyValueOpts("--opt", func(_, _ string) error {
- assert.Fail(t, "callback should not be called")
- return nil
- })
-
- if expectedErr == nil {
- assert.NoError(t, err)
- } else {
- assert.EqualError(t, err, expectedErr.Error())
- }
- })
- }
-
- defer func() { os.Args = args }()
-}
diff --git a/snapshotter/cmd/devmapper/main.go b/snapshotter/cmd/devmapper/main.go
deleted file mode 100644
index 75b3b993d..000000000
--- a/snapshotter/cmd/devmapper/main.go
+++ /dev/null
@@ -1,125 +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.
-
-package main
-
-import (
- "context"
- "flag"
- "os"
- "strings"
-
- "github.com/containerd/containerd/log"
- "github.com/containerd/containerd/snapshots"
- "github.com/docker/go-units"
-
- "github.com/firecracker-microvm/firecracker-containerd/snapshotter"
- "github.com/firecracker-microvm/firecracker-containerd/snapshotter/devmapper"
- "github.com/firecracker-microvm/firecracker-containerd/snapshotter/pkg/dmsetup"
-)
-
-const (
- configPathEnvName = "DEVMAPPER_SNAPSHOTTER_CONFIG_PATH"
- defaultConfigPath = "/etc/containerd/devmapper-snapshotter.json"
-)
-
-func main() {
- var (
- ctx = context.Background()
- config = &devmapper.Config{}
- configPath = ""
- rootPath = ""
- )
-
- flag.StringVar(&configPath, "config", "", "Path to devmapper configuration file")
- flag.StringVar(&rootPath, "path", "", "Path to snapshotter data")
-
- // These flags are needed for compatibility with container-storage-setup tool as there is no way to
- // ignore unknown flags in `flag` package. Storage options are parsed using visitKeyValueOpts func.
- flag.String("storage-driver", "devicemapper", "Storage driver to use. Always devicemapper.")
- flag.Bool("storage-opt", false, "Storage configuration options (compatible with Docker dm.* flags)")
-
- flag.Parse()
-
- // Try load file from disk
- if cfg, err := loadConfig(ctx, configPath); err == nil {
- config = cfg
- } else if err != os.ErrNotExist {
- log.G(ctx).WithError(err).Fatal("failed to load config file")
- }
-
- if rootPath != "" {
- config.RootPath = rootPath
- }
-
- // Append and/or overwrite file configuration with --storage-opt dm.XXX=YYY command line flags
- if err := visitKeyValueOpts("--storage-opt", func(key, value string) error {
- return applyStorageOpt(ctx, key, value, config)
- }); err != nil {
- log.G(ctx).WithError(err).Fatal("failed to apply storage options")
- }
-
- if err := config.Validate(); err != nil {
- log.G(ctx).WithError(err).Fatal("invalid configuration")
- }
-
- snapshotter.Run(func(ctx context.Context) (snapshots.Snapshotter, error) {
- return devmapper.NewSnapshotter(ctx, config)
- })
-}
-
-// loadConfig loads configuration file from disk
-func loadConfig(ctx context.Context, configPath string) (*devmapper.Config, error) {
- if configPath == "" {
- configPath = os.Getenv(configPathEnvName)
- }
-
- if configPath == "" {
- configPath = defaultConfigPath
- }
-
- config, err := devmapper.LoadConfig(configPath)
- if err != nil {
- return nil, err
- }
-
- log.G(ctx).Infof("loaded configuration file %q", configPath)
- return config, nil
-}
-
-// applyStorageOpt overwrites configuration with --storage-opt command line flags
-func applyStorageOpt(ctx context.Context, key, value string, config *devmapper.Config) error {
- log.G(ctx).Infof("applying storage opt: %s=%s", key, value)
-
- switch key {
- case "dm.basesize":
- size, err := units.RAMInBytes(value)
- if err != nil {
- return err
- }
-
- config.BaseImageSize = value
- config.BaseImageSizeBytes = uint64(size)
- case "dm.thinpooldev":
- config.PoolName = strings.TrimPrefix(value, dmsetup.DevMapperDir)
- case "dm.fs":
- // TODO: Support alternative file systems (https://github.com/firecracker-microvm/firecracker-containerd/issues/44)
- if value != "ext4" {
- log.G(ctx).Warnf("%q not supported, defaulting to ext4", value)
- }
- default:
- log.G(ctx).Warnf("ignoring unsupported flag %q", key)
- }
-
- return nil
-}
diff --git a/snapshotter/cmd/devmapper/main_test.go b/snapshotter/cmd/devmapper/main_test.go
deleted file mode 100644
index 9fd57f465..000000000
--- a/snapshotter/cmd/devmapper/main_test.go
+++ /dev/null
@@ -1,47 +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.
-
-package main
-
-import (
- "context"
- "testing"
-
- "github.com/stretchr/testify/assert"
-
- "github.com/firecracker-microvm/firecracker-containerd/snapshotter/devmapper"
-)
-
-func TestApplyStorageOpt(t *testing.T) {
- var (
- config = &devmapper.Config{}
- ctx = context.Background()
- )
-
- opts := map[string]string{
- "dm.basesize": "10gb",
- "dm.metadatadev": "/meta_dev",
- "dm.datadev": "/data_dev",
- "dm.thinpooldev": "/pool_dev",
- "dm.blocksize": "100mb",
- }
-
- for key, value := range opts {
- err := applyStorageOpt(ctx, key, value, config)
- assert.NoErrorf(t, err, "failed to apply opt %q", key)
- }
-
- assert.Equal(t, "10gb", config.BaseImageSize)
- assert.EqualValues(t, 10*1024*1024*1024, config.BaseImageSizeBytes)
- assert.Equal(t, "/pool_dev", config.PoolName)
-}
diff --git a/snapshotter/cmd/naive/.gitignore b/snapshotter/cmd/naive/.gitignore
deleted file mode 100644
index c8e0cbdd3..000000000
--- a/snapshotter/cmd/naive/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-naive_snapshotter
diff --git a/snapshotter/cmd/naive/Makefile b/snapshotter/cmd/naive/Makefile
deleted file mode 100644
index 40bcd7acd..000000000
--- a/snapshotter/cmd/naive/Makefile
+++ /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.
-
-GOMOD := $(shell go env GOMOD)
-GOSUM := $(GOMOD:.mod=.sum)
-
-all: naive_snapshotter
-
-naive_snapshotter: *.go $(GOMOD) $(GOSUM)
- go build -o naive_snapshotter
-
-install: naive_snapshotter
- install -D -o root -g root -m755 -t $(INSTALLROOT)/bin naive_snapshotter
-
-clean:
- - rm -f naive_snapshotter
-
-.PHONY: all clean install
diff --git a/snapshotter/cmd/naive/README.md b/snapshotter/cmd/naive/README.md
deleted file mode 100644
index 9715db7fe..000000000
--- a/snapshotter/cmd/naive/README.md
+++ /dev/null
@@ -1,57 +0,0 @@
-# Naive device snapshotter for containerd and Firecracker
-
-This component is a
-[snapshotter](https://github.com/containerd/containerd/blob/master/design/snapshots.md)
-[plugin](https://github.com/containerd/containerd/blob/master/PLUGINS.md) for
-containerd that stores snapshots in flat, ext4-formatted filesystem images.
-The snapshots created by this snapshotter are usable with the
-containerd-firecracker-runtime to run microVM-backed containers with the
-Firecracker VMM.
-
-This snapshotter plugin is written for broad compatibility, and should run on
-any Linux system capable of running Firecracker and containerd. However, it
-sacrifices efficiency in order to achieve this compatibility. Each layer in a
-container image is represented as a unique filesystem image. Each container is
-given a complete private copy of it's root filesystem image upon creation. Thus,
-container creation is expensive in terms IO and disk space.
-
-## Installation
-
-To make containerd aware of this plugin, you need to register it in
-containerd's configuration file. This file is typically located at
-`/etc/containerd/config.toml`.
-
-Here's a sample entry that can be made in the configuration file:
-
-```toml
-[proxy_plugins]
- [proxy_plugins.firecracker-snapshotter]
- type = "snapshot"
- address = "/var/run/firecracker-snapshotter.sock"
-```
-
-The name of the plugin in this example is "firecracker-snapshotter". The
-`address` entry points to a socket file exposed by the snapshotter, which is
-determined when you run it.
-
-## Usage
-
-```
-./naive_snapshotter -address UNIX-DOMAIN-SOCKET -path ROOT -debug
-```
-
-To run the snapshotter, you must specify both a Unix domain socket and a root
-directory where the snapshots will be stored. For example, to run the
-snapshotter with its domain socket at `/var/run/firecracker-snapshotter.sock`
-and its storage at `/var/lib/firecracker-snapshotter`, you would run the
-snapshotter plugin process as follows:
-
-```
-./naive_snapshotter -address /var/run/firecracker-snapshotter.sock -path /var/lib/firecracker-snapshotter
-```
-
-Now you can use snapshotter with containerd:
-
-```
-CONTAINERD_SNAPSHOTTER=firecracker-snapshotter ctr images pull docker.io/library/alpine:latest
-```
diff --git a/snapshotter/cmd/naive/main.go b/snapshotter/cmd/naive/main.go
deleted file mode 100644
index 7afc46ed1..000000000
--- a/snapshotter/cmd/naive/main.go
+++ /dev/null
@@ -1,34 +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.
-
-package main
-
-import (
- "context"
- "flag"
-
- "github.com/containerd/containerd/snapshots"
-
- "github.com/firecracker-microvm/firecracker-containerd/snapshotter"
- "github.com/firecracker-microvm/firecracker-containerd/snapshotter/naive"
-)
-
-func main() {
- var rootPath string
- flag.StringVar(&rootPath, "path", "./images", "Path to snapshotter data (default: ./images)")
- flag.Parse()
-
- snapshotter.Run(func(ctx context.Context) (snapshots.Snapshotter, error) {
- return naive.NewSnapshotter(ctx, rootPath)
- })
-}
diff --git a/snapshotter/devmapper/config.go b/snapshotter/devmapper/config.go
deleted file mode 100644
index 60b514507..000000000
--- a/snapshotter/devmapper/config.go
+++ /dev/null
@@ -1,99 +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.
-
-package devmapper
-
-import (
- "encoding/json"
- "fmt"
- "io/ioutil"
- "os"
-
- "github.com/docker/go-units"
- "github.com/hashicorp/go-multierror"
- "github.com/pkg/errors"
-)
-
-// Config represents device mapper configuration loaded from file.
-// Size units can be specified in human-readable string format (like "32KIB", "32GB", "32Tb")
-type Config struct {
- // Device snapshotter root directory for metadata
- RootPath string `json:"root_path"`
-
- // Name for 'thin-pool' device to be used by snapshotter (without /dev/mapper/ prefix)
- PoolName string `json:"pool_name"`
-
- // Defines how much space to allocate when creating base image for container
- BaseImageSize string `json:"base_image_size"`
- BaseImageSizeBytes uint64 `json:"-"`
-}
-
-// LoadConfig reads devmapper configuration file JSON format from disk
-func LoadConfig(path string) (*Config, error) {
- if _, err := os.Stat(path); err != nil {
- if os.IsNotExist(err) {
- return nil, os.ErrNotExist
- }
-
- return nil, err
- }
-
- data, err := ioutil.ReadFile(path)
- if err != nil {
- return nil, errors.Wrap(err, "failed to read file")
- }
-
- config := Config{}
- if err := json.Unmarshal(data, &config); err != nil {
- return nil, errors.Wrapf(err, "failed to unmarshal data at '%s'", path)
- }
-
- if err := config.parse(); err != nil {
- return nil, err
- }
-
- if err := config.Validate(); err != nil {
- return nil, err
- }
-
- return &config, nil
-}
-
-func (c *Config) parse() error {
- baseImageSize, err := units.RAMInBytes(c.BaseImageSize)
- if err != nil {
- return errors.Wrapf(err, "failed to parse base image size: '%s'", c.BaseImageSize)
- }
-
- c.BaseImageSizeBytes = uint64(baseImageSize)
- return nil
-}
-
-// Validate makes sure configuration fields are valid
-func (c *Config) Validate() error {
- var result *multierror.Error
-
- if c.PoolName == "" {
- result = multierror.Append(result, fmt.Errorf("pool_name is required"))
- }
-
- if c.RootPath == "" {
- result = multierror.Append(result, fmt.Errorf("root_path is required"))
- }
-
- if c.BaseImageSize == "" {
- result = multierror.Append(result, fmt.Errorf("base_image_size is required"))
- }
-
- return result.ErrorOrNil()
-}
diff --git a/snapshotter/devmapper/config_test.go b/snapshotter/devmapper/config_test.go
deleted file mode 100644
index 314d71234..000000000
--- a/snapshotter/devmapper/config_test.go
+++ /dev/null
@@ -1,101 +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.
-
-package devmapper
-
-import (
- "encoding/json"
- "io/ioutil"
- "os"
- "testing"
-
- "github.com/hashicorp/go-multierror"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestLoadConfig(t *testing.T) {
- expected := Config{
- RootPath: "/tmp",
- PoolName: "test",
- BaseImageSize: "128Mb",
- }
-
- data, err := json.Marshal(&expected)
- require.NoErrorf(t, err, "failed to serialize config")
-
- file, err := ioutil.TempFile("", "devmapper-config-")
- require.NoError(t, err)
-
- defer func() {
- err := file.Close()
- assert.NoError(t, err)
-
- err = os.Remove(file.Name())
- assert.NoError(t, err)
- }()
-
- _, err = file.Write(data)
- require.NoError(t, err)
-
- loaded, err := LoadConfig(file.Name())
- require.NoError(t, err)
-
- assert.Equal(t, loaded.RootPath, expected.RootPath)
- assert.Equal(t, loaded.PoolName, expected.PoolName)
- assert.Equal(t, loaded.BaseImageSize, expected.BaseImageSize)
-
- assert.EqualValues(t, 128*1024*1024, loaded.BaseImageSizeBytes)
-}
-
-func TestLoadConfigInvalidPath(t *testing.T) {
- _, err := LoadConfig("")
- require.Equal(t, os.ErrNotExist, err)
-
- _, err = LoadConfig("/dev/null")
- require.Error(t, err)
-}
-
-func TestParseInvalidData(t *testing.T) {
- config := Config{
- BaseImageSize: "y",
- }
-
- err := config.parse()
- require.Error(t, err)
- require.EqualError(t, err, "failed to parse base image size: 'y': invalid size: 'y'")
-}
-
-func TestFieldValidation(t *testing.T) {
- config := &Config{}
- err := config.Validate()
- require.Error(t, err)
-
- multErr := (err).(*multierror.Error)
- require.Len(t, multErr.Errors, 3)
-
- assert.Error(t, multErr.Errors[0], "pool_name is empty")
- assert.Error(t, multErr.Errors[1], "root_path is empty")
- assert.Error(t, multErr.Errors[2], "base_image_size is empty")
-}
-
-func TestExistingPoolFieldValidation(t *testing.T) {
- config := &Config{
- PoolName: "test",
- RootPath: "test",
- BaseImageSize: "10mb",
- }
-
- err := config.Validate()
- assert.NoError(t, err)
-}
diff --git a/snapshotter/devmapper/device_info.go b/snapshotter/devmapper/device_info.go
deleted file mode 100644
index 4994283d7..000000000
--- a/snapshotter/devmapper/device_info.go
+++ /dev/null
@@ -1,101 +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.
-
-package devmapper
-
-import (
- "fmt"
-)
-
-const (
- maxDeviceID = 0xffffff // Device IDs are 24-bit numbers
-)
-
-// DeviceState represents current devmapper device state reflected in meta store
-type DeviceState int
-
-const (
- // Unknown means that device just allocated and no operations were performed
- Unknown DeviceState = iota
- // Creating means that device is going to be created
- Creating
- // Created means that devices successfully created
- Created
- // Activating means that device is going to be activated
- Activating
- // Activated means that device successfully activated
- Activated
- // Suspending means that device is going to be suspended
- Suspending
- // Suspended means that device successfully suspended
- Suspended
- // Resuming means that device is going to be resumed from suspended state
- Resuming
- // Resumed means that device successfully resumed
- Resumed
- // Deactivating means that device is going to be deactivated
- Deactivating
- // Deactivated means that device successfully deactivated
- Deactivated
- // Removing means that device is going to be removed
- Removing
- // Removed means that device successfully removed but not yet deleted from meta store
- Removed
-)
-
-func (s DeviceState) String() string {
- switch s {
- case Creating:
- return "Creating"
- case Created:
- return "Created"
- case Activating:
- return "Activating"
- case Activated:
- return "Activated"
- case Suspending:
- return "Suspending"
- case Suspended:
- return "Suspended"
- case Resuming:
- return "Resuming"
- case Resumed:
- return "Resumed"
- case Deactivating:
- return "Deactivating"
- case Deactivated:
- return "Deactivated"
- case Removing:
- return "Removing"
- case Removed:
- return "Removed"
- default:
- return fmt.Sprintf("unknown %d", s)
- }
-}
-
-// DeviceInfo represents metadata for thin device within thin-pool
-type DeviceInfo struct {
- // DeviceID is a 24-bit number assigned to a device within thin-pool device
- DeviceID uint32 `json:"device_id"`
- // Size is a thin device size
- Size uint64 `json:"size"`
- // Name is a device name to be used in /dev/mapper/
- Name string `json:"name"`
- // ParentName is a name of parent device (if snapshot)
- ParentName string `json:"parent_name"`
- // State represents current device state
- State DeviceState `json:"state"`
- // Error details if device state change failed
- Error string `json:"error"`
-}
diff --git a/snapshotter/devmapper/metadata.go b/snapshotter/devmapper/metadata.go
deleted file mode 100644
index 53897915d..000000000
--- a/snapshotter/devmapper/metadata.go
+++ /dev/null
@@ -1,310 +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.
-
-package devmapper
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "strconv"
-
- "github.com/pkg/errors"
- bolt "go.etcd.io/bbolt"
-)
-
-type (
- // DeviceInfoCallback is a callback used for device updates
- DeviceInfoCallback func(deviceInfo *DeviceInfo) error
-)
-
-type deviceIDState byte
-
-const (
- deviceFree deviceIDState = iota
- deviceTaken
-)
-
-// Bucket names
-var (
- devicesBucketName = []byte("devices") // Contains thin devices metadata =
- deviceIDBucketName = []byte("device_ids") // Tracks used device ids =
-)
-
-var (
- // ErrNotFound represents an error returned when object not found in meta store
- ErrNotFound = errors.New("not found")
- // ErrAlreadyExists represents an error returned when object can't be duplicated in meta store
- ErrAlreadyExists = errors.New("object already exists")
-)
-
-// PoolMetadata keeps device info for the given thin-pool device, it also responsible for
-// generating next available device ids and tracking devmapper transaction numbers
-type PoolMetadata struct {
- db *bolt.DB
-}
-
-// NewPoolMetadata creates new or open existing pool metadata database
-func NewPoolMetadata(dbfile string) (*PoolMetadata, error) {
- db, err := bolt.Open(dbfile, 0600, nil)
- if err != nil {
- return nil, err
- }
-
- metadata := &PoolMetadata{db: db}
- if err := metadata.ensureDatabaseInitialized(); err != nil {
- return nil, errors.Wrap(err, "failed to initialize database")
- }
-
- return metadata, nil
-}
-
-// ensureDatabaseInitialized creates buckets required for metadata store in order
-// to avoid bucket existence checks across the code
-func (m *PoolMetadata) ensureDatabaseInitialized() error {
- return m.db.Update(func(tx *bolt.Tx) error {
- if _, err := tx.CreateBucketIfNotExists(devicesBucketName); err != nil {
- return err
- }
-
- if _, err := tx.CreateBucketIfNotExists(deviceIDBucketName); err != nil {
- return err
- }
-
- return nil
- })
-}
-
-// AddDevice saves device info to database.
-func (m *PoolMetadata) AddDevice(ctx context.Context, info *DeviceInfo) error {
- return m.db.Update(func(tx *bolt.Tx) error {
- devicesBucket := tx.Bucket(devicesBucketName)
-
- // Make sure device name is unique
- if err := getObject(devicesBucket, info.Name, nil); err == nil {
- return ErrAlreadyExists
- }
-
- // Find next available device ID
- deviceID, err := getNextDeviceID(tx)
- if err != nil {
- return err
- }
-
- info.DeviceID = deviceID
-
- return putObject(devicesBucket, info.Name, info, false)
- })
-}
-
-// getNextDeviceID finds the next free device ID by taking a cursor
-// through the deviceIDBucketName bucket and finding the next sequentially
-// unassigned ID. Device ID state is marked by a byte deviceFree or
-// deviceTaken. Low device IDs will be reused sooner.
-func getNextDeviceID(tx *bolt.Tx) (uint32, error) {
- bucket := tx.Bucket(deviceIDBucketName)
- cursor := bucket.Cursor()
-
- // Check if any device id can be reused.
- // Bolt stores its keys in byte-sorted order within a bucket.
- // This makes sequential iteration extremely fast.
- for key, taken := cursor.First(); key != nil; key, taken = cursor.Next() {
- isFree := taken[0] == byte(deviceFree)
- if !isFree {
- continue
- }
-
- parsedID, err := strconv.ParseUint(string(key), 10, 32)
- if err != nil {
- return 0, err
- }
-
- id := uint32(parsedID)
- if err := markDeviceID(tx, id, deviceTaken); err != nil {
- return 0, err
- }
-
- return id, nil
- }
-
- // Try allocate new device ID
- seq, err := bucket.NextSequence()
- if err != nil {
- return 0, err
- }
-
- if seq >= maxDeviceID {
- return 0, errors.Errorf("dm-meta: couldn't find free device key")
- }
-
- id := uint32(seq)
- if err := markDeviceID(tx, id, deviceTaken); err != nil {
- return 0, err
- }
-
- return id, nil
-}
-
-// markDeviceID marks a device as deviceFree or deviceTaken
-func markDeviceID(tx *bolt.Tx, deviceID uint32, state deviceIDState) error {
- var (
- bucket = tx.Bucket(deviceIDBucketName)
- key = strconv.FormatUint(uint64(deviceID), 10)
- value = []byte{byte(state)}
- )
-
- if err := bucket.Put([]byte(key), value); err != nil {
- return errors.Wrapf(err, "failed to free device id %q", key)
- }
-
- return nil
-}
-
-// UpdateDevice updates device info in metadata store.
-// The callback should be used to indicate whether device info update was successful or not.
-// An error returned from the callback will rollback the update transaction in the database.
-// Name and Device ID are not allowed to change.
-func (m *PoolMetadata) UpdateDevice(ctx context.Context, name string, fn DeviceInfoCallback) error {
- return m.db.Update(func(tx *bolt.Tx) error {
- var (
- device = &DeviceInfo{}
- bucket = tx.Bucket(devicesBucketName)
- )
-
- if err := getObject(bucket, name, device); err != nil {
- return err
- }
-
- // Don't allow changing these values, keep things in sync with devmapper
- name := device.Name
- devID := device.DeviceID
-
- if err := fn(device); err != nil {
- return err
- }
-
- if name != device.Name {
- return fmt.Errorf("failed to update device info, name didn't match: %q %q", name, device.Name)
- }
-
- if devID != device.DeviceID {
- return fmt.Errorf("failed to update device info, device id didn't match: %d %d", devID, device.DeviceID)
- }
-
- return putObject(bucket, name, device, true)
- })
-}
-
-// GetDevice retrieves device info by name from database
-func (m *PoolMetadata) GetDevice(ctx context.Context, name string) (*DeviceInfo, error) {
- var (
- dev DeviceInfo
- err error
- )
-
- err = m.db.View(func(tx *bolt.Tx) error {
- bucket := tx.Bucket(devicesBucketName)
- return getObject(bucket, name, &dev)
- })
-
- return &dev, err
-}
-
-// RemoveDevice removes device info from store.
-func (m *PoolMetadata) RemoveDevice(ctx context.Context, name string) error {
- return m.db.Update(func(tx *bolt.Tx) error {
- var (
- device = &DeviceInfo{}
- bucket = tx.Bucket(devicesBucketName)
- )
-
- if err := getObject(bucket, name, device); err != nil {
- return err
- }
-
- if err := bucket.Delete([]byte(name)); err != nil {
- return errors.Wrapf(err, "failed to delete device info for %q", name)
- }
-
- if err := markDeviceID(tx, device.DeviceID, deviceFree); err != nil {
- return err
- }
-
- return nil
- })
-}
-
-// GetDeviceNames retrieves the list of device names currently stored in database
-func (m *PoolMetadata) GetDeviceNames(ctx context.Context) ([]string, error) {
- var (
- names []string
- err error
- )
-
- err = m.db.View(func(tx *bolt.Tx) error {
- bucket := tx.Bucket(devicesBucketName)
- return bucket.ForEach(func(k, _ []byte) error {
- names = append(names, string(k))
- return nil
- })
- })
-
- if err != nil {
- return nil, err
- }
-
- return names, nil
-}
-
-// Close closes metadata store
-func (m *PoolMetadata) Close() error {
- if err := m.db.Close(); err != nil && err != bolt.ErrDatabaseNotOpen {
- return err
- }
-
- return nil
-}
-
-func putObject(bucket *bolt.Bucket, key string, obj interface{}, overwrite bool) error {
- keyBytes := []byte(key)
-
- if !overwrite && bucket.Get(keyBytes) != nil {
- return errors.Errorf("object with key %q already exists", key)
- }
-
- data, err := json.Marshal(obj)
- if err != nil {
- return errors.Wrapf(err, "failed to marshal object with key %q", key)
- }
-
- if err := bucket.Put(keyBytes, data); err != nil {
- return errors.Wrapf(err, "failed to insert object with key %q", key)
- }
-
- return nil
-}
-
-func getObject(bucket *bolt.Bucket, key string, obj interface{}) error {
- data := bucket.Get([]byte(key))
- if data == nil {
- return ErrNotFound
- }
-
- if obj != nil {
- if err := json.Unmarshal(data, obj); err != nil {
- return errors.Wrapf(err, "failed to unmarshal object with key %q", key)
- }
- }
-
- return nil
-}
diff --git a/snapshotter/devmapper/metadata_test.go b/snapshotter/devmapper/metadata_test.go
deleted file mode 100644
index d468183f0..000000000
--- a/snapshotter/devmapper/metadata_test.go
+++ /dev/null
@@ -1,184 +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.
-
-package devmapper
-
-import (
- "context"
- "io/ioutil"
- "os"
- "path/filepath"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-var (
- testCtx = context.Background()
-)
-
-func TestPoolMetadata_AddDevice(t *testing.T) {
- tempDir, store := createStore(t)
- defer cleanupStore(t, tempDir, store)
-
- expected := &DeviceInfo{
- Name: "test2",
- ParentName: "test1",
- Size: 1,
- State: Activated,
- }
-
- err := store.AddDevice(testCtx, expected)
- assert.NoError(t, err)
-
- result, err := store.GetDevice(testCtx, "test2")
- assert.NoError(t, err)
-
- assert.Equal(t, expected.Name, result.Name)
- assert.Equal(t, expected.ParentName, result.ParentName)
- assert.Equal(t, expected.Size, result.Size)
- assert.Equal(t, expected.State, result.State)
- assert.NotZero(t, result.DeviceID)
- assert.Equal(t, expected.DeviceID, result.DeviceID)
-}
-
-func TestPoolMetadata_AddDeviceRollback(t *testing.T) {
- tempDir, store := createStore(t)
- defer cleanupStore(t, tempDir, store)
-
- err := store.AddDevice(testCtx, &DeviceInfo{Name: ""})
- assert.Error(t, err)
-
- _, err = store.GetDevice(testCtx, "")
- assert.Equal(t, ErrNotFound, err)
-}
-
-func TestPoolMetadata_AddDeviceDuplicate(t *testing.T) {
- tempDir, store := createStore(t)
- defer cleanupStore(t, tempDir, store)
-
- err := store.AddDevice(testCtx, &DeviceInfo{Name: "test"})
- assert.NoError(t, err)
-
- err = store.AddDevice(testCtx, &DeviceInfo{Name: "test"})
- assert.Equal(t, ErrAlreadyExists, err)
-}
-
-func TestPoolMetadata_ReuseDeviceID(t *testing.T) {
- tempDir, store := createStore(t)
- defer cleanupStore(t, tempDir, store)
-
- info1 := &DeviceInfo{Name: "test1"}
- err := store.AddDevice(testCtx, info1)
- assert.NoError(t, err)
-
- info2 := &DeviceInfo{Name: "test2"}
- err = store.AddDevice(testCtx, info2)
- assert.NoError(t, err)
-
- assert.NotEqual(t, info1.DeviceID, info2.DeviceID)
- assert.NotZero(t, info1.DeviceID)
-
- err = store.RemoveDevice(testCtx, info2.Name)
- assert.NoError(t, err)
-
- info3 := &DeviceInfo{Name: "test3"}
- err = store.AddDevice(testCtx, info3)
- assert.NoError(t, err)
-
- assert.Equal(t, info2.DeviceID, info3.DeviceID)
-}
-
-func TestPoolMetadata_RemoveDevice(t *testing.T) {
- tempDir, store := createStore(t)
- defer cleanupStore(t, tempDir, store)
-
- err := store.AddDevice(testCtx, &DeviceInfo{Name: "test"})
- assert.NoError(t, err)
-
- err = store.RemoveDevice(testCtx, "test")
- assert.NoError(t, err)
-
- _, err = store.GetDevice(testCtx, "test")
- assert.Equal(t, ErrNotFound, err)
-}
-
-func TestPoolMetadata_UpdateDevice(t *testing.T) {
- tempDir, store := createStore(t)
- defer cleanupStore(t, tempDir, store)
-
- oldInfo := &DeviceInfo{
- Name: "test1",
- ParentName: "test2",
- Size: 3,
- State: Activated,
- }
-
- err := store.AddDevice(testCtx, oldInfo)
- assert.NoError(t, err)
-
- err = store.UpdateDevice(testCtx, oldInfo.Name, func(info *DeviceInfo) error {
- info.ParentName = "test5"
- info.Size = 6
- info.State = Created
- return nil
- })
-
- assert.NoError(t, err)
-
- newInfo, err := store.GetDevice(testCtx, "test1")
- require.NoError(t, err)
-
- assert.Equal(t, "test1", newInfo.Name)
- assert.Equal(t, "test5", newInfo.ParentName)
- assert.EqualValues(t, 6, newInfo.Size)
- assert.Equal(t, Created, newInfo.State)
-}
-
-func TestPoolMetadata_GetDeviceNames(t *testing.T) {
- tempDir, store := createStore(t)
- defer cleanupStore(t, tempDir, store)
-
- err := store.AddDevice(testCtx, &DeviceInfo{Name: "test1"})
- assert.NoError(t, err)
-
- err = store.AddDevice(testCtx, &DeviceInfo{Name: "test2"})
- assert.NoError(t, err)
-
- names, err := store.GetDeviceNames(testCtx)
- assert.NoError(t, err)
- require.Len(t, names, 2)
-
- assert.Equal(t, "test1", names[0])
- assert.Equal(t, "test2", names[1])
-}
-
-func createStore(t *testing.T) (tempDir string, store *PoolMetadata) {
- tempDir, err := ioutil.TempDir("", "pool-metadata-")
- require.NoErrorf(t, err, "couldn't create temp directory for metadata tests")
-
- path := filepath.Join(tempDir, "test.db")
- metadata, err := NewPoolMetadata(path)
- require.NoError(t, err)
-
- return tempDir, metadata
-}
-
-func cleanupStore(t *testing.T, tempDir string, store *PoolMetadata) {
- err := store.Close()
- assert.NoErrorf(t, err, "failed to close metadata store")
-
- err = os.RemoveAll(tempDir)
- assert.NoErrorf(t, err, "failed to cleanup temp directory")
-}
diff --git a/snapshotter/devmapper/pool_device.go b/snapshotter/devmapper/pool_device.go
deleted file mode 100644
index 8b0bee84d..000000000
--- a/snapshotter/devmapper/pool_device.go
+++ /dev/null
@@ -1,381 +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.
-
-package devmapper
-
-import (
- "context"
- "path/filepath"
- "strconv"
-
- "github.com/containerd/containerd/log"
- "github.com/hashicorp/go-multierror"
- "github.com/pkg/errors"
-
- "github.com/firecracker-microvm/firecracker-containerd/snapshotter/pkg/dmsetup"
-)
-
-// PoolDevice ties together data and metadata volumes, represents thin-pool and manages volumes, snapshots and device ids.
-type PoolDevice struct {
- poolName string
- metadata *PoolMetadata
-}
-
-// NewPoolDevice creates new thin-pool from existing data and metadata volumes.
-// If pool 'poolName' already exists, it'll be reloaded with new parameters.
-func NewPoolDevice(ctx context.Context, config *Config) (*PoolDevice, error) {
- log.G(ctx).Infof("initializing pool device %q", config.PoolName)
-
- version, err := dmsetup.Version()
- if err != nil {
- log.G(ctx).Errorf("dmsetup not available")
- return nil, err
- }
-
- log.G(ctx).Infof("using dmsetup:\n%s", version)
-
- dbpath := filepath.Join(config.RootPath, config.PoolName+".db")
- poolMetaStore, err := NewPoolMetadata(dbpath)
- if err != nil {
- return nil, err
- }
-
- // Make sure pool exists and available
- poolPath := dmsetup.GetFullDevicePath(config.PoolName)
- if _, err := dmsetup.Info(poolPath); err != nil {
- return nil, errors.Wrapf(err, "failed to query pool %q", poolPath)
- }
-
- return &PoolDevice{
- poolName: config.PoolName,
- metadata: poolMetaStore,
- }, nil
-}
-
-// transition invokes 'updateStateFn' callback to perform devmapper operation and reflects device state changes/errors in meta store.
-// 'tryingState' will be set before invoking callback. If callback succeeded 'successState' will be set, otherwise
-// error details will be recorded in meta store.
-func (p *PoolDevice) transition(ctx context.Context, deviceName string, tryingState DeviceState, successState DeviceState, updateStateFn func() error) error {
- // Set device to trying state
- uerr := p.metadata.UpdateDevice(ctx, deviceName, func(deviceInfo *DeviceInfo) error {
- deviceInfo.State = tryingState
- return nil
- })
-
- if uerr != nil {
- return errors.Wrapf(uerr, "failed to set device %q state to %q", deviceName, tryingState)
- }
-
- var result *multierror.Error
-
- // Invoke devmapper operation
- err := updateStateFn()
-
- if err != nil {
- result = multierror.Append(result, err)
- }
-
- // If operation succeeded transition to success state, otherwise save error details
- uerr = p.metadata.UpdateDevice(ctx, deviceName, func(deviceInfo *DeviceInfo) error {
- if err == nil {
- deviceInfo.State = successState
- deviceInfo.Error = ""
- } else {
- deviceInfo.Error = err.Error()
- }
- return nil
- })
-
- if uerr != nil {
- result = multierror.Append(result, uerr)
- }
-
- return result.ErrorOrNil()
-}
-
-// CreateThinDevice creates new devmapper thin-device with given name and size.
-// Device ID for thin-device will be allocated from metadata store.
-// If allocation successful, device will be activated with /dev/mapper/
-func (p *PoolDevice) CreateThinDevice(ctx context.Context, deviceName string, virtualSizeBytes uint64) (retErr error) {
- info := &DeviceInfo{
- Name: deviceName,
- Size: virtualSizeBytes,
- State: Unknown,
- }
-
- // Save initial device metadata and allocate new device ID from store
- if err := p.metadata.AddDevice(ctx, info); err != nil {
- return errors.Wrapf(err, "failed to save initial metadata for new thin device %q", deviceName)
- }
-
- defer func() {
- if retErr == nil {
- return
- }
-
- // Rollback metadata
- retErr = multierror.Append(retErr, p.metadata.RemoveDevice(ctx, info.Name))
- }()
-
- // Create thin device
- if err := p.createDevice(ctx, info); err != nil {
- return err
- }
-
- defer func() {
- if retErr == nil {
- return
- }
-
- // Rollback creation
- retErr = multierror.Append(retErr, p.deleteDevice(ctx, info))
- }()
-
- return p.activateDevice(ctx, info)
-}
-
-// createDevice creates thin device
-func (p *PoolDevice) createDevice(ctx context.Context, info *DeviceInfo) error {
- if err := p.transition(ctx, info.Name, Creating, Created, func() error {
- return dmsetup.CreateDevice(p.poolName, info.DeviceID)
- }); err != nil {
- return errors.Wrapf(err, "failed to create new thin device %q (dev: %d)", info.Name, info.DeviceID)
- }
-
- return nil
-}
-
-// activateDevice activates thin device
-func (p *PoolDevice) activateDevice(ctx context.Context, info *DeviceInfo) error {
- if err := p.transition(ctx, info.Name, Activating, Activated, func() error {
- return dmsetup.ActivateDevice(p.poolName, info.Name, info.DeviceID, info.Size, "")
- }); err != nil {
- return errors.Wrapf(err, "failed to activate new thin device %q (dev: %d)", info.Name, info.DeviceID)
- }
-
- return nil
-}
-
-// CreateSnapshotDevice creates and activates new thin-device from parent thin-device (makes snapshot)
-func (p *PoolDevice) CreateSnapshotDevice(ctx context.Context, deviceName string, snapshotName string, virtualSizeBytes uint64) (retErr error) {
- baseInfo, err := p.metadata.GetDevice(ctx, deviceName)
- if err != nil {
- return errors.Wrapf(err, "failed to query device metadata for %q", deviceName)
- }
-
- // Suspend thin device if it was activated previously to avoid corruptions
- isActivated := p.IsActivated(baseInfo.Name)
- if isActivated {
- if err := p.suspendDevice(ctx, baseInfo); err != nil {
- return err
- }
-
- // Resume back base thin device on exit
- defer func() {
- retErr = multierror.Append(retErr, p.resumeDevice(ctx, baseInfo)).ErrorOrNil()
- }()
- }
-
- snapInfo := &DeviceInfo{
- Name: snapshotName,
- Size: virtualSizeBytes,
- ParentName: deviceName,
- State: Unknown,
- }
-
- // Save snapshot metadata and allocate new device ID
- if err := p.metadata.AddDevice(ctx, snapInfo); err != nil {
- return errors.Wrapf(err, "failed to save initial metadata for snapshot %q", snapshotName)
- }
-
- defer func() {
- if retErr == nil {
- return
- }
-
- // Rollback metadata
- retErr = multierror.Append(retErr, p.metadata.RemoveDevice(ctx, snapInfo.Name))
- }()
-
- // Create thin device snapshot
- if err := p.createSnapshot(ctx, baseInfo, snapInfo); err != nil {
- return err
- }
-
- defer func() {
- if retErr == nil {
- return
- }
-
- // Rollback snapshot creation
- retErr = multierror.Append(retErr, p.deleteDevice(ctx, snapInfo))
- }()
-
- // Activate snapshot device
- return p.activateDevice(ctx, snapInfo)
-}
-
-func (p *PoolDevice) suspendDevice(ctx context.Context, info *DeviceInfo) error {
- if err := p.transition(ctx, info.Name, Suspending, Suspended, func() error {
- return dmsetup.SuspendDevice(info.Name)
- }); err != nil {
- return errors.Wrapf(err, "failed to suspend device %q", info.Name)
- }
-
- return nil
-}
-
-func (p *PoolDevice) resumeDevice(ctx context.Context, info *DeviceInfo) error {
- if err := p.transition(ctx, info.Name, Resuming, Resumed, func() error {
- return dmsetup.ResumeDevice(info.Name)
- }); err != nil {
- return errors.Wrapf(err, "failed to resume device %q", info.Name)
- }
-
- return nil
-}
-
-func (p *PoolDevice) createSnapshot(ctx context.Context, baseInfo, snapInfo *DeviceInfo) error {
- if err := p.transition(ctx, snapInfo.Name, Creating, Created, func() error {
- return dmsetup.CreateSnapshot(p.poolName, snapInfo.DeviceID, baseInfo.DeviceID)
- }); err != nil {
- return errors.Wrapf(err,
- "failed to create snapshot %q (dev: %d) from %q (dev: %d)",
- snapInfo.Name,
- snapInfo.DeviceID,
- baseInfo.Name,
- baseInfo.DeviceID)
- }
-
- return nil
-}
-
-// DeactivateDevice deactivates thin device
-func (p *PoolDevice) DeactivateDevice(ctx context.Context, deviceName string, deferred bool) error {
- if !p.IsActivated(deviceName) {
- return nil
- }
-
- opts := []dmsetup.RemoveDeviceOpt{dmsetup.RemoveWithForce, dmsetup.RemoveWithRetries}
- if deferred {
- opts = append(opts, dmsetup.RemoveDeferred)
- }
-
- if err := p.transition(ctx, deviceName, Deactivating, Deactivated, func() error {
- return dmsetup.RemoveDevice(deviceName, opts...)
- }); err != nil {
- return errors.Wrapf(err, "failed to deactivate device %q", deviceName)
- }
-
- return nil
-}
-
-// IsActivated returns true if thin-device is activated and not suspended
-func (p *PoolDevice) IsActivated(deviceName string) bool {
- infos, err := dmsetup.Info(deviceName)
- if err != nil || len(infos) != 1 {
- // Couldn't query device info, device not active
- return false
- }
-
- if devInfo := infos[0]; devInfo.Suspended {
- return false
- }
-
- return true
-}
-
-// GetUsage reports total size in bytes consumed by a thin-device.
-// It relies on the number of used blocks reported by 'dmsetup status'.
-// The output looks like:
-// device2: 0 204800 thin 17280 204799
-// Where 17280 is the number of used sectors
-func (p *PoolDevice) GetUsage(deviceName string) (int64, error) {
- status, err := dmsetup.Status(deviceName)
- if err != nil {
- return 0, errors.Wrapf(err, "can't get status for device %q", deviceName)
- }
-
- if len(status.Params) == 0 {
- return 0, errors.Errorf("failed to get the number of used blocks, unexpected output from dmsetup status")
- }
-
- count, err := strconv.ParseInt(status.Params[0], 10, 64)
- if err != nil {
- return 0, errors.Wrapf(err, "failed to parse status params: %q", status.Params[0])
- }
-
- return count * dmsetup.SectorSize, nil
-}
-
-// RemoveDevice completely wipes out thin device from thin-pool and frees it's device ID
-func (p *PoolDevice) RemoveDevice(ctx context.Context, deviceName string) error {
- info, err := p.metadata.GetDevice(ctx, deviceName)
- if err != nil {
- return errors.Wrapf(err, "can't query metadata for device %q", deviceName)
- }
-
- if err := p.DeactivateDevice(ctx, deviceName, true); err != nil {
- return err
- }
-
- if err := p.deleteDevice(ctx, info); err != nil {
- return err
- }
-
- // Remove record from meta store and free device ID
- if err := p.metadata.RemoveDevice(ctx, deviceName); err != nil {
- return errors.Wrapf(err, "can't remove device %q metadata from store after removal", deviceName)
- }
-
- return nil
-}
-
-func (p *PoolDevice) deleteDevice(ctx context.Context, info *DeviceInfo) error {
- if err := p.transition(ctx, info.Name, Removing, Removed, func() error {
- // Send 'delete' message to thin-pool
- return dmsetup.DeleteDevice(p.poolName, info.DeviceID)
- }); err != nil {
- return errors.Wrapf(err, "failed to delete device %q (dev id: %d)", info.Name, info.DeviceID)
- }
-
- return nil
-}
-
-// RemovePool deactivates all child thin-devices and removes thin-pool device
-func (p *PoolDevice) RemovePool(ctx context.Context) error {
- deviceNames, err := p.metadata.GetDeviceNames(ctx)
- if err != nil {
- return errors.Wrap(err, "can't query device names")
- }
-
- var result *multierror.Error
-
- // Deactivate devices if any
- for _, name := range deviceNames {
- if err := p.DeactivateDevice(ctx, name, true); err != nil {
- result = multierror.Append(result, errors.Wrapf(err, "failed to remove %q", name))
- }
- }
-
- if err := dmsetup.RemoveDevice(p.poolName, dmsetup.RemoveWithForce, dmsetup.RemoveWithRetries, dmsetup.RemoveDeferred); err != nil {
- result = multierror.Append(result, errors.Wrapf(err, "failed to remove pool %q", p.poolName))
- }
-
- return result.ErrorOrNil()
-}
-
-// Close closes pool device (thin-pool will not be removed)
-func (p *PoolDevice) Close() error {
- return p.metadata.Close()
-}
diff --git a/snapshotter/devmapper/pool_device_test.go b/snapshotter/devmapper/pool_device_test.go
deleted file mode 100644
index 9d7acee14..000000000
--- a/snapshotter/devmapper/pool_device_test.go
+++ /dev/null
@@ -1,239 +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.
-
-package devmapper
-
-import (
- "context"
- "fmt"
- "io/ioutil"
- "os"
- "os/exec"
- "path/filepath"
- "testing"
- "time"
-
- "github.com/containerd/containerd/mount"
- "github.com/docker/go-units"
- "github.com/sirupsen/logrus"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-
- "github.com/firecracker-microvm/firecracker-containerd/internal"
- "github.com/firecracker-microvm/firecracker-containerd/snapshotter/pkg/dmsetup"
- "github.com/firecracker-microvm/firecracker-containerd/snapshotter/pkg/losetup"
-)
-
-const (
- thinDevice1 = "thin-1"
- thinDevice2 = "thin-2"
- snapDevice1 = "snap-1"
- device1Size = 100000
- device2Size = 200000
- testsPrefix = "devmapper-snapshotter-tests-"
-)
-
-// TestPoolDevice runs integration tests for pool device.
-// The following scenario implemented:
-// - Create pool device with name 'test-pool-device'
-// - Create two thin volumes 'thin-1' and 'thin-2'
-// - Write ext4 file system on 'thin-1' and make sure it'errs moutable
-// - Write v1 test file on 'thin-1' volume
-// - Take 'thin-1' snapshot 'snap-1'
-// - Change v1 file to v2 on 'thin-1'
-// - Mount 'snap-1' and make sure test file is v1
-// - Unmount volumes and remove all devices
-func TestPoolDevice(t *testing.T) {
- internal.RequiresRoot(t)
- logrus.SetLevel(logrus.DebugLevel)
- ctx := context.Background()
-
- tempDir, err := ioutil.TempDir("", "pool-device-test-")
- require.NoErrorf(t, err, "couldn't get temp directory for testing")
-
- _, loopDataDevice := createLoopbackDevice(t, tempDir)
- _, loopMetaDevice := createLoopbackDevice(t, tempDir)
-
- poolName := fmt.Sprintf("test-pool-device-%d", time.Now().Nanosecond())
- err = dmsetup.CreatePool(poolName, loopDataDevice, loopMetaDevice, 64*1024/dmsetup.SectorSize)
- require.NoErrorf(t, err, "failed to create pool %q", poolName)
-
- defer func() {
- // Detach loop devices and remove images
- err := losetup.DetachLoopDevice(loopDataDevice, loopMetaDevice)
- assert.NoError(t, err)
-
- err = os.RemoveAll(tempDir)
- assert.NoErrorf(t, err, "couldn't cleanup temp directory")
- }()
-
- config := &Config{
- PoolName: poolName,
- RootPath: tempDir,
- BaseImageSize: "16mb",
- BaseImageSizeBytes: 16 * 1024 * 1024,
- }
-
- pool, err := NewPoolDevice(ctx, config)
- require.NoError(t, err, "can't create device pool")
- require.NotNil(t, pool)
-
- defer func() {
- err := pool.RemovePool(ctx)
- require.NoError(t, err, "can't close device pool")
- }()
-
- // Create thin devices
- t.Run("CreateThinDevice", func(t *testing.T) {
- testCreateThinDevice(t, pool)
- })
-
- // Make ext4 filesystem on 'thin-1'
- t.Run("MakeFileSystem", func(t *testing.T) {
- testMakeFileSystem(t, pool)
- })
-
- // Mount 'thin-1'
- mount.WithTempMount(ctx, getMounts(thinDevice1), func(thin1MountPath string) error {
- // Write v1 test file on 'thin-1' device
- thin1TestFilePath := filepath.Join(thin1MountPath, "TEST")
- err := ioutil.WriteFile(thin1TestFilePath, []byte("test file (v1)"), 0700)
- require.NoErrorf(t, err, "failed to write test file v1 on '%s' volume", thinDevice1)
-
- // Take snapshot of 'thin-1'
- t.Run("CreateSnapshotDevice", func(t *testing.T) {
- testCreateSnapshot(t, pool)
- })
-
- // Update TEST file on 'thin-1' to v2
- err = ioutil.WriteFile(thin1TestFilePath, []byte("test file (v2)"), 0700)
- assert.NoErrorf(t, err, "failed to write test file v2 on 'thin-1' volume after taking snapshot")
-
- return err
- })
-
- // Mount 'snap-1' and make sure TEST file is v1
- mount.WithTempMount(ctx, getMounts(snapDevice1), func(snap1MountPath string) error {
- // Read test file from snapshot device and make sure it's v1
- fileData, err := ioutil.ReadFile(filepath.Join(snap1MountPath, "TEST"))
- assert.NoErrorf(t, err, "couldn't read test file from '%s' device", snapDevice1)
- assert.EqualValues(t, "test file (v1)", string(fileData), "test file content is invalid on snapshot")
-
- return err
- })
-
- t.Run("DeactivateDevice", func(t *testing.T) {
- testDeactivateThinDevice(t, pool)
- })
-
- t.Run("RemoveDevice", func(t *testing.T) {
- testRemoveThinDevice(t, pool)
- })
-}
-
-func testCreateThinDevice(t *testing.T, pool *PoolDevice) {
- ctx := context.Background()
-
- err := pool.CreateThinDevice(ctx, thinDevice1, device1Size)
- require.NoError(t, err, "can't create first thin device")
-
- err = pool.CreateThinDevice(ctx, thinDevice1, device1Size)
- require.Error(t, err, "device pool allows duplicated device names")
-
- err = pool.CreateThinDevice(ctx, thinDevice2, device2Size)
- require.NoError(t, err, "can't create second thin device")
-
- deviceInfo1, err := pool.metadata.GetDevice(ctx, thinDevice1)
- assert.NoError(t, err)
-
- deviceInfo2, err := pool.metadata.GetDevice(ctx, thinDevice2)
- assert.NoError(t, err)
-
- assert.NotEqual(t, deviceInfo1.DeviceID, deviceInfo2.DeviceID, "assigned device ids should be different")
-
- usage, err := pool.GetUsage(thinDevice1)
- assert.NoError(t, err)
- assert.Zero(t, usage)
-}
-
-func testMakeFileSystem(t *testing.T, pool *PoolDevice) {
- devicePath := dmsetup.GetFullDevicePath(thinDevice1)
- args := []string{
- devicePath,
- "-E",
- "nodiscard,lazy_itable_init=0,lazy_journal_init=0",
- }
-
- output, err := exec.Command("mkfs.ext4", args...).CombinedOutput()
- require.NoErrorf(t, err, "failed to make filesystem on '%s': %s", thinDevice1, string(output))
-
- usage, err := pool.GetUsage(thinDevice1)
- assert.NoError(t, err)
- assert.NotZero(t, usage)
-}
-
-func testCreateSnapshot(t *testing.T, pool *PoolDevice) {
- err := pool.CreateSnapshotDevice(context.Background(), thinDevice1, snapDevice1, device1Size)
- assert.NoErrorf(t, err, "failed to create snapshot from '%s' volume", thinDevice1)
-}
-
-func testDeactivateThinDevice(t *testing.T, pool *PoolDevice) {
- deviceList := []string{
- thinDevice2,
- snapDevice1,
- }
-
- for _, deviceName := range deviceList {
- assert.True(t, pool.IsActivated(deviceName))
-
- err := pool.DeactivateDevice(context.Background(), deviceName, false)
- assert.NoErrorf(t, err, "failed to remove '%s'", deviceName)
-
- assert.False(t, pool.IsActivated(deviceName))
- }
-}
-
-func testRemoveThinDevice(t *testing.T, pool *PoolDevice) {
- err := pool.RemoveDevice(testCtx, thinDevice1)
- assert.NoErrorf(t, err, "should delete thin device from pool")
-}
-
-func getMounts(thinDeviceName string) []mount.Mount {
- return []mount.Mount{
- {
- Source: dmsetup.GetFullDevicePath(thinDeviceName),
- Type: "ext4",
- },
- }
-}
-
-func createLoopbackDevice(t *testing.T, dir string) (string, string) {
- file, err := ioutil.TempFile(dir, testsPrefix)
- require.NoError(t, err)
-
- size, err := units.RAMInBytes("128Mb")
- require.NoError(t, err)
-
- err = file.Truncate(size)
- require.NoError(t, err)
-
- err = file.Close()
- require.NoError(t, err)
-
- imagePath := file.Name()
-
- loopDevice, err := losetup.AttachLoopDevice(imagePath)
- require.NoError(t, err)
-
- return imagePath, loopDevice
-}
diff --git a/snapshotter/devmapper/snapshotter.go b/snapshotter/devmapper/snapshotter.go
deleted file mode 100644
index 88f68c836..000000000
--- a/snapshotter/devmapper/snapshotter.go
+++ /dev/null
@@ -1,442 +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.
-
-package devmapper
-
-import (
- "context"
- "fmt"
- "os"
- "os/exec"
- "path/filepath"
- "strings"
- "sync"
-
- "github.com/containerd/containerd/log"
- "github.com/containerd/containerd/mount"
- "github.com/containerd/containerd/snapshots"
- "github.com/containerd/containerd/snapshots/storage"
- "github.com/hashicorp/go-multierror"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-
- "github.com/firecracker-microvm/firecracker-containerd/snapshotter/pkg/dmsetup"
-)
-
-const (
- metadataFileName = "metadata.db"
- fsTypeExt4 = "ext4"
-)
-
-type closeFunc func() error
-
-// Snapshotter implements containerd's snapshotter (https://godoc.org/github.com/containerd/containerd/snapshots#Snapshotter)
-// based on Linux device-mapper targets.
-type Snapshotter struct {
- store *storage.MetaStore
- pool *PoolDevice
- config *Config
- cleanupFn []closeFunc
- closeOnce sync.Once
-}
-
-// NewSnapshotter creates new device mapper snapshotter.
-// Internally it creates thin-pool device (or reloads if it's already exists) and
-// initializes a database file for metadata.
-func NewSnapshotter(ctx context.Context, config *Config) (*Snapshotter, error) {
- // Make sure snapshotter configuration valid before running
- if err := config.parse(); err != nil {
- return nil, err
- }
-
- if err := config.Validate(); err != nil {
- return nil, err
- }
-
- var cleanupFn []closeFunc
-
- if err := os.MkdirAll(config.RootPath, 0750); err != nil && !os.IsExist(err) {
- return nil, errors.Wrapf(err, "failed to create root directory: %s", config.RootPath)
- }
-
- store, err := storage.NewMetaStore(filepath.Join(config.RootPath, metadataFileName))
- if err != nil {
- return nil, errors.Wrap(err, "failed to create metastore")
- }
-
- cleanupFn = append(cleanupFn, store.Close)
-
- poolDevice, err := NewPoolDevice(ctx, config)
- if err != nil {
- return nil, err
- }
-
- cleanupFn = append(cleanupFn, poolDevice.Close)
-
- return &Snapshotter{
- store: store,
- config: config,
- pool: poolDevice,
- cleanupFn: cleanupFn,
- }, nil
-}
-
-// Stat returns the info for an active or committed snapshot from store
-func (s *Snapshotter) Stat(ctx context.Context, key string) (snapshots.Info, error) {
- log.G(ctx).WithField("key", key).Debug("stat")
-
- var (
- info snapshots.Info
- err error
- )
-
- err = s.withTransaction(ctx, false, func(ctx context.Context) error {
- _, info, _, err = storage.GetInfo(ctx, key)
- return err
- })
-
- return info, err
-}
-
-// Update updates an existing snapshot info's data
-func (s *Snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) {
- log.G(ctx).Debugf("update: %s", strings.Join(fieldpaths, ", "))
-
- var err error
- err = s.withTransaction(ctx, true, func(ctx context.Context) error {
- info, err = storage.UpdateInfo(ctx, info, fieldpaths...)
- return err
- })
-
- return info, err
-}
-
-// Usage returns the resource usage of an active or committed snapshot excluding the usage of parent snapshots.
-func (s *Snapshotter) Usage(ctx context.Context, key string) (snapshots.Usage, error) {
- log.G(ctx).WithField("key", key).Debug("usage")
-
- var (
- id string
- err error
- info snapshots.Info
- usage snapshots.Usage
- )
-
- err = s.withTransaction(ctx, false, func(ctx context.Context) error {
- id, info, usage, err = storage.GetInfo(ctx, key)
- if err != nil {
- return err
- }
-
- if info.Kind == snapshots.KindActive {
- deviceName := s.getDeviceName(id)
- usage.Size, err = s.pool.GetUsage(deviceName)
- if err != nil {
- return err
- }
- }
-
- if info.Parent != "" {
- // GetInfo returns total number of bytes used by a snapshot (including parent).
- // So subtract parent usage in order to get delta consumed by layer itself.
- _, _, parentUsage, err := storage.GetInfo(ctx, info.Parent)
- if err != nil {
- return err
- }
-
- usage.Size -= parentUsage.Size
- }
-
- return err
- })
-
- return usage, err
-}
-
-// Mounts return the list of mounts for the active or view snapshot
-func (s *Snapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, error) {
- log.G(ctx).WithField("key", key).Debug("mounts")
-
- var (
- snap storage.Snapshot
- err error
- )
-
- err = s.withTransaction(ctx, false, func(ctx context.Context) error {
- snap, err = storage.GetSnapshot(ctx, key)
- return err
- })
-
- return s.buildMounts(snap), nil
-}
-
-// Prepare creates thin device for an active snapshot identified by key
-func (s *Snapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
- log.G(ctx).WithFields(logrus.Fields{"key": key, "parent": parent}).Debug("prepare")
-
- var (
- mounts []mount.Mount
- err error
- )
-
- err = s.withTransaction(ctx, true, func(ctx context.Context) error {
- mounts, err = s.createSnapshot(ctx, snapshots.KindActive, key, parent, opts...)
- return err
- })
-
- return mounts, err
-}
-
-// View creates readonly thin device for the given snapshot key
-func (s *Snapshotter) View(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
- log.G(ctx).WithFields(logrus.Fields{"key": key, "parent": parent}).Debug("prepare")
-
- var (
- mounts []mount.Mount
- err error
- )
-
- err = s.withTransaction(ctx, true, func(ctx context.Context) error {
- mounts, err = s.createSnapshot(ctx, snapshots.KindView, key, parent, opts...)
- return err
- })
-
- return mounts, err
-}
-
-// Commit marks an active snapshot as committed in meta store.
-// Block device unmount operation captures snapshot changes by itself, so no
-// additional actions needed within Commit operation.
-func (s *Snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error {
- log.G(ctx).WithFields(logrus.Fields{"name": name, "key": key}).Debug("commit")
-
- return s.withTransaction(ctx, true, func(ctx context.Context) error {
- id, _, _, err := storage.GetInfo(ctx, key)
- if err != nil {
- return err
- }
-
- deviceName := s.getDeviceName(id)
- size, err := s.pool.GetUsage(deviceName)
- if err != nil {
- return err
- }
-
- usage := snapshots.Usage{
- Size: size,
- }
-
- _, err = storage.CommitActive(ctx, key, name, usage, opts...)
- return err
- })
-}
-
-// Remove removes thin device and snapshot metadata by key
-func (s *Snapshotter) Remove(ctx context.Context, key string) error {
- log.G(ctx).WithField("key", key).Debug("remove")
-
- return s.withTransaction(ctx, true, func(ctx context.Context) error {
- return s.removeDevice(ctx, key)
- })
-}
-
-func (s *Snapshotter) removeDevice(ctx context.Context, key string) error {
- snapID, _, err := storage.Remove(ctx, key)
- if err != nil {
- return err
- }
-
- deviceName := s.getDeviceName(snapID)
- if err := s.pool.RemoveDevice(ctx, deviceName); err != nil {
- log.G(ctx).WithError(err).Errorf("failed to remove device")
- return err
- }
-
- return nil
-}
-
-// Walk iterates through all metadata Info for the stored snapshots and calls the provided function for each.
-func (s *Snapshotter) Walk(ctx context.Context, fn func(context.Context, snapshots.Info) error) error {
- log.G(ctx).Debug("walk")
- return s.withTransaction(ctx, false, func(ctx context.Context) error {
- return storage.WalkInfo(ctx, fn)
- })
-}
-
-// ResetPool deactivates and deletes all thin devices in thin-pool.
-// Used for cleaning pool after benchmarking.
-func (s *Snapshotter) ResetPool(ctx context.Context) error {
- names, err := s.pool.metadata.GetDeviceNames(ctx)
- if err != nil {
- return err
- }
-
- var result *multierror.Error
- for _, name := range names {
- if err := s.pool.RemoveDevice(ctx, name); err != nil {
- result = multierror.Append(result, err)
- }
- }
-
- return result.ErrorOrNil()
-}
-
-// Close releases devmapper snapshotter resources.
-// All subsequent Close calls will be ignored.
-func (s *Snapshotter) Close() error {
- log.L.Debug("close")
-
- var result *multierror.Error
- s.closeOnce.Do(func() {
- for _, fn := range s.cleanupFn {
- if err := fn(); err != nil {
- result = multierror.Append(result, err)
- }
- }
- })
-
- return result.ErrorOrNil()
-}
-
-func (s *Snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
- snap, err := storage.CreateSnapshot(ctx, kind, key, parent, opts...)
- if err != nil {
- return nil, err
- }
-
- if len(snap.ParentIDs) == 0 {
- deviceName := s.getDeviceName(snap.ID)
- log.G(ctx).Debugf("creating new thin device '%s'", deviceName)
-
- err := s.pool.CreateThinDevice(ctx, deviceName, s.config.BaseImageSizeBytes)
- if err != nil {
- log.G(ctx).WithError(err).Errorf("failed to create thin device for snapshot %s", snap.ID)
- return nil, err
- }
-
- if err := s.mkfs(ctx, deviceName); err != nil {
- // Rollback thin device creation if mkfs failed
- return nil, multierror.Append(err,
- s.pool.RemoveDevice(ctx, deviceName))
- }
- } else {
- parentDeviceName := s.getDeviceName(snap.ParentIDs[0])
- snapDeviceName := s.getDeviceName(snap.ID)
- log.G(ctx).Debugf("creating snapshot device '%s' from '%s'", snapDeviceName, parentDeviceName)
-
- err := s.pool.CreateSnapshotDevice(ctx, parentDeviceName, snapDeviceName, s.config.BaseImageSizeBytes)
- if err != nil {
- log.G(ctx).WithError(err).Errorf("failed to create snapshot device from parent %s", parentDeviceName)
- return nil, err
- }
- }
-
- mounts := s.buildMounts(snap)
-
- // Remove default directories not expected by the container image
- _ = mount.WithTempMount(ctx, mounts, func(root string) error {
- return os.Remove(filepath.Join(root, "lost+found"))
- })
-
- return mounts, nil
-}
-
-// mkfs creates ext4 filesystem on the given devmapper device
-func (s *Snapshotter) mkfs(ctx context.Context, deviceName string) error {
- args := []string{
- "-E",
- // We don't want any zeroing in advance when running mkfs on thin devices (see "man mkfs.ext4")
- "nodiscard,lazy_itable_init=0,lazy_journal_init=0",
- dmsetup.GetFullDevicePath(deviceName),
- }
-
- log.G(ctx).Debugf("mkfs.ext4 %s", strings.Join(args, " "))
- output, err := exec.Command("mkfs.ext4", args...).CombinedOutput()
- if err != nil {
- log.G(ctx).WithError(err).Errorf("failed to write fs:\n%s", string(output))
- return err
- }
-
- log.G(ctx).Debugf("mkfs:\n%s", string(output))
- return nil
-}
-
-func (s *Snapshotter) getDeviceName(snapID string) string {
- // Add pool name as prefix to avoid collisions with devices from other pools
- return fmt.Sprintf("%s-snap-%s", s.config.PoolName, snapID)
-}
-
-func (s *Snapshotter) getDevicePath(snap storage.Snapshot) string {
- name := s.getDeviceName(snap.ID)
- return dmsetup.GetFullDevicePath(name)
-}
-
-func (s *Snapshotter) buildMounts(snap storage.Snapshot) []mount.Mount {
- var options []string
-
- if snap.Kind != snapshots.KindActive {
- options = append(options, "ro")
- }
-
- mounts := []mount.Mount{
- {
- Source: s.getDevicePath(snap),
- Type: fsTypeExt4,
- Options: options,
- },
- }
-
- return mounts
-}
-
-// withTransaction wraps fn callback with containerd's meta store transaction.
-// If callback returns an error or transaction is not writable, database transaction will be discarded.
-func (s *Snapshotter) withTransaction(ctx context.Context, writable bool, fn func(ctx context.Context) error) error {
- ctx, trans, err := s.store.TransactionContext(ctx, writable)
- if err != nil {
- return err
- }
-
- var result *multierror.Error
-
- err = fn(ctx)
- if err != nil {
- result = multierror.Append(result, err)
- }
-
- // Always rollback if transaction is not writable
- if err != nil || !writable {
- if terr := trans.Rollback(); terr != nil {
- log.G(ctx).WithError(terr).Error("failed to rollback transaction")
- result = multierror.Append(result, errors.Wrap(terr, "rollback failed"))
- }
- } else {
- if terr := trans.Commit(); terr != nil {
- log.G(ctx).WithError(terr).Error("failed to commit transaction")
- result = multierror.Append(result, errors.Wrap(terr, "commit failed"))
- }
- }
-
- if err := result.ErrorOrNil(); err != nil {
- log.G(ctx).WithError(err).Debug("snapshotter error")
-
- // Unwrap if just one error
- if result.Len() == 1 {
- return result.Errors[0]
- }
-
- return err
- }
-
- return nil
-}
diff --git a/snapshotter/devmapper/snapshotter_test.go b/snapshotter/devmapper/snapshotter_test.go
deleted file mode 100644
index 22848327b..000000000
--- a/snapshotter/devmapper/snapshotter_test.go
+++ /dev/null
@@ -1,135 +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.
-
-package devmapper
-
-import (
- "context"
- "fmt"
- "io/ioutil"
- "os"
- "testing"
- "time"
-
- "github.com/containerd/containerd/mount"
- "github.com/containerd/containerd/namespaces"
- "github.com/containerd/containerd/snapshots"
- "github.com/containerd/containerd/snapshots/testsuite"
- "github.com/containerd/continuity/fs/fstest"
- "github.com/hashicorp/go-multierror"
- "github.com/sirupsen/logrus"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-
- "github.com/firecracker-microvm/firecracker-containerd/internal"
- "github.com/firecracker-microvm/firecracker-containerd/snapshotter/pkg/dmsetup"
- "github.com/firecracker-microvm/firecracker-containerd/snapshotter/pkg/losetup"
-)
-
-func TestSnapshotterSuite(t *testing.T) {
- internal.RequiresRoot(t)
- logrus.SetLevel(logrus.DebugLevel)
-
- snapshotterFn := func(ctx context.Context, root string) (snapshots.Snapshotter, func() error, error) {
- // Create loopback devices for each test case
- _, loopDataDevice := createLoopbackDevice(t, root)
- _, loopMetaDevice := createLoopbackDevice(t, root)
-
- poolName := fmt.Sprintf("containerd-snapshotter-suite-pool-%d", time.Now().Nanosecond())
- err := dmsetup.CreatePool(poolName, loopDataDevice, loopMetaDevice, 64*1024/dmsetup.SectorSize)
- require.NoErrorf(t, err, "failed to create pool %q", poolName)
-
- config := &Config{
- RootPath: root,
- PoolName: poolName,
- BaseImageSize: "16Mb",
- }
-
- snap, err := NewSnapshotter(context.Background(), config)
- if err != nil {
- return nil, nil, err
- }
-
- // Remove device mapper pool and detach loop devices after test completes
- removePool := func() error {
- result := multierror.Append(
- snap.pool.RemovePool(ctx),
- losetup.DetachLoopDevice(loopDataDevice, loopMetaDevice))
-
- return result.ErrorOrNil()
- }
-
- // Pool cleanup should be called before closing metadata store (as we need to retrieve device names)
- snap.cleanupFn = append([]closeFunc{removePool}, snap.cleanupFn...)
-
- return snap, snap.Close, nil
- }
-
- testsuite.SnapshotterSuite(t, "devmapper", snapshotterFn)
-
- ctx := context.Background()
- ctx = namespaces.WithNamespace(ctx, "testsuite")
-
- t.Run("DevMapperUsage", func(t *testing.T) {
- tempDir, err := ioutil.TempDir("", "snapshot-suite-usage")
- require.NoError(t, err)
- defer os.RemoveAll(tempDir)
-
- snapshotter, closer, err := snapshotterFn(ctx, tempDir)
- require.NoError(t, err)
- defer closer()
-
- testUsage(t, snapshotter)
- })
-}
-
-// testUsage tests devmapper's Usage implementation. This is an approximate test as it's hard to
-// predict how many blocks will be consumed under different conditions and parameters.
-func testUsage(t *testing.T, snapshotter snapshots.Snapshotter) {
- ctx := context.Background()
-
- // Create empty base layer
- _, err := snapshotter.Prepare(ctx, "prepare-1", "")
- require.NoError(t, err)
-
- emptyLayerUsage, err := snapshotter.Usage(ctx, "prepare-1")
- assert.NoError(t, err)
-
- // Should be > 0 as just written file system also consumes blocks
- assert.NotZero(t, emptyLayerUsage.Size)
-
- err = snapshotter.Commit(ctx, "layer-1", "prepare-1")
- require.NoError(t, err)
-
- // Create child layer with 1MB file
-
- var (
- sizeBytes int64 = 1048576 // 1MB
- baseApplier = fstest.Apply(fstest.CreateRandomFile("/a", 12345679, sizeBytes, 0777))
- )
-
- mounts, err := snapshotter.Prepare(ctx, "prepare-2", "layer-1")
- require.NoError(t, err)
-
- err = mount.WithTempMount(ctx, mounts, baseApplier.Apply)
- require.NoError(t, err)
-
- err = snapshotter.Commit(ctx, "layer-2", "prepare-2")
- require.NoError(t, err)
-
- layer2Usage, err := snapshotter.Usage(ctx, "layer-2")
- require.NoError(t, err)
-
- // Should be at least 1 MB + fs metadata
- assert.InDelta(t, sizeBytes, layer2Usage.Size, 256*dmsetup.SectorSize)
-}
diff --git a/snapshotter/naive/naive.go b/snapshotter/naive/naive.go
deleted file mode 100644
index ba859f48f..000000000
--- a/snapshotter/naive/naive.go
+++ /dev/null
@@ -1,391 +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.
-
-package naive
-
-import (
- "context"
- "os"
- "os/exec"
- "path/filepath"
- "strings"
-
- "github.com/containerd/containerd/log"
- "github.com/containerd/containerd/mount"
- "github.com/containerd/containerd/snapshots"
- "github.com/containerd/containerd/snapshots/storage"
- "github.com/containerd/continuity/fs"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-
- "github.com/firecracker-microvm/firecracker-containerd/snapshotter/pkg/losetup"
-)
-
-const (
- metadataFileName = "metadata.db"
- imageDirName = "images"
- imageFSType = "ext4"
- sparseImageSizeMB = 1024
- mib = 1048576
-)
-
-// Snapshotter implements naive snapshotter for containerd
-type Snapshotter struct {
- root string
- store *storage.MetaStore
-}
-
-// NewSnapshotter creates naive snapshotter for Firecracker.
-// Each layer is represented by separate Linux image and corresponding containerd's snapshot ID.
-// Snapshotter has the following file structure:
-// {root}/images/{ID} - keeps filesystem images
-// {root}/metadata.db - keeps metadata (info and relationships between layers)
-func NewSnapshotter(ctx context.Context, root string) (snapshots.Snapshotter, error) {
- log.G(ctx).WithField("root", root).Info("creating naive snapshotter")
-
- root, err := filepath.Abs(root)
- if err != nil {
- log.G(ctx).WithError(err).Error("failed to get absoluate path")
- return nil, err
- }
-
- for _, path := range []string{
- root,
- filepath.Join(root, imageDirName),
- } {
- if err := os.Mkdir(path, 0750); err != nil && !os.IsExist(err) {
- log.G(ctx).WithError(err).Errorf("mkdir failed for '%s'", path)
- return nil, err
- }
- }
-
- ms, err := storage.NewMetaStore(filepath.Join(root, metadataFileName))
- if err != nil {
- return nil, err
- }
-
- return &Snapshotter{
- root: root,
- store: ms,
- }, nil
-}
-
-// Stat returns the info for an active or committed snapshot by name or key.
-func (s *Snapshotter) Stat(ctx context.Context, key string) (snapshots.Info, error) {
- log.G(ctx).WithField("key", key).Debug("stat")
-
- ctx, trans, err := s.store.TransactionContext(ctx, false)
- if err != nil {
- return snapshots.Info{}, err
- }
-
- defer trans.Rollback()
-
- _, info, _, err := storage.GetInfo(ctx, key)
- if err != nil {
- return snapshots.Info{}, err
- }
-
- return info, nil
-}
-
-// Update updates the info for a snapshot.
-func (s *Snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) {
- log.G(ctx).Debugf("update: %s", strings.Join(fieldpaths, ", "))
-
- ctx, trans, err := s.store.TransactionContext(ctx, true)
- if err != nil {
- return snapshots.Info{}, err
- }
-
- info, err = storage.UpdateInfo(ctx, info, fieldpaths...)
- if err != nil {
- return snapshots.Info{}, complete(ctx, trans, err)
- }
-
- return info, complete(ctx, trans, nil)
-}
-
-// Usage not yet implemented
-func (s *Snapshotter) Usage(ctx context.Context, key string) (snapshots.Usage, error) {
- log.G(ctx).WithField("key", key).Debug("usage")
-
- return snapshots.Usage{}, errors.New("not implemented")
-}
-
-// Mounts returns loop device mount by snapshot key
-func (s *Snapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, error) {
- log.G(ctx).WithField("key", key).Debug("mounts")
-
- ctx, trans, err := s.store.TransactionContext(ctx, false)
- if err != nil {
- return nil, err
- }
-
- defer trans.Rollback()
-
- snap, err := storage.GetSnapshot(ctx, key)
- if err != nil {
- return nil, err
- }
-
- return s.buildMounts(snap)
-}
-
-// Prepare creates block device (sparse image with attached loop device) for an active snapshot identified by key
-func (s *Snapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
- log.G(ctx).WithFields(logrus.Fields{"key": key, "parent": parent}).Debug("prepare")
- return s.createSnapshot(ctx, snapshots.KindActive, key, parent, opts...)
-}
-
-// View creates readonly block device for the given snapshot
-func (s *Snapshotter) View(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
- log.G(ctx).WithFields(logrus.Fields{"key": key, "parent": parent}).Debug("view")
- return s.createSnapshot(ctx, snapshots.KindView, key, parent, opts...)
-}
-
-// Commit detaches loop devices from sparse image if any and updates meta store info.
-// Snapshot changes are captured when unmounting block device with 'sync' flag.
-func (s *Snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error {
- log.G(ctx).WithFields(logrus.Fields{"name": name, "key": key}).Debug("commit")
-
- ctx, trans, err := s.store.TransactionContext(ctx, true)
- if err != nil {
- return err
- }
-
- snapID, _, _, err := storage.GetInfo(ctx, key)
- if err != nil {
- return complete(ctx, trans, err)
- }
-
- imagePath := s.getImagePath(snapID)
- if err := losetup.RemoveLoopDevicesAssociatedWithImage(imagePath); err != nil {
- return complete(ctx, trans, err)
- }
-
- if _, err := storage.CommitActive(ctx, key, name, snapshots.Usage{}, opts...); err != nil {
- return complete(ctx, trans, err)
- }
-
- return complete(ctx, trans, nil)
-}
-
-// Remove unmounts an image and deletes it from images directory
-func (s *Snapshotter) Remove(ctx context.Context, key string) error {
- log.G(ctx).WithField("key", key).Debug("remove")
-
- ctx, trans, err := s.store.TransactionContext(ctx, true)
- if err != nil {
- return err
- }
-
- id, _, err := storage.Remove(ctx, key)
- if err != nil {
- return complete(ctx, trans, err)
- }
-
- imagePath := s.getImagePath(id)
-
- if err := losetup.RemoveLoopDevicesAssociatedWithImage(imagePath); err != nil {
- log.G(ctx).WithError(err).Errorf("failed to detach loop devices from '%s'", imagePath)
- return complete(ctx, trans, err)
- }
-
- if err := os.Remove(imagePath); err != nil {
- log.G(ctx).WithError(err).Errorf("failed to delete image '%s'", imagePath)
- return complete(ctx, trans, err)
- }
-
- return complete(ctx, trans, nil)
-}
-
-// Walk walks all snapshots in the snapshotter. For each snapshot in the snapshotter, the function will be called.
-func (s *Snapshotter) Walk(ctx context.Context, fn func(context.Context, snapshots.Info) error) error {
- log.G(ctx).Debug("walk")
-
- ctx, trans, err := s.store.TransactionContext(ctx, false)
- if err != nil {
- return err
- }
-
- defer trans.Rollback()
- return storage.WalkInfo(ctx, fn)
-}
-
-// Close releases snapshotter resources and detaches loop devices from all images
-func (s *Snapshotter) Close() error {
- log.L.Debug("close")
-
- if err := s.store.Close(); err != nil {
- return err
- }
-
- // Find all images and detach loop devices if any
- imageDir := filepath.Join(s.root, imageDirName)
- err := filepath.Walk(imageDir, func(path string, info os.FileInfo, err error) error {
- if info.IsDir() {
- return nil
- }
-
- fullImagePath := filepath.Join(imageDir, info.Name())
- return losetup.RemoveLoopDevicesAssociatedWithImage(fullImagePath)
- })
-
- return err
-}
-
-// createSnapshot creates an active snapshot for containerd and returns mount path.
-// Behind the scene it creates an image, builds ext4 file system, and takes care of parent snapshot if needed.
-// Command line for this will look like:
-// dd if=/dev/zero of=drive-2.img bs=1k count=102400
-// mkfs -t ext4 image.img
-// If snapshot has a parent, all files from parent will be copied to this snapshot first.
-func (s *Snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
- ctx, trans, err := s.store.TransactionContext(ctx, true)
- if err != nil {
- return nil, err
- }
-
- snap, err := storage.CreateSnapshot(ctx, kind, key, parent, opts...)
- if err != nil {
- return nil, complete(ctx, trans, err)
- }
-
- hasParent := len(snap.ParentIDs) > 0
- if !hasParent {
- // TODO: figure out what to do with file size
- imagePath := s.getImagePath(snap.ID)
- if err := s.createImage(ctx, imagePath, sparseImageSizeMB); err != nil {
- return nil, complete(ctx, trans, err)
- }
- } else {
- parentID := snap.ParentIDs[0]
- log.G(ctx).Infof("copying data from parent snapshot %s", parentID)
-
- if err := fs.CopyFile(s.getImagePath(snap.ID), s.getImagePath(parentID)); err != nil {
- log.G(ctx).WithError(err).Errorf("failed copy parent layer")
- return nil, complete(ctx, trans, err)
- }
- }
-
- mounts, err := s.buildMounts(snap)
- if err != nil {
- return nil, complete(ctx, trans, err)
- }
-
- if !hasParent {
- // lost+found breaks diff comparisons of containerd snapshotter test suite
- // Not needed for containerd snapshots
- _ = mount.WithTempMount(ctx, mounts, func(root string) error {
- return os.Remove(filepath.Join(root, "lost+found"))
- })
- }
-
- return mounts, complete(ctx, trans, nil)
-}
-
-func (s *Snapshotter) createImage(ctx context.Context, imagePath string, fileSizeMB int) error {
- // Create a new empty file and resize
- log.G(ctx).WithField("image", imagePath).Infof("creating new image of size %d MB", fileSizeMB)
- file, err := os.Create(imagePath)
- if err != nil {
- return err
- }
-
- if err := file.Truncate(int64(fileSizeMB) * mib); err != nil {
- return err
- }
-
- if err := file.Close(); err != nil {
- return err
- }
-
- // Build a Linux filesystem
- log.G(ctx).WithField("image", file.Name()).Info("building file system")
- if err := run("mkfs", "-t", imageFSType, "-F", file.Name()); err != nil {
- return err
- }
-
- return nil
-}
-
-func (s *Snapshotter) getImagePath(id string) string {
- return filepath.Join(s.root, imageDirName, id)
-}
-
-// buildMounts returns a mount for the given snapshot.
-// Block device represented as an attached loop device to a sparse image.
-// buildMounts attaches new loop device to an image unless there is an existing association.
-func (s *Snapshotter) buildMounts(snap storage.Snapshot) ([]mount.Mount, error) {
- // Snapshot changes need to be flushed to disk immediately when unmounting.
- options := []string{"sync", "dirsync"}
-
- if snap.Kind != snapshots.KindActive {
- options = append(options, "ro")
- }
-
- var (
- imagePath = s.getImagePath(snap.ID)
- loopDevice string
- )
-
- // Try find existing loop device attached to the given image
- loopDeviceList, err := losetup.FindAssociatedLoopDevices(imagePath)
- if err != nil {
- return nil, err
- }
-
- if len(loopDeviceList) > 0 {
- loopDevice = loopDeviceList[0]
- } else {
- // Find first unused loop device and attach to image
- loopDevice, err = losetup.AttachLoopDevice(imagePath)
- if err != nil {
- return nil, err
- }
- }
-
- mounts := []mount.Mount{
- {
- Source: loopDevice,
- Type: imageFSType,
- Options: options,
- },
- }
-
- return mounts, nil
-}
-
-func run(cmd string, args ...string) error {
- command := exec.Command(cmd, args...)
- if err := command.Run(); err != nil {
- return errors.Wrapf(err, "exec failed: %s %s", cmd, strings.Join(args, " "))
- }
-
- return nil
-}
-
-func complete(ctx context.Context, trans storage.Transactor, err error) error {
- if err != nil {
- if terr := trans.Rollback(); terr != nil {
- log.G(ctx).WithError(terr).Error("failed to rollback transaction")
- }
- } else {
- if terr := trans.Commit(); terr != nil {
- log.G(ctx).WithError(terr).Error("failed to commit transaction")
- }
- }
-
- return err
-}
diff --git a/snapshotter/naive/naive_test.go b/snapshotter/naive/naive_test.go
deleted file mode 100644
index 481d4af36..000000000
--- a/snapshotter/naive/naive_test.go
+++ /dev/null
@@ -1,72 +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.
-
-package naive
-
-import (
- "context"
- _ "crypto/sha256"
- "io/ioutil"
- "os"
- "path/filepath"
- "testing"
-
- "github.com/containerd/containerd/snapshots"
- "github.com/containerd/containerd/snapshots/testsuite"
-
- "github.com/firecracker-microvm/firecracker-containerd/internal"
-)
-
-func TestCreateImage(t *testing.T) {
- internal.RequiresRoot(t)
- snap := Snapshotter{}
-
- tempDir, err := ioutil.TempDir("", "fc-snapshotter")
- if err != nil {
- t.Fatal(err)
- }
-
- defer os.RemoveAll(tempDir)
-
- imgPath := filepath.Join(tempDir, "x.img")
-
- const (
- sizeMiB = 100
- sizeBytes = sizeMiB * mib
- )
-
- err = snap.createImage(context.Background(), imgPath, sizeMiB)
- if err != nil {
- t.Fatal(err)
- }
-
- if stat, err := os.Stat(imgPath); os.IsNotExist(err) {
- t.Fatal("error creating image file")
- } else if stat.Size() != sizeBytes {
- t.Errorf("wrong image size %d != %d", stat.Size(), sizeBytes)
- }
-}
-
-func createSnapshotter(ctx context.Context, root string) (snapshots.Snapshotter, func() error, error) {
- snap, err := NewSnapshotter(ctx, root)
- if err != nil {
- return nil, nil, err
- }
-
- return snap, snap.Close, nil
-}
-
-func TestSnapshotterSuite(t *testing.T) {
- internal.RequiresRoot(t)
- testsuite.SnapshotterSuite(t, "Snapshotter", createSnapshotter)
-}
diff --git a/snapshotter/pkg/dmsetup/dmsetup.go b/snapshotter/pkg/dmsetup/dmsetup.go
deleted file mode 100644
index b707a21e4..000000000
--- a/snapshotter/pkg/dmsetup/dmsetup.go
+++ /dev/null
@@ -1,385 +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.
-
-package dmsetup
-
-import (
- "fmt"
- "os/exec"
- "strconv"
- "strings"
-
- "github.com/pkg/errors"
- "golang.org/x/sys/unix"
-)
-
-const (
- // DevMapperDir represents devmapper devices location
- DevMapperDir = "/dev/mapper/"
- // SectorSize represents the number of bytes in one sector on devmapper devices
- SectorSize = 512
-)
-
-// DeviceInfo represents device info returned by "dmsetup info".
-// dmsetup(8) provides more information on each of these fields.
-type DeviceInfo struct {
- Name string
- BlockDeviceName string
- TableLive bool
- TableInactive bool
- Suspended bool
- ReadOnly bool
- Major uint32
- Minor uint32
- OpenCount uint32 // Open reference count
- TargetCount uint32 // Number of targets in the live table
- EventNumber uint32 // Last event sequence number (used by wait)
-}
-
-var errTable map[string]unix.Errno
-
-func init() {
- // Precompute map of = for optimal lookup
- errTable = make(map[string]unix.Errno)
- for errno := unix.EPERM; errno <= unix.EHWPOISON; errno++ {
- errTable[errno.Error()] = errno
- }
-}
-
-// CreatePool creates a device with the given name, data and metadata file and block size (see "dmsetup create")
-func CreatePool(poolName, dataFile, metaFile string, blockSizeSectors uint32) error {
- thinPool, err := makeThinPoolMapping(dataFile, metaFile, blockSizeSectors)
- if err != nil {
- return err
- }
-
- _, err = dmsetup("create", poolName, "--table", thinPool)
- return err
-}
-
-// ReloadPool reloads existing thin-pool (see "dmsetup reload")
-func ReloadPool(deviceName, dataFile, metaFile string, blockSizeSectors uint32) error {
- thinPool, err := makeThinPoolMapping(dataFile, metaFile, blockSizeSectors)
- if err != nil {
- return err
- }
-
- _, err = dmsetup("reload", deviceName, "--table", thinPool)
- return err
-}
-
-const (
- lowWaterMark = 32768 // Picked arbitrary, might need tuning
- skipZeroing = "skip_block_zeroing" // Skipping zeroing to reduce latency for device creation
-)
-
-// makeThinPoolMapping makes thin-pool table entry
-func makeThinPoolMapping(dataFile, metaFile string, blockSizeSectors uint32) (string, error) {
- dataDeviceSizeBytes, err := BlockDeviceSize(dataFile)
- if err != nil {
- return "", errors.Wrapf(err, "failed to get block device size: %s", dataFile)
- }
-
- // Thin-pool mapping target has the following format:
- // start - starting block in virtual device
- // length - length of this segment
- // metadata_dev - the metadata device
- // data_dev - the data device
- // data_block_size - the data block size in sectors
- // low_water_mark - the low water mark, expressed in blocks of size data_block_size
- // feature_args - the number of feature arguments
- // args
- lengthSectors := dataDeviceSizeBytes / SectorSize
- target := fmt.Sprintf("0 %d thin-pool %s %s %d %d 1 %s",
- lengthSectors,
- metaFile,
- dataFile,
- blockSizeSectors,
- lowWaterMark,
- skipZeroing)
-
- return target, nil
-}
-
-// CreateDevice sends "create_thin " message to the given thin-pool
-func CreateDevice(poolName string, deviceID uint32) error {
- _, err := dmsetup("message", poolName, "0", fmt.Sprintf("create_thin %d", deviceID))
- return err
-}
-
-// ActivateDevice activates the given thin-device using the 'thin' target
-func ActivateDevice(poolName string, deviceName string, deviceID uint32, size uint64, external string) error {
- mapping := makeThinMapping(poolName, deviceID, size, external)
- _, err := dmsetup("create", deviceName, "--table", mapping)
- return err
-}
-
-// makeThinMapping makes thin target table entry
-func makeThinMapping(poolName string, deviceID uint32, sizeBytes uint64, externalOriginDevice string) string {
- lengthSectors := sizeBytes / SectorSize
-
- // Thin target has the following format:
- // start - starting block in virtual device
- // length - length of this segment
- // pool_dev - the thin-pool device, can be /dev/mapper/pool_name or 253:0
- // dev_id - the internal device id of the device to be activated
- // external_origin_dev - an optional block device outside the pool to be treated as a read-only snapshot origin.
- target := fmt.Sprintf("0 %d thin %s %d %s", lengthSectors, GetFullDevicePath(poolName), deviceID, externalOriginDevice)
- return strings.TrimSpace(target)
-}
-
-// SuspendDevice suspends the given device (see "dmsetup suspend")
-func SuspendDevice(deviceName string) error {
- _, err := dmsetup("suspend", deviceName)
- return err
-}
-
-// ResumeDevice resumes the given device (see "dmsetup resume")
-func ResumeDevice(deviceName string) error {
- _, err := dmsetup("resume", deviceName)
- return err
-}
-
-// Table returns the current table for the device
-func Table(deviceName string) (string, error) {
- return dmsetup("table", deviceName)
-}
-
-// CreateSnapshot sends "create_snap" message to the given thin-pool.
-// Caller needs to suspend and resume device if it is active.
-func CreateSnapshot(poolName string, deviceID uint32, baseDeviceID uint32) error {
- _, err := dmsetup("message", poolName, "0", fmt.Sprintf("create_snap %d %d", deviceID, baseDeviceID))
- return err
-}
-
-// DeleteDevice sends "delete " message to the given thin-pool
-func DeleteDevice(poolName string, deviceID uint32) error {
- _, err := dmsetup("message", poolName, "0", fmt.Sprintf("delete %d", deviceID))
- return err
-}
-
-// RemoveDeviceOpt represents command line arguments for "dmsetup remove" command
-type RemoveDeviceOpt string
-
-const (
- // RemoveWithForce flag replaces the table with one that fails all I/O if
- // open device can't be removed
- RemoveWithForce RemoveDeviceOpt = "--force"
- // RemoveWithRetries option will cause the operation to be retried
- // for a few seconds before failing
- RemoveWithRetries RemoveDeviceOpt = "--retry"
- // RemoveDeferred flag will enable deferred removal of open devices,
- // the device will be removed when the last user closes it
- RemoveDeferred RemoveDeviceOpt = "--deferred"
-)
-
-// RemoveDevice removes a device (see "dmsetup remove")
-func RemoveDevice(deviceName string, opts ...RemoveDeviceOpt) error {
- args := []string{
- "remove",
- }
-
- for _, opt := range opts {
- args = append(args, string(opt))
- }
-
- args = append(args, GetFullDevicePath(deviceName))
-
- _, err := dmsetup(args...)
- return err
-}
-
-// Info outputs device information (see "dmsetup info").
-// If device name is empty, all device infos will be returned.
-func Info(deviceName string) ([]*DeviceInfo, error) {
- output, err := dmsetup(
- "info",
- "--columns",
- "--noheadings",
- "-o",
- "name,blkdevname,attr,major,minor,open,segments,events",
- "--separator",
- " ",
- deviceName)
-
- if err != nil {
- return nil, err
- }
-
- var (
- lines = strings.Split(output, "\n")
- devices = make([]*DeviceInfo, len(lines))
- )
-
- for i, line := range lines {
- var (
- attr = ""
- info = &DeviceInfo{}
- )
-
- _, err := fmt.Sscan(line,
- &info.Name,
- &info.BlockDeviceName,
- &attr,
- &info.Major,
- &info.Minor,
- &info.OpenCount,
- &info.TargetCount,
- &info.EventNumber)
-
- if err != nil {
- return nil, errors.Wrapf(err, "failed to parse line %q", line)
- }
-
- // Parse attributes (see "man 8 dmsetup" for details)
- info.Suspended = strings.Contains(attr, "s")
- info.ReadOnly = strings.Contains(attr, "r")
- info.TableLive = strings.Contains(attr, "L")
- info.TableInactive = strings.Contains(attr, "I")
-
- devices[i] = info
- }
-
- return devices, nil
-}
-
-// Version returns "dmsetup version" output
-func Version() (string, error) {
- return dmsetup("version")
-}
-
-// DeviceStatus represents devmapper device status information
-type DeviceStatus struct {
- Offset int64
- Length int64
- Target string
- Params []string
-}
-
-// Status provides status information for devmapper device
-func Status(deviceName string) (*DeviceStatus, error) {
- var (
- err error
- status DeviceStatus
- )
-
- output, err := dmsetup("status", deviceName)
- if err != nil {
- return nil, err
- }
-
- // Status output format:
- // Offset (int64)
- // Length (int64)
- // Target type (string)
- // Params (Array of strings)
- const MinParseCount = 4
- parts := strings.Split(output, " ")
- if len(parts) < MinParseCount {
- return nil, errors.Errorf("failed to parse output: %q", output)
- }
-
- status.Offset, err = strconv.ParseInt(parts[0], 10, 64)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to parse offset: %q", parts[0])
- }
-
- status.Length, err = strconv.ParseInt(parts[1], 10, 64)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to parse length: %q", parts[1])
- }
-
- status.Target = parts[2]
- status.Params = parts[3:]
-
- return &status, nil
-}
-
-// GetFullDevicePath returns full path for the given device name (like "/dev/mapper/name")
-func GetFullDevicePath(deviceName string) string {
- if strings.HasPrefix(deviceName, DevMapperDir) {
- return deviceName
- }
-
- return DevMapperDir + deviceName
-}
-
-// BlockDeviceSize returns size of block device in bytes
-func BlockDeviceSize(devicePath string) (uint64, error) {
- data, err := exec.Command("blockdev", "--getsize64", "-q", devicePath).CombinedOutput()
- output := string(data)
- if err != nil {
- return 0, errors.Wrapf(err, output)
- }
-
- output = strings.TrimSuffix(output, "\n")
- return strconv.ParseUint(output, 10, 64)
-}
-
-func dmsetup(args ...string) (string, error) {
- data, err := exec.Command("dmsetup", args...).CombinedOutput()
- output := string(data)
- if err != nil {
- // Try find Linux error code otherwise return generic error with dmsetup output
- if errno, ok := tryGetUnixError(output); ok {
- return "", errno
- }
-
- return "", errors.Wrapf(err, "dmsetup %s\nerror: %s\n", strings.Join(args, " "), output)
- }
-
- output = strings.Trim(output, "\n")
- output = strings.TrimSpace(output)
-
- return output, nil
-}
-
-// tryGetUnixError tries to find Linux error code from dmsetup output
-func tryGetUnixError(output string) (unix.Errno, bool) {
- // It's useful to have Linux error codes like EBUSY, EPERM, ..., instead of just text.
- // Unfortunately there is no better way than extracting/comparing error text.
- text := parseDmsetupError(output)
- if text == "" {
- return 0, false
- }
-
- err, ok := errTable[text]
- return err, ok
-}
-
-// dmsetup returns error messages in format:
-// device-mapper: message ioctl on failed: File exists\n
-// Command failed\n
-// parseDmsetupError extracts text between "failed: " and "\n"
-func parseDmsetupError(output string) string {
- lines := strings.SplitN(output, "\n", 2)
- if len(lines) < 2 {
- return ""
- }
-
- const failedSubstr = "failed: "
-
- line := lines[0]
- idx := strings.LastIndex(line, failedSubstr)
- if idx == -1 {
- return ""
- }
-
- str := line[idx:]
-
- // Strip "failed: " prefix
- str = strings.TrimPrefix(str, failedSubstr)
-
- str = strings.ToLower(str)
- return str
-}
diff --git a/snapshotter/pkg/dmsetup/dmsetup_test.go b/snapshotter/pkg/dmsetup/dmsetup_test.go
deleted file mode 100644
index ec4d4d0dc..000000000
--- a/snapshotter/pkg/dmsetup/dmsetup_test.go
+++ /dev/null
@@ -1,202 +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.
-
-package dmsetup
-
-import (
- "io/ioutil"
- "os"
- "strings"
- "testing"
-
- "github.com/docker/go-units"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "golang.org/x/sys/unix"
-
- "github.com/firecracker-microvm/firecracker-containerd/internal"
- "github.com/firecracker-microvm/firecracker-containerd/snapshotter/pkg/losetup"
-)
-
-const (
- testPoolName = "test-pool"
- testDeviceName = "test-device"
- deviceID = 1
- snapshotID = 2
-)
-
-func TestDMSetup(t *testing.T) {
- internal.RequiresRoot(t)
- tempDir, err := ioutil.TempDir("", "dmsetup-tests-")
- require.NoErrorf(t, err, "failed to make temp dir for tests")
-
- defer func() {
- err := os.RemoveAll(tempDir)
- assert.NoError(t, err)
- }()
-
- dataImage, loopDataDevice := createLoopbackDevice(t, tempDir)
- metaImage, loopMetaDevice := createLoopbackDevice(t, tempDir)
-
- defer func() {
- err = losetup.RemoveLoopDevicesAssociatedWithImage(dataImage)
- assert.NoErrorf(t, err, "failed to detach loop devices for data image: %s", dataImage)
-
- err = losetup.RemoveLoopDevicesAssociatedWithImage(metaImage)
- assert.NoErrorf(t, err, "failed to detach loop devices for meta image: %s", metaImage)
- }()
-
- t.Run("CreatePool", func(t *testing.T) {
- err := CreatePool(testPoolName, loopDataDevice, loopMetaDevice, 128)
- require.NoErrorf(t, err, "failed to create thin-pool")
-
- table, err := Table(testPoolName)
- t.Logf("table: %s", table)
- assert.NoError(t, err)
- assert.True(t, strings.HasPrefix(table, "0 32768 thin-pool"))
- assert.True(t, strings.HasSuffix(table, "128 32768 1 skip_block_zeroing"))
- })
-
- t.Run("ReloadPool", func(t *testing.T) {
- err := ReloadPool(testPoolName, loopDataDevice, loopMetaDevice, 256)
- assert.NoErrorf(t, err, "failed to reload thin-pool")
- })
-
- t.Run("CreateDevice", testCreateDevice)
-
- t.Run("CreateSnapshot", testCreateSnapshot)
- t.Run("DeleteSnapshot", testDeleteSnapshot)
-
- t.Run("ActivateDevice", testActivateDevice)
- t.Run("DeviceStatus", testDeviceStatus)
- t.Run("SuspendResumeDevice", testSuspendResumeDevice)
- t.Run("RemoveDevice", testRemoveDevice)
-
- t.Run("RemovePool", func(t *testing.T) {
- err = RemoveDevice(testPoolName, RemoveWithForce, RemoveWithRetries)
- require.NoErrorf(t, err, "failed to remove thin-pool")
- })
-
- t.Run("Version", testVersion)
-}
-
-func testCreateDevice(t *testing.T) {
- err := CreateDevice(testPoolName, deviceID)
- require.NoError(t, err, "failed to create test device")
-
- err = CreateDevice(testPoolName, deviceID)
- assert.EqualValues(t, unix.EEXIST, err)
-
- infos, err := Info(testPoolName)
- require.NoError(t, err)
- require.Lenf(t, infos, 1, "got unexpected number of device infos")
-}
-
-func testCreateSnapshot(t *testing.T) {
- err := CreateSnapshot(testPoolName, snapshotID, deviceID)
- require.NoError(t, err)
-}
-
-func testDeleteSnapshot(t *testing.T) {
- err := DeleteDevice(testPoolName, snapshotID)
- require.NoErrorf(t, err, "failed to send delete message")
-
- err = DeleteDevice(testPoolName, snapshotID)
- assert.EqualValues(t, unix.ENODATA, err)
-}
-
-func testActivateDevice(t *testing.T) {
- err := ActivateDevice(testPoolName, testDeviceName, 1, 1024, "")
- require.NoErrorf(t, err, "failed to activate device")
-
- err = ActivateDevice(testPoolName, testDeviceName, 1, 1024, "")
- assert.Equal(t, err, unix.EBUSY)
-
- if _, err := os.Stat("/dev/mapper/" + testDeviceName); err != nil && !os.IsExist(err) {
- assert.Errorf(t, err, "failed to stat device")
- }
-
- list, err := Info(testPoolName)
- assert.NoError(t, err)
- require.Len(t, list, 1)
-
- info := list[0]
- assert.Equal(t, testPoolName, info.Name)
- assert.True(t, info.TableLive)
-}
-
-func testDeviceStatus(t *testing.T) {
- status, err := Status(testDeviceName)
- require.NoError(t, err)
-
- assert.EqualValues(t, 0, status.Offset)
- assert.EqualValues(t, 2, status.Length)
- assert.Equal(t, "thin", status.Target)
- assert.EqualValues(t, status.Params, []string{"0", "-"})
-}
-
-func testSuspendResumeDevice(t *testing.T) {
- err := SuspendDevice(testDeviceName)
- assert.NoError(t, err)
-
- err = SuspendDevice(testDeviceName)
- assert.NoError(t, err)
-
- list, err := Info(testDeviceName)
- assert.NoError(t, err)
- require.Len(t, list, 1)
-
- info := list[0]
- assert.True(t, info.Suspended)
-
- err = ResumeDevice(testDeviceName)
- assert.NoError(t, err)
-
- err = ResumeDevice(testDeviceName)
- assert.NoError(t, err)
-}
-
-func testRemoveDevice(t *testing.T) {
- err := RemoveDevice(testPoolName)
- assert.EqualValues(t, unix.EBUSY, err, "removing thin-pool with dependencies shouldn't be allowed")
-
- err = RemoveDevice(testDeviceName, RemoveWithRetries)
- assert.NoErrorf(t, err, "failed to remove thin-device")
-}
-
-func testVersion(t *testing.T) {
- version, err := Version()
- assert.NoError(t, err)
- assert.NotEmpty(t, version)
-}
-
-func createLoopbackDevice(t *testing.T, dir string) (string, string) {
- file, err := ioutil.TempFile(dir, "dmsetup-tests-")
- require.NoError(t, err)
-
- size, err := units.RAMInBytes("16Mb")
- require.NoError(t, err)
-
- err = file.Truncate(size)
- require.NoError(t, err)
-
- err = file.Close()
- require.NoError(t, err)
-
- imagePath := file.Name()
-
- loopDevice, err := losetup.AttachLoopDevice(imagePath)
- require.NoError(t, err)
-
- return imagePath, loopDevice
-}
diff --git a/snapshotter/pkg/losetup/losetup.go b/snapshotter/pkg/losetup/losetup.go
deleted file mode 100644
index e77ac159e..000000000
--- a/snapshotter/pkg/losetup/losetup.go
+++ /dev/null
@@ -1,80 +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.
-
-package losetup
-
-import (
- "os/exec"
- "strings"
-
- "github.com/pkg/errors"
-)
-
-// FindAssociatedLoopDevices returns a list of loop devices attached to a given image
-func FindAssociatedLoopDevices(imagePath string) ([]string, error) {
- output, err := losetup("--list", "--output", "NAME", "--associated", imagePath)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to get loop devices: '%s'", output)
- }
-
- if output == "" {
- return []string{}, nil
- }
-
- items := strings.Split(output, "\n")
- if len(items) <= 1 {
- return []string{}, nil
- }
-
- // Skip header with column names
- return items[1:], nil
-}
-
-// AttachLoopDevice finds first available loop device and associates it with an image.
-func AttachLoopDevice(imagePath string) (string, error) {
- return losetup("--find", "--show", imagePath)
-}
-
-// DetachLoopDevice detaches loop devices
-func DetachLoopDevice(loopDevice ...string) error {
- args := append([]string{"--detach"}, loopDevice...)
- _, err := losetup(args...)
- return err
-}
-
-// RemoveLoopDevicesAssociatedWithImage detaches all loop devices attached to a given sparse image
-func RemoveLoopDevicesAssociatedWithImage(imagePath string) error {
- loopDevices, err := FindAssociatedLoopDevices(imagePath)
- if err != nil {
- return err
- }
-
- for _, loopDevice := range loopDevices {
- if err = DetachLoopDevice(loopDevice); err != nil {
- return err
- }
- }
-
- return nil
-}
-
-// losetup is a wrapper around losetup command line tool
-func losetup(args ...string) (string, error) {
- data, err := exec.Command("losetup", args...).CombinedOutput()
- output := string(data)
- if err != nil {
- return "", errors.Wrapf(err, "losetup %s\nerror: %s\n", strings.Join(args, " "), output)
- }
-
- return strings.TrimSuffix(output, "\n"), err
-}
diff --git a/snapshotter/pkg/losetup/losetup_test.go b/snapshotter/pkg/losetup/losetup_test.go
deleted file mode 100644
index 1f31ff8a4..000000000
--- a/snapshotter/pkg/losetup/losetup_test.go
+++ /dev/null
@@ -1,110 +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.
-
-package losetup
-
-import (
- "io/ioutil"
- "os"
- "testing"
-
- "github.com/docker/go-units"
- "github.com/firecracker-microvm/firecracker-containerd/internal"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestLosetup(t *testing.T) {
- internal.RequiresRoot(t)
- var (
- imagePath = createSparseImage(t)
- loopDevice1 string
- loopDevice2 string
- )
-
- defer func() {
- err := os.Remove(imagePath)
- assert.NoError(t, err)
- }()
-
- t.Run("AttachLoopDevice", func(t *testing.T) {
- dev1, err := AttachLoopDevice(imagePath)
- require.NoError(t, err)
- require.NotEmpty(t, dev1)
-
- dev2, err := AttachLoopDevice(imagePath)
- assert.NoError(t, err)
- assert.NotEqualf(t, dev2, dev1, "should attach different loop device")
-
- loopDevice1 = dev1
- loopDevice2 = dev2
- })
-
- t.Run("AttachEmptyLoopDevice", func(t *testing.T) {
- _, err := AttachLoopDevice("")
- assert.Error(t, err, "shouldn't attach empty path")
- })
-
- t.Run("FindAssociatedLoopDevices", func(t *testing.T) {
- devices, err := FindAssociatedLoopDevices(imagePath)
- assert.NoError(t, err)
- assert.Lenf(t, devices, 2, "unexpected number of attached devices")
- assert.ElementsMatch(t, devices, []string{loopDevice1, loopDevice2})
- })
-
- t.Run("FindAssociatedLoopDevicesForInvalidImage", func(t *testing.T) {
- devices, err := FindAssociatedLoopDevices("")
- assert.NoError(t, err)
- assert.Empty(t, devices)
- })
-
- t.Run("DetachLoopDevice", func(t *testing.T) {
- err := DetachLoopDevice(loopDevice2)
- require.NoErrorf(t, err, "failed to detach %q", loopDevice2)
- })
-
- t.Run("DetachEmptyDevice", func(t *testing.T) {
- err := DetachLoopDevice("")
- assert.Error(t, err, "shouldn't detach empty path")
- })
-
- t.Run("RemoveLoopDevicesAssociatedWithImage", func(t *testing.T) {
- err := RemoveLoopDevicesAssociatedWithImage(imagePath)
- assert.NoError(t, err)
-
- devices, err := FindAssociatedLoopDevices(imagePath)
- assert.NoError(t, err)
- assert.Empty(t, devices)
- })
-
- t.Run("RemoveLoopDevicesAssociatedWithInvalidImage", func(t *testing.T) {
- err := RemoveLoopDevicesAssociatedWithImage("")
- assert.NoError(t, err)
- })
-}
-
-func createSparseImage(t *testing.T) string {
- file, err := ioutil.TempFile("", "losetup-tests-")
- require.NoError(t, err)
-
- size, err := units.RAMInBytes("16Mb")
- require.NoError(t, err)
-
- err = file.Truncate(size)
- require.NoError(t, err)
-
- err = file.Close()
- require.NoError(t, err)
-
- return file.Name()
-}
diff --git a/snapshotter/run.go b/snapshotter/run.go
deleted file mode 100644
index 80bddf414..000000000
--- a/snapshotter/run.go
+++ /dev/null
@@ -1,124 +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.
-
-package snapshotter
-
-import (
- "context"
- "flag"
- "net"
- "os"
- "os/signal"
- "syscall"
-
- snapshotsapi "github.com/containerd/containerd/api/services/snapshots/v1"
- "github.com/containerd/containerd/contrib/snapshotservice"
- "github.com/containerd/containerd/log"
- "github.com/containerd/containerd/snapshots"
- "github.com/sirupsen/logrus"
- "golang.org/x/sync/errgroup"
- "google.golang.org/grpc"
-)
-
-var (
- unixAddr string
- debug bool
-)
-
-func init() {
- flag.StringVar(&unixAddr,
- "address",
- "./firecracker-snapshotter.sock",
- "RPC server unix address (default: ./firecracker-snapshotter.sock)")
-
- flag.BoolVar(&debug,
- "debug",
- false,
- "Debug mode")
-}
-
-// CreateFunc represents a callback to be used for creating concrete snapshotter implementation
-type CreateFunc func(ctx context.Context) (snapshots.Snapshotter, error)
-
-// Run runs snapshotter ttrpc server for containerd (somewhat similar to shim.Run).
-// snapInit should create concrete snapshotter implementation such as naive or devmapper.
-// There are two command line parameters available out of the box:
-// - address: specifies unix address to run ttrpc server on
-// - debug: turns on debug logging
-// Any extra flags might me specified if additional configuration needed, flags.Parse will
-// be called prior snapshot create callback (see naive example).
-func Run(snapInit CreateFunc) {
- if !flag.Parsed() {
- flag.Parse()
- }
-
- if debug {
- logrus.SetLevel(logrus.DebugLevel)
- }
-
- stop := make(chan os.Signal, 1)
- signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM, syscall.SIGPIPE, syscall.SIGHUP, syscall.SIGQUIT)
-
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
-
- group, ctx := errgroup.WithContext(ctx)
-
- rpc := grpc.NewServer()
-
- snap, err := snapInit(ctx)
- if err != nil {
- log.G(ctx).WithError(err).Fatal("failed to create snapshotter")
- }
-
- // Convert the snapshotter interface to gRPC service and run server
- log.G(ctx).WithField("unix_addr", unixAddr).Info("running gRPC server")
- service := snapshotservice.FromSnapshotter(snap)
- snapshotsapi.RegisterSnapshotsServer(rpc, service)
-
- listener, err := net.Listen("unix", unixAddr)
- if err != nil {
- log.G(ctx).WithError(err).Fatalf("failed to listen socket at %s", unixAddr)
- }
-
- group.Go(func() error {
- return rpc.Serve(listener)
- })
-
- group.Go(func() error {
- defer func() {
- log.G(ctx).Info("stopping server")
- rpc.Stop()
-
- if err := snap.Close(); err != nil {
- log.G(ctx).WithError(err).Error("failed to close snapshotter")
- }
- }()
-
- for {
- select {
- case <-stop:
- cancel()
- return nil
- case <-ctx.Done():
- return ctx.Err()
- }
- }
- })
-
- if err := group.Wait(); err != nil {
- log.G(ctx).WithError(err).Warn("snapshotter error")
- }
-
- log.G(ctx).Info("done")
-}
diff --git a/tools/docker/entrypoint.sh b/tools/docker/entrypoint.sh
index cb6409ec5..60b61184f 100755
--- a/tools/docker/entrypoint.sh
+++ b/tools/docker/entrypoint.sh
@@ -6,21 +6,6 @@ chmod a+rwx ${FICD_LOG_DIR}
mkdir -p /etc/containerd/snapshotter
case "$FICD_SNAPSHOTTER" in
- naive)
- cat > /etc/containerd/snapshotter/naive.toml <> ${FICD_SNAPSHOTTER_OUTFILE} &
- ;;
devmapper)
cat > /etc/containerd/snapshotter/devmapper.toml <