Skip to content

Commit

Permalink
csi: document how to use existing volumes
Browse files Browse the repository at this point in the history
* Add Readme with examples on how to use existing volumes
* Add an option to prevent formatting an existing volume (needs more
testing)

KNOWN ISSUES:

* Deleting a `PVC` will delete the volume if the reclaim policy is set
to `Delete`, however the `PV` will be not deleted. This seems to be a
bug in K8S
* Using an existing, attached volume might cause confusion if the POD is
scheduled to a different node. It's advised that user only use existing
**detached** volumes
  • Loading branch information
fatih committed Oct 8, 2018
1 parent b2b4e71 commit 571c5c8
Show file tree
Hide file tree
Showing 8 changed files with 245 additions and 11 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
## unreleased

* Add a [tutorial](examples/kubernetes/pod-single-existing-volume/README.md) on how to re-use an existing volume. Also a new option is introduced to prevent formatting an existing volume.
[[GH-87]](https://github.com/digitalocean/csi-digitalocean/pull/87)
* Handle case if a volume is already attached to a droplet
[[GH-87]](https://github.com/digitalocean/csi-digitalocean/pull/87)

## v0.2.0 - 2018.09.05

* Add support to CSI Spec `v0.3.0`. This includes many new changes, make sure
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ $ make test
If you want to test your changes, create a new image with the version set to `dev`:

```
$ VERSION=dev make publish-dev
$ VERSION=dev make publish
```

This will create a binary with version `dev` and docker image pushed to
Expand Down
22 changes: 21 additions & 1 deletion driver/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,10 @@ func (d *Driver) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest)
if err != nil {
if resp != nil && resp.StatusCode == http.StatusNotFound {
// we assume it's deleted already for idempotency
ll.WithFields(logrus.Fields{
"error": err,
"resp": resp,
}).Warn("assuming volume is deleted already")
return &csi.DeleteVolumeResponse{}, nil
}
return nil, err
Expand Down Expand Up @@ -227,7 +231,7 @@ func (d *Driver) ControllerPublishVolume(ctx context.Context, req *csi.Controlle
ll.Info("controller publish volume called")

// check if volume exist before trying to attach it
_, resp, err := d.doClient.Storage.GetVolume(ctx, req.VolumeId)
vol, resp, err := d.doClient.Storage.GetVolume(ctx, req.VolumeId)
if err != nil {
if resp != nil && resp.StatusCode == http.StatusNotFound {
return nil, status.Errorf(codes.NotFound, "volume %q not found", req.VolumeId)
Expand All @@ -244,6 +248,22 @@ func (d *Driver) ControllerPublishVolume(ctx context.Context, req *csi.Controlle
return nil, err
}

attachedID := 0
for _, id := range vol.DropletIDs {
attachedID = id
if id == dropletID {
ll.Info("volume is already attached")
return &csi.ControllerPublishVolumeResponse{}, nil
}
}

// droplet is attached to a different node, return an error
if attachedID != 0 {
return nil, status.Errorf(codes.FailedPrecondition,
"volume is attached to a wrong droplet(%q), dettach the volume to fix it", attachedID)
}

// attach the volume to the correct node
action, resp, err := d.doClient.StorageActions.Attach(ctx, req.VolumeId, dropletID)
if err != nil {
// don't do anything if attached
Expand Down
30 changes: 21 additions & 9 deletions driver/node.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

147 changes: 147 additions & 0 deletions examples/kubernetes/pod-single-existing-volume/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Use an existing volume

Below you will find the instruction on how to use an existing DigitalOcean Block
Storage with your Kubernetes cluster.

## Known issues

* Deleting a `PVC` will delete the volume if the reclaim policy is set
to `Delete`, however the bound `PV` will be not deleted. This seems to be a
bug in K8S.
* Using an existing, attached volume might cause confusion if the `POD` is
scheduled to a different node. It's advised to use only existing **detached**
volumes. If you have an attached volume, please make sure to detach it, so
Kubernetes attaches it to the correct droplet. The CSI plugin will return an
error if the volume is attached to a wrong droplet.

## Example

To use an existing volume, we have to create manually a `PersistentVolume` (PV)
resource. Here is an example `PersistenVolume` resource for an existing volume:

```
kind: PersistentVolume
apiVersion: v1
metadata:
name: volume-nyc1-01
annotations:
# fake it by indicating this is provisioned dynamically, so the system
# works properly
pv.kubernetes.io/provisioned-by: com.digitalocean.csi.dobs
spec:
storageClassName: do-block-storage
# by default, the volume will be not deleted if you delete the PVC, change to
# "Delete" if you wish the volume to be deleted automatically with the PVC
persistentVolumeReclaimPolicy: Delete
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
csi:
driver: com.digitalocean.csi.dobs
fsType: ext4
volumeHandle: 1952d58a-c714-11e8-bc0c-0a58ac14421e
volumeAttributes:
com.digitalocean.csi/format: "true"
```

Couple of things to note,

* `volumeHandle` is the volume ID you want to reuse. Make sure it matches exactly the volume you're targeting. You can list the ID's of your volumes via doctl: `doctl compute volume list`
* `volumeAttributes` has a special, csi-digitalocean specific annotation called `com.digitalocean.csi/format`. If you add this key, the CSI plugin make sure to **not format** the volume. If you don't add this, it'll be formatted.
* `storage` make sure it's set to the same storage size as your existing DigitalOcean Block Storage volume.

Create a file with this content, naming it `pv.yaml` and deploy it:

```
kubectl create -f pv.yaml
```

View information about the `PersistentVolume`:

```
$ kubectl get pv volume-nyc1-01
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
volume-nyc1-01 5Gi RWO Delete Available do-block-storage 15s
```

The status is `Available`. This means it has not yet been bound to a
PersistentVolumeClaim. Now we can proceed to create our PVC:


```
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: csi-pod-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: do-block-storage
```

This is the same (just like our other examples). When you create `PVC`,
Kubernetes will try to match it to an existing `PV`. Because the
storageClassName and the storage size matches our `PV` descriptions, kubernetes
will bind this `PVC` to our manually create `PV`. CSI **will not** create a new
volume because of the existing `PV`.

Create the PersistentVolumeClaim:

```
kubectl create -f pvc.yaml
```

Now look at the PersistentVolumeClaim (PVC):

```
kubectl get pvc task-pv-claim
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
csi-pod-pvc Bound volume-nyc1-01 5Gi RWO do-block-storage 5s
```

As you see, the output shows that the PVC is bound to our PersistentVolume, `volume-nyc1-01`.

Finally, define your pod that refers to this PVC:

```
kind: Pod
apiVersion: v1
metadata:
name: my-csi-app
spec:
containers:
- name: my-frontend
image: busybox
volumeMounts:
- mountPath: "/data"
name: my-do-volume
command: [ "sleep", "1000000" ]
volumes:
- name: my-do-volume
persistentVolumeClaim:
claimName: csi-pod-pvc
```


Check if the pod is running successfully:


```
$ kubectl describe pods/my-csi-app
```

Write inside the app container:

```
$ kubectl exec -ti my-csi-app /bin/sh
/ # touch /data/hello-world
/ # exit
$ kubectl exec -ti my-csi-app /bin/sh
/ # ls /data
hello-world
```
16 changes: 16 additions & 0 deletions examples/kubernetes/pod-single-existing-volume/pod.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
kind: Pod
apiVersion: v1
metadata:
name: my-csi-app
spec:
containers:
- name: my-frontend
image: busybox
volumeMounts:
- mountPath: "/data"
name: my-do-volume
command: [ "sleep", "1000000" ]
volumes:
- name: my-do-volume
persistentVolumeClaim:
claimName: csi-pod-pvc
23 changes: 23 additions & 0 deletions examples/kubernetes/pod-single-existing-volume/pv.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
kind: PersistentVolume
apiVersion: v1
metadata:
name: volume-nyc1-01
annotations:
# fake it by indicating this is provisioned dynamically, so the system
# works properly
pv.kubernetes.io/provisioned-by: com.digitalocean.csi.dobs
spec:
storageClassName: do-block-storage
# by default, the volume will be not deleted if you delete the PVC, change to
# "Delete" if you wish the volume to be deleted automatically with the PVC
persistentVolumeReclaimPolicy: Delete
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
csi:
driver: com.digitalocean.csi.dobs
fsType: ext4
volumeHandle: 1952d58a-c714-11e8-bc0c-0a58ac14421e
volumeAttributes:
com.digitalocean.csi/noformat: "true"
11 changes: 11 additions & 0 deletions examples/kubernetes/pod-single-existing-volume/pvc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: csi-pod-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: do-block-storage

0 comments on commit 571c5c8

Please sign in to comment.