diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..cb366cb --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +* +!/plugin/ +!/etc/docker/docker-lvm-plugin +!/docker-lvm-plugin diff --git a/.gitignore b/.gitignore index fee13eb..9018fba 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,5 @@ _testmain.go *.prof .vagrant/ +plugin/rootfs docker-lvm-plugin diff --git a/README.md b/README.md index 6b0b573..cc59f55 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,10 @@ This plugin can be used to create lvm volumes of specified size, which can then be bind mounted into the container using `docker run` command. ## Setup +### Using Docker + docker plugin install --alias lvm containers/docker-lvm-plugin/docker-lvm-plugin VOLUME_GROUP=vg0 +### Manual 1) git clone git@github.com:projectatomic/docker-lvm-plugin.git (You can also use HTTPS to clone: git clone https://github.com/projectatomic/docker-lvm-plugin.git) 2) cd docker-lvm-plugin 3) export GO111MODULE=on diff --git a/driver.go b/driver.go index 07f0bdf..870e2ba 100644 --- a/driver.go +++ b/driver.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "log/syslog" + "log" "os" "os/exec" "sync" @@ -19,7 +19,7 @@ type lvmDriver struct { volumes map[string]*vol count map[string]int mu sync.RWMutex - logger *syslog.Writer + logger *log.Logger } type vol struct { @@ -32,10 +32,10 @@ type vol struct { } func newDriver(home, vgConfig string) (*lvmDriver, error) { - logger, err := syslog.New(syslog.LOG_ERR, "docker-lvm-plugin") - if err != nil { - return nil, err - } + logger := log.New(os.Stdout, "docker-lvm-plugin", 1) + //if err != nil { + // return nil, err + //} return &lvmDriver{ home: home, @@ -82,7 +82,7 @@ func (l *lvmDriver) Create(req *volume.CreateRequest) error { return fmt.Errorf("Please don't specify --opt keyfile= for snapshots") } if isThinSnap, _, err = isThinlyProvisioned(vgName, snap); err != nil { - l.logger.Err(fmt.Sprintf("Create: lvdisplayGrep error: %v", err)) + l.logger.Print(fmt.Sprintf("Create: lvdisplayGrep error: %v", err)) return fmt.Errorf("Error creating volume") } } @@ -123,7 +123,7 @@ func (l *lvmDriver) Create(req *volume.CreateRequest) error { } cmd := exec.Command("lvcreate", cmdArgs...) if out, err := cmd.CombinedOutput(); err != nil { - l.logger.Err(fmt.Sprintf("Create: lvcreate error: %s output %s", err, string(out))) + l.logger.Print(fmt.Sprintf("Create: lvcreate error: %s output %s", err, string(out))) return fmt.Errorf("Error creating volume") } @@ -139,19 +139,19 @@ func (l *lvmDriver) Create(req *volume.CreateRequest) error { if hasKeyFile { cmd = exec.Command("cryptsetup", "-q", "-d", keyFile, "luksFormat", device) if out, err := cmd.CombinedOutput(); err != nil { - l.logger.Err(fmt.Sprintf("Create: cryptsetup error: %s output %s", err, string(out))) + l.logger.Print(fmt.Sprintf("Create: cryptsetup error: %s output %s", err, string(out))) return fmt.Errorf("Error encrypting volume") } if out, err := luksOpen(vgName, req.Name, keyFile); err != nil { - l.logger.Err(fmt.Sprintf("Create: cryptsetup error: %s output %s", err, string(out))) + l.logger.Print(fmt.Sprintf("Create: cryptsetup error: %s output %s", err, string(out))) return fmt.Errorf("Error opening encrypted volume") } defer func() { if err != nil { if out, err := luksClose(req.Name); err != nil { - l.logger.Err(fmt.Sprintf("Create: cryptsetup error: %s output %s", err, string(out))) + l.logger.Print(fmt.Sprintf("Create: cryptsetup error: %s output %s", err, string(out))) } } }() @@ -161,13 +161,13 @@ func (l *lvmDriver) Create(req *volume.CreateRequest) error { cmd = exec.Command("mkfs.xfs", device) if out, err := cmd.CombinedOutput(); err != nil { - l.logger.Err(fmt.Sprintf("Create: mkfs.xfs error: %s output %s", err, string(out))) + l.logger.Print(fmt.Sprintf("Create: mkfs.xfs error: %s output %s", err, string(out))) return fmt.Errorf("Error partitioning volume") } if hasKeyFile { if out, err := luksClose(req.Name); err != nil { - l.logger.Err(fmt.Sprintf("Create: cryptsetup error: %s output %s", err, string(out))) + l.logger.Print(fmt.Sprintf("Create: cryptsetup error: %s output %s", err, string(out))) return fmt.Errorf("Error closing encrypted volume") } } @@ -237,7 +237,7 @@ func (l *lvmDriver) Get(req *volume.GetRequest) (*volume.GetResponse, error) { createdAt, err := getVolumeCreationDateTime(v.VgName, v.Name) if err != nil { - l.logger.Err(fmt.Sprintf("Get: %v", err)) + l.logger.Print(fmt.Sprintf("Get: %v", err)) return nil, err } @@ -287,7 +287,7 @@ func (l *lvmDriver) Remove(req *volume.RemoveRequest) error { } if out, err := removeLogicalVolume(req.Name, vol.VgName); err != nil { - l.logger.Err(fmt.Sprintf("Remove: removeLogicalVolume error %s output %s", err, string(out))) + l.logger.Print(fmt.Sprintf("Remove: removeLogicalVolume error %s output %s", err, string(out))) return fmt.Errorf("error removing volume") } @@ -334,21 +334,21 @@ func (l *lvmDriver) Mount(req *volume.MountRequest) (*volume.MountResponse, erro if keyFile != "" { if err := keyFileExists(keyFile); err != nil { - l.logger.Err(fmt.Sprintf("Mount: %s", err)) + l.logger.Print(fmt.Sprintf("Mount: %s", err)) return &volume.MountResponse{}, err } if err := cryptsetupInstalled(); err != nil { - l.logger.Err(fmt.Sprintf("Mount: %s", err)) + l.logger.Print(fmt.Sprintf("Mount: %s", err)) return &volume.MountResponse{}, err } if out, err := luksOpen(vol.VgName, req.Name, keyFile); err != nil { - l.logger.Err(fmt.Sprintf("Mount: cryptsetup error: %s output %s", err, string(out))) + l.logger.Print(fmt.Sprintf("Mount: cryptsetup error: %s output %s", err, string(out))) return &volume.MountResponse{}, fmt.Errorf("Error opening encrypted volume") } defer func() { if err != nil { if out, err := luksClose(req.Name); err != nil { - l.logger.Err(fmt.Sprintf("Mount: cryptsetup error: %s output %s", err, string(out))) + l.logger.Print(fmt.Sprintf("Mount: cryptsetup error: %s output %s", err, string(out))) } } }() @@ -361,7 +361,7 @@ func (l *lvmDriver) Mount(req *volume.MountRequest) (*volume.MountResponse, erro } cmd := exec.Command("mount", mountArgs...) if out, err := cmd.CombinedOutput(); err != nil { - l.logger.Err(fmt.Sprintf("Mount: mount error: %s output %s", err, string(out))) + l.logger.Print(fmt.Sprintf("Mount: mount error: %s output %s", err, string(out))) return &volume.MountResponse{}, fmt.Errorf("Error mouting volume") } } @@ -379,22 +379,22 @@ func (l *lvmDriver) Unmount(req *volume.UnmountRequest) error { mp := getMountpoint(l.home, req.Name) isVolMounted, err := mount.Mounted(mp) if err != nil { - l.logger.Err(fmt.Sprintf("Unmount: %s", err)) + l.logger.Print(fmt.Sprintf("Unmount: %s", err)) return fmt.Errorf("error unmounting volume") } if isVolMounted { cmd := exec.Command("umount", mp) if out, err := cmd.CombinedOutput(); err != nil { - l.logger.Err(fmt.Sprintf("Unmount: unmount error: %s output %s", err, string(out))) + l.logger.Print(fmt.Sprintf("Unmount: unmount error: %s output %s", err, string(out))) return fmt.Errorf("error unmounting volume") } if v, ok := l.volumes[req.Name]; ok && v.KeyFile != "" { if err := cryptsetupInstalled(); err != nil { - l.logger.Err(fmt.Sprintf("Unmount: %s", err)) + l.logger.Print(fmt.Sprintf("Unmount: %s", err)) return err } if out, err := luksClose(req.Name); err != nil { - l.logger.Err(fmt.Sprintf("Unmount: cryptsetup error: %s output %s", err, string(out))) + l.logger.Print(fmt.Sprintf("Unmount: cryptsetup error: %s output %s", err, string(out))) return fmt.Errorf("Error closing encrypted volume") } } diff --git a/etc/docker/docker-lvm-plugin b/etc/docker/docker-lvm-plugin index 60fd957..0ca2225 100644 --- a/etc/docker/docker-lvm-plugin +++ b/etc/docker/docker-lvm-plugin @@ -4,4 +4,4 @@ # OR create a new volume group using `vgcreate` command. # e.g. vgcreate volume_group_one /dev/hda where /dev/hda is your partition or # whole disk on which physical volumes were created. -VOLUME_GROUP= +VOLUME_GROUP=foo diff --git a/plugin/Dockerfile b/plugin/Dockerfile new file mode 100644 index 0000000..f6067a4 --- /dev/null +++ b/plugin/Dockerfile @@ -0,0 +1,5 @@ +FROM alpine +RUN apk update && apk add lvm2 xfsprogs cryptsetup thin-provisioning-tools +RUN mkdir -p /var/lib/docker-lvm-plugin +COPY etc/docker/docker-lvm-plugin /etc/docker/docker-lvm-plugin +COPY docker-lvm-plugin / diff --git a/plugin/Makefile b/plugin/Makefile new file mode 100644 index 0000000..2287942 --- /dev/null +++ b/plugin/Makefile @@ -0,0 +1,38 @@ +name := containers/docker-lvm-plugin + +.PHONY: build +build: docker-lvm-plugin + +docker-lvm-plugin: main.go driver.go utils.go go.mod go.sum + docker run --rm --tmpfs /tmp -v $(shell pwd):/tmp/docker-lvm-plugin -w /tmp/docker-lvm-plugin golang:alpine go build + +.PHONY: create +create: plugin/config.json plugin/rootfs + docker plugin rm --force $(name) || true + docker plugin create $(name) plugin + +.PHONY: install +install: create + docker plugin rm --force $(name) || true + docker plugin install $(name) --alias lvm --grant-all-permissions + +.PHONY: push +push: create + docker plugin push $(name) + +.PHONY: enable +enable: create + docker plugin enable $(name) + +.PHONY: clean +clean: + rm -rf plugin/rootfs + +plugin/rootfs: .dockerignore plugin/Dockerfile docker-lvm-plugin + docker build --tag $(name):rootfs -f plugin/Dockerfile . + docker rm --force --volumes rootfs || true + docker create --name rootfs $(name):rootfs + rm -rf $@ + mkdir -p $@ + docker export rootfs | tar -x -C $@ + docker rm --force --volumes rootfs diff --git a/plugin/config.json b/plugin/config.json new file mode 100644 index 0000000..6b78c5f --- /dev/null +++ b/plugin/config.json @@ -0,0 +1,36 @@ +{ + "Description": "LVM volume plugin for Docker", + "Documentation": "https://docs.docker.com/engine/extend/plugins/", + "Entrypoint": ["/docker-lvm-plugin"], + "Env": [ + { + "Name": "VOLUME_GROUP", + "Description": "LVM volume group to create volumes in.", + "Settable": ["value"], + "Value": null + } + ], + "Interface": { + "Socket": "lvm.sock", + "Types": ["docker.volumedriver/1.0"] + }, + "Linux": { + "Capabilities": ["CAP_SYS_ADMIN"], + "AllowAllDevices": true, + "Devices": null + }, + "Mounts": [ + { + "description": "Device access for devicemapper (/dev/mapper/*, /dev/*/*)", + "destination": "/dev", + "options": ["rbind"], + "name": "dev", + "source": "/dev", + "type": "bind" + } + ], + "Network": { + "Type": "host" + }, + "PropagatedMount": "/var/lib/docker-lvm-plugin" +}