Skip to content

Commit 2f1cfcf

Browse files
authored
Merge pull request #25 from displague/feature/no-require-region
Address a few issues
2 parents 069d82b + b3a5378 commit 2f1cfcf

File tree

6 files changed

+96
-54
lines changed

6 files changed

+96
-54
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ CMD ["/go/bin/docker-volume-linode"]
1111

1212
FROM alpine
1313
COPY --from=builder /go/bin/docker-volume-linode .
14-
RUN apk update && apk add ca-certificates e2fsprogs
14+
RUN apk update && apk add ca-certificates e2fsprogs xfsprogs btrfs-progs util-linux
1515
CMD ["./docker-volume-linode"]

Makefile

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ DOCKER_PASSWORD ?= xxxxx
1010

1111
# Test Arguments
1212
TEST_TOKEN ?= xyz
13-
TEST_REGION ?= xyz
1413
TEST_LABEL ?= xyz
1514

1615
GOPATH=$(shell go env GOPATH)
@@ -82,16 +81,15 @@ test-use-volume:
8281
docker run --rm -i -v test-volume-default-size:/mnt busybox test -f /mnt/abc.txt || false
8382

8483
test-pre-check:
85-
@if [ "${TEST_TOKEN}" = "xyz" ] || [ "${TEST_REGION}" = "xyz" ] || [ "${TEST_LABEL}" = "xyz" ] ; then \
84+
@if [ "${TEST_TOKEN}" = "xyz" ] || [ "${TEST_LABEL}" = "xyz" ] ; then \
8685
echo -en "#############################\nYou must set TEST_* Variables\n#############################\n"; exit 1; fi
8786

8887
test-setup:
89-
@docker plugin set $(PLUGIN_NAME) LINODE_TOKEN=${TEST_TOKEN} LINODE_REGION=${TEST_REGION} LINODE_LABEL=${TEST_LABEL}
90-
docker plugin enable $(PLUGIN_NAME)
88+
@docker plugin set $(PLUGIN_NAME) LINODE_TOKEN=${TEST_TOKEN} LINODE_LABEL=${TEST_LABEL}
89+
docker plugin enable $(PLUGIN_NAME)
9190

9291
check:
9392
# Tools
94-
9593
GO111MODULE=off go get -u github.com/tsenart/deadcode
9694
GO111MODULE=off go get -u github.com/kisielk/errcheck
9795
GO111MODULE=off go get -u golang.org/x/lint/golint

README.md

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,14 @@ The plugin can also be configured (or reconfigured) in multiple steps.
2828
docker plugin install --alias linode linode/docker-volume-linode
2929
docker plugin disable linode
3030
docker plugin set linode linode-token=<linode token>
31-
docker plugin set linode linode-region=<linode region>
3231
docker plugin set linode linode-label=<linode label>
3332
docker plugin enable linode
3433
```
3534

3635
- \<linode token\>: You will need a Linode APIv4 Personal Access Token. Get one here: <https://developers.linode.com/api/v4#section/Personal-Access-Token>. The API Token must have Read/Write permission for Volumes and Linodes.
37-
- \<linode regions\>: `us-east`, `us-central`, `us-west`, `eu-west`, `eu-central`, `ap-south`, `ap-northeast`. [Some Linode regions do not have Block Storage Volume support](https://www.linode.com/community/questions/344/when-will-block-storage-be-available-in-my-datacenter), such as: `us-southeast` and `ap-northeast-1a`.
38-
- \<linode label\>: The label given to the host Linode Control Panel.
39-
- For a complete list of regions: https://api.linode.com/v4/regions
40-
- For all options see "Driver Options" section
36+
- \<linode label\>: The label given to the host Linode Control Panel. Defaults to the system hostname.
37+
[Some Linode regions do not have Block Storage Volume support](https://www.linode.com/community/questions/344/when-will-block-storage-be-available-in-my-datacenter), such as: `us-southeast` and `ap-northeast-1a`. For a complete list of regions: https://api.linode.com/v4/regions
38+
- For all options see [Driver Options](#Driver-Options) section
4139

4240
### Docker Swarm
4341

@@ -56,9 +54,17 @@ $ docker volume create -d linode my-test-volume
5654
my-test-volume
5755
```
5856

57+
If a named volume already exists on the Linode account and it is in the same region of the Linode, it will be reattached if possible. A Linode Volume can be attached to a single Linode at a time.
58+
5959
#### Create Options
6060

61-
This driver offers `size` as [driver specific option](https://docs.docker.com/engine/reference/commandline/volume_create/#driver-specific-options). The `size` option specifies the size (in GB) of the volume to be created. Volumes must be at least 10GB in size, so the default is 10GB.
61+
The driver offers [driver specific volume create options](https://docs.docker.com/engine/reference/commandline/volume_create/#driver-specific-options):
62+
63+
| Option | Type | Default | Description |
64+
| --- | --- | --- | --- |
65+
| `size` | int | `10` | the size (in GB) of the volume to be created. Volumes must be at least 10GB in size, so the default is 10GB.
66+
| `filesystem` | string | `ext4` | the filesystem argument for `mkfs` when formating the new (raw) volume (xfs, btrfs, ext4)
67+
| `delete-on-remove` | bool | `false`| if the Linode volume should be deleted when removed
6268

6369
```sh
6470
$ docker volume create -o size=50 -d linode my-test-volume-50
@@ -71,6 +77,13 @@ Volumes can also be created and attached from `docker run`:
7177
docker run -it --rm --mount volume-driver=linode,source=test-vol,destination=/test,volume-opt=size=25 alpine
7278
```
7379

80+
Multiple create options can be supplied:
81+
82+
```sh
83+
docker run -it --rm --mount volume-driver=linode,source=test-vol,destination=/test,volume-opt=size=25,volume-opt=filesystem=btrfs,volume-opt=delete-on-remove=true alpine
84+
```
85+
86+
7487
### List Volumes
7588

7689
```sh
@@ -103,7 +116,6 @@ my-test-volume-50
103116
| --- | --- |
104117
| linode-token | **Required** The Linode APIv4 [Personal Access Token](https://cloud.linode.com/profile/tokens)
105118
| linode-label | The Linode Label to attach block storage volumes to (defaults to the system hostname) |
106-
| linode-region | The Linode region to create volumes in (inferred if using linode-label, defaults to us-west) |
107119
| socket-file | Sets the socket file/address (defaults to /run/docker/plugins/linode.sock) |
108120
| socket-gid | Sets the socket GID (defaults to 0) |
109121
| mount-root | Sets the root directory for volume mounts (default /mnt) |
@@ -119,7 +131,7 @@ Options can be set once for all future uses with [`docker plugin set`](https://d
119131
### Run the driver
120132

121133
```sh
122-
docker-volume-linode --linode-token=<token from linode console> --linode-region=<linode region> --linode-label=<linode label>
134+
docker-volume-linode --linode-token=<token from linode console> --linode-label=<linode label>
123135
```
124136

125137
### Debugging
@@ -135,7 +147,7 @@ docker plugin set docker-volume-linode LOG_LEVEL=debug
135147
#### Enable Debug Level in manual installation
136148

137149
```sh
138-
docker-volume-linode --linode-token=<...> --linode-region=<...> --linode-label=<...> --log-level=debug
150+
docker-volume-linode --linode-token=<...> --linode-label=<...> --log-level=debug
139151
```
140152

141153
## Development

driver.go

Lines changed: 65 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net/http"
88
"os"
99
"strconv"
10+
"strings"
1011
"sync"
1112

1213
"github.com/docker/docker/api/types/filters"
@@ -29,15 +30,21 @@ type linodeVolumeDriver struct {
2930
linodeAPIPtr *linodego.Client
3031
}
3132

32-
// Constructor
33-
func newLinodeVolumeDriver(region string, linodeLabel string, linodeToken string) linodeVolumeDriver {
33+
const (
34+
fsTagPrefix = "docker-volume-filesystem-"
35+
)
3436

37+
// Constructor
38+
func newLinodeVolumeDriver(linodeLabel string, linodeToken string) linodeVolumeDriver {
3539
driver := linodeVolumeDriver{
3640
linodeToken: linodeToken,
37-
region: region,
3841
linodeLabel: linodeLabel,
3942
mutex: &sync.Mutex{},
4043
}
44+
if _, err := driver.linodeAPI(); err != nil {
45+
log.Fatalf("Could not initialize Linode API: %s", err)
46+
}
47+
4148
return driver
4249
}
4350

@@ -50,7 +57,20 @@ func (driver *linodeVolumeDriver) linodeAPI() (*linodego.Client, error) {
5057
return driver.linodeAPIPtr, nil
5158
}
5259

53-
tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: *linodeTokenParamPtr})
60+
driver.linodeAPIPtr = setupLinodeAPI(*linodeTokenParamPtr)
61+
62+
if driver.instanceID == 0 {
63+
if err := driver.determineLinodeID(); err != nil {
64+
driver.linodeAPIPtr = nil
65+
return nil, err
66+
}
67+
}
68+
69+
return driver.linodeAPIPtr, nil
70+
}
71+
72+
func setupLinodeAPI(token string) *linodego.Client {
73+
tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
5474
oauth2Client := &http.Client{
5575
Transport: &oauth2.Transport{
5676
Source: tokenSource,
@@ -60,14 +80,15 @@ func (driver *linodeVolumeDriver) linodeAPI() (*linodego.Client, error) {
6080
api := linodego.NewClient(oauth2Client)
6181
ua := fmt.Sprintf("docker-volume-linode/%s linodego/%s", VERSION, linodego.Version)
6282
api.SetUserAgent(ua)
83+
return &api
84+
}
6385

64-
driver.linodeAPIPtr = &api
65-
86+
func (driver *linodeVolumeDriver) determineLinodeID() error {
6687
if driver.linodeLabel == "" {
6788
var hostnameErr error
6889
driver.linodeLabel, hostnameErr = os.Hostname()
6990
if hostnameErr != nil {
70-
return nil, fmt.Errorf("Could not determine hostname: %s", hostnameErr)
91+
return fmt.Errorf("Could not determine hostname: %s", hostnameErr)
7192
}
7293
}
7394

@@ -76,17 +97,16 @@ func (driver *linodeVolumeDriver) linodeAPI() (*linodego.Client, error) {
7697
linodes, lErr := driver.linodeAPIPtr.ListInstances(context.Background(), listOpts)
7798

7899
if lErr != nil {
79-
return nil, fmt.Errorf("Could not determine Linode instance ID from Linode label %s due to error: %s", driver.linodeLabel, lErr)
100+
return fmt.Errorf("Could not determine Linode instance ID from Linode label %s due to error: %s", driver.linodeLabel, lErr)
80101
} else if len(linodes) != 1 {
81-
return nil, fmt.Errorf("Could not determine Linode instance ID from Linode label %s", driver.linodeLabel)
102+
return fmt.Errorf("Could not determine Linode instance ID from Linode label %s", driver.linodeLabel)
82103
}
83104

84105
driver.instanceID = linodes[0].ID
85106
if driver.region == "" {
86107
driver.region = linodes[0].Region
87108
}
88-
89-
return driver.linodeAPIPtr, nil
109+
return nil
90110
}
91111

92112
// Get implementation
@@ -159,6 +179,7 @@ func (driver *linodeVolumeDriver) Create(req *volume.CreateRequest) error {
159179
defer driver.mutex.Unlock()
160180

161181
var size int
182+
162183
if sizeOpt, ok := req.Options["size"]; ok {
163184
s, err := strconv.Atoi(sizeOpt)
164185
if err != nil {
@@ -173,6 +194,20 @@ func (driver *linodeVolumeDriver) Create(req *volume.CreateRequest) error {
173194
Size: size,
174195
}
175196

197+
if fsOpt, ok := req.Options["filesystem"]; ok {
198+
createOpts.Tags = append(createOpts.Tags, fsTagPrefix+fsOpt)
199+
}
200+
201+
if deleteOpt, ok := req.Options["delete-on-remove"]; ok {
202+
b, err := strconv.ParseBool(deleteOpt)
203+
if err != nil {
204+
return fmt.Errorf("Invalid delete-on-remove argument")
205+
}
206+
if b {
207+
createOpts.Tags = append(createOpts.Tags, "docker-volume-delete-on-remove")
208+
}
209+
}
210+
176211
if _, err := api.CreateVolume(context.Background(), createOpts); err != nil {
177212
return fmt.Errorf("Create(%s) Failed: %s", req.Name, err)
178213
}
@@ -203,10 +238,16 @@ func (driver *linodeVolumeDriver) Remove(req *volume.RemoveRequest) error {
203238
return err
204239
}
205240

206-
// Send Delete request
207-
if err := api.DeleteVolume(context.Background(), linVol.ID); err != nil {
208-
return err
241+
// Optionally send Delete request
242+
for _, t := range linVol.Tags {
243+
if t == "docker-volume-delete-on-remove" {
244+
if err := api.DeleteVolume(context.Background(), linVol.ID); err != nil {
245+
return err
246+
}
247+
break
248+
}
209249
}
250+
210251
return nil
211252
}
212253

@@ -262,14 +303,21 @@ func (driver *linodeVolumeDriver) Mount(req *volume.MountRequest) (*volume.Mount
262303
// else... linode already attached to current host
263304

264305
// wait for kernel to have block device available
265-
if err := waitForDeviceFileExists(linVol.FilesystemPath, 180); err != nil {
306+
if err := waitForDeviceFileExists(linVol.FilesystemPath, 300); err != nil {
266307
return nil, err
267308
}
268309

269310
// Format block device if no FS found
270311
if GetFSType(linVol.FilesystemPath) == "" {
271312
log.Infof("Formatting device:%s;", linVol.FilesystemPath)
272-
if err := Format(linVol.FilesystemPath); err != nil {
313+
filesystem := "ext4"
314+
for _, tag := range linVol.Tags {
315+
if strings.HasPrefix(tag, fsTagPrefix) {
316+
filesystem = tag[len(fsTagPrefix):]
317+
break
318+
}
319+
}
320+
if err := Format(linVol.FilesystemPath, filesystem); err != nil {
273321
return nil, err
274322
}
275323
}
@@ -376,7 +424,7 @@ func attachAndWait(api *linodego.Client, volumeID int, linodeID int) error {
376424
return fmt.Errorf("Error attaching volume(%d) to linode(%d): %s", volumeID, linodeID, err)
377425
}
378426

379-
if _, err := api.WaitForVolumeLinodeID(context.Background(), volumeID, &linodeID, 180); err != nil {
427+
if _, err := api.WaitForVolumeLinodeID(context.Background(), volumeID, &linodeID, 300); err != nil {
380428
return fmt.Errorf("Error waiting for attachment of volume(%d) to linode(%d): %s", volumeID, linodeID, err)
381429
}
382430
return nil

fs_utils_linux.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,9 @@ import (
88
log "github.com/sirupsen/logrus"
99
)
1010

11-
const (
12-
formatFSType = "ext4"
13-
)
14-
1511
// Format calls mke2fs on path
16-
func Format(path string) error {
17-
cmd := exec.Command("mke2fs", "-t", formatFSType, path)
12+
func Format(path string, formatFSType string) error {
13+
cmd := exec.Command("mkfs", "-t", formatFSType, path)
1814
stdOutAndErr, err := cmd.CombinedOutput()
1915
log.Debugf("Mke2fs Output:\n%s", stdOutAndErr)
2016
return err

main.go

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,13 @@ var (
2828
socketAddressParamPtr = cfgString("socket-file", DefaultSocketAddress, "Sets the socket file/address.")
2929
mountRootParamPtr = cfgString("mount-root", MountRoot, "Sets the root directory for volume mounts.")
3030
linodeTokenParamPtr = cfgString("linode-token", "", "Required Personal Access Token generated in Linode Console.")
31-
linodeRegionParamPtr = cfgString("linode-region", "", "Required linode region.")
32-
linodeLabelParamPtr = cfgString("linode-label", "", "Sets the Linode instance label.")
33-
logLevelPtr = cfgString("log-level", "info", "Sets log level debug,info,warn,error")
31+
linodeLabelParamPtr = cfgString("linode-label", "", "Sets the Linode Instance Label (defaults to the OS HOSTNAME)")
32+
logLevelPtr = cfgString("log-level", "info", "Sets log level: debug,info,warn,error")
3433
)
3534

3635
func main() {
3736
//
3837
flag.Parse()
39-
4038
//
4139
log.SetOutput(os.Stdout)
4240
level, err := log.ParseLevel(*logLevelPtr)
@@ -49,26 +47,16 @@ func main() {
4947

5048
// check required parameters (token, region and label)
5149
if *linodeTokenParamPtr == "" {
52-
log.Error("linode-token is required.")
53-
}
54-
55-
if *linodeRegionParamPtr == "" {
56-
log.Error("linode-region is required.")
57-
}
58-
59-
if *linodeLabelParamPtr == "" {
60-
log.Error("linode-label is required.")
50+
log.Fatal("linode-token is required.")
6151
}
6252

6353
MountRoot = *mountRootParamPtr
6454

65-
//
6655
log.Debugf("linode-token: %s", *linodeTokenParamPtr)
67-
log.Debugf("linode-region: %s", *linodeRegionParamPtr)
6856
log.Debugf("linode-label: %s", *linodeLabelParamPtr)
6957

7058
// Driver instance
71-
driver := newLinodeVolumeDriver(*linodeRegionParamPtr, *linodeLabelParamPtr, *linodeTokenParamPtr)
59+
driver := newLinodeVolumeDriver(*linodeLabelParamPtr, *linodeTokenParamPtr)
7260

7361
// Attach Driver to docker
7462
handler := volume.NewHandler(&driver)

0 commit comments

Comments
 (0)