Skip to content

Commit

Permalink
Enhance helper Pod interface and configuration.
Browse files Browse the repository at this point in the history
* Make the helper Pod receive only environment variables instead of args (this changes the interface in a non-backward compatible way but is simpler to use and potentially provides more backward compatibility in the future).
* Adds the manager options `--pvc-annotation[-required]` to pass through annotations from the PVC to the PV and to the helper Pod.
* Merge the helper Pod's `data` VolumeMount with the one provided with the template to be able to specify `mountPropagation` within the template.
* Rename `helperPod.yaml` to `helper-pod.yaml` (more convenient and if we break sth we can break this as well).
* Expose `--helper-pod-timeout` option.
* Provide a basic usage example of the new features (`examples/cache`).
* Support forceful termination of the manager binary (2xCtrl+c - since this is annoying during development otherwise).

Closes #164
Closes #165

Signed-off-by: Max Goltzsche <[email protected]>
  • Loading branch information
mgoltzsche committed Dec 30, 2020
1 parent d253f2b commit 0dbf8c9
Show file tree
Hide file tree
Showing 25 changed files with 663 additions and 408 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
./.dapper
./.cache
./dist
./examples/cache/testmount
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
*.swp
.idea
.vscode/
local-path-provisioner
/examples/cache/testmount
58 changes: 18 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,41 +132,11 @@ data:
}
setup: |-
#!/bin/sh
while getopts "m:s:p:" opt
do
case $opt in
p)
absolutePath=$OPTARG
;;
s)
sizeInBytes=$OPTARG
;;
m)
volMode=$OPTARG
;;
esac
done
mkdir -m 0777 -p ${absolutePath}
mkdir -m 0777 -p "$VOL_DIR"
teardown: |-
#!/bin/sh
while getopts "m:s:p:" opt
do
case $opt in
p)
absolutePath=$OPTARG
;;
s)
sizeInBytes=$OPTARG
;;
m)
volMode=$OPTARG
;;
esac
done
rm -rf ${absolutePath}
helperPod.yaml: |-
rm -rf "$VOL_DIR"
helper-pod.yaml: |-
apiVersion: v1
kind: Pod
metadata:
Expand Down Expand Up @@ -195,16 +165,24 @@ The configuration must obey following rules:
3. No duplicate paths allowed for one node.
4. No duplicate node allowed.

#### Scripts `setup` and `teardown` and `helperPod.yaml`
#### Scripts `setup` and `teardown` and `helper-pod.yaml` template

The script `setup` will be executed before the volume is created, to prepare the directory on the node for the volume.
* The `setup` script is run before the volume is created, to prepare the volume directory on the node.
* The `teardown` script is run after the volume is deleted, to cleanup the volume directory on the node.
* The `helper-pod.yaml` template is used to create a helper Pod that runs the `setup` or `teardown` script.

The script `teardown` will be executed after the volume is deleted, to cleanup the directory on the node for the volume.
The helper Pod receives its input as environment variables:

The yaml file `helperPod.yaml` will be created by local-path-storage to execute `setup` or `teardown` script with three paramemters `-p <path> -s <size> -m <mode>` :
* path: the absolute path provisioned on the node
- size: pvc.Spec.resources.requests.storage in bytes
* mode: pvc.Spec.VolumeMode
| Environment variable | Description |
| -------------------- | ----------- |
| `VOL_DIR` | Volume directory that should be created or removed. |
| `VOL_NAME` | Name of the PersistentVolume. |
| `VOL_TYPE` | Type of the PersistentVolume (`Block` or `Filesystem`). |
| `VOL_SIZE_BYTES` | Requested volume size in bytes. |
| `PVC_NAME` | Name of the PersistentVolumeClaim. |
| `PVC_NAMESPACE` | Namespace of the PersistentVolumeClaim. |
| `PVC_ANNOTATION` | Value of the PersistentVolumeClaim annotation specified by the manager's `--pvc-annotation` option. |
| `PVC_ANNOTATION_{SUFFIX}` | Value of the PersistentVolumeClaim annotation with the prefix specified by the manager's `--pvc-annotation` option. The `SUFFIX` is the normalized part of the annotation name after the `/`. E.g. if the managed is run with `--pvc-annotation=storage.example.org` the PVC annotation `storage.example.org/cache-name` is passed through to the Pod as env var `PVC_ANNOTATION_CACHE_NAME`. |

#### Reloading

Expand Down
38 changes: 3 additions & 35 deletions deploy/example-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,41 +23,11 @@ data:
}
setup: |-
#!/bin/sh
while getopts "m:s:p:" opt
do
case $opt in
p)
absolutePath=$OPTARG
;;
s)
sizeInBytes=$OPTARG
;;
m)
volMode=$OPTARG
;;
esac
done
mkdir -m 0777 -p ${absolutePath}
mkdir -m 0777 -p "$VOL_DIR"
teardown: |-
#!/bin/sh
while getopts "m:s:p:" opt
do
case $opt in
p)
absolutePath=$OPTARG
;;
s)
sizeInBytes=$OPTARG
;;
m)
volMode=$OPTARG
;;
esac
done
rm -rf ${absolutePath}
helperPod.yaml: |-
rm -rf "$VOL_DIR"
helper-pod.yaml: |-
apiVersion: v1
kind: Pod
metadata:
Expand All @@ -66,5 +36,3 @@ data:
containers:
- name: helper-pod
image: busybox
36 changes: 3 additions & 33 deletions deploy/local-path-storage.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -104,41 +104,11 @@ data:
}
setup: |-
#!/bin/sh
while getopts "m:s:p:" opt
do
case $opt in
p)
absolutePath=$OPTARG
;;
s)
sizeInBytes=$OPTARG
;;
m)
volMode=$OPTARG
;;
esac
done
mkdir -m 0777 -p ${absolutePath}
mkdir -m 0777 -p "$VOL_DIR"
teardown: |-
#!/bin/sh
while getopts "m:s:p:" opt
do
case $opt in
p)
absolutePath=$OPTARG
;;
s)
sizeInBytes=$OPTARG
;;
m)
volMode=$OPTARG
;;
esac
done
rm -rf ${absolutePath}
helperPod.yaml: |-
rm -rf "$VOL_DIR"
helper-pod.yaml: |-
apiVersion: v1
kind: Pod
metadata:
Expand Down
55 changes: 55 additions & 0 deletions examples/cache/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Example cache provisioner

This example uses a [buildah](https://github.com/containers/buildah)-based helper Pod to provision an overlayfs based on a container image as PersistentVolume and commit it when deprovisioning.
Users can select the desired cache using a PersistentVolumeClaim annotation that is passed through to the helper Pod as environment variable.

While it is not part of this example caches could also be synchronized across nodes using an image registry.
The [cache-provisioner](https://github.com/mgoltzsche/cache-provisioner) project aims to achieve this as well as other cache management features.

## Test

### Test the helper Pod separately

The helper Pod can be tested separately using docker locally:
```sh
./helper-test.sh
```

### Test the integration

_Please note that this test requires `/data/example-cache-storage` on the Kubernetes host to be an overlay mount._
_For instance this works well when running `minikube start` initially._

Install the example kustomization:
```sh
kustomize build . | kubectl apply -f -
```

If you want to test changes to the `local-path-provisioner` binary locally:
```sh
kubectl delete -n example-cache-storage deploy local-path-provisioner
(
cd ../..
go build .
./local-path-provisioner --debug start \
--namespace=example-cache-storage \
--configmap-name=local-path-config \
--service-account-name=local-path-provisioner-service-account \
--provisioner-name=storage.example.org/cache \
--pvc-annotation=storage.example.org \
--pvc-annotation-required=storage.example.org/cache-name
)
```

Within another terminal create an example Pod and PVC that pulls and runs a container image using [podman](https://github.com/containers/podman):
```sh
kubectl apply -f test-pod.yaml
kubectl logs -f cached-build
```

If the Pod and PVC are removed and recreated you can observe that, during the 2nd Pod execution on the same node, the image for the nested container doesn't need to be pulled again since it is cached:
```sh
kubectl delete -f test-pod.yaml
kubectl apply -f test-pod.yaml
kubectl logs -f cached-build
```
16 changes: 16 additions & 0 deletions examples/cache/config/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"nodePathMap": [
{
"node": "DEFAULT_PATH_FOR_NON_LISTED_NODES",
"paths": ["/data/example-cache-storage"]
},
{
"node": "minikube",
"paths": ["/data/example-cache-storage"]
},
{
"node": "kind-control-plane",
"paths": ["/var/opt/example-cache-storage"]
}
]
}
15 changes: 15 additions & 0 deletions examples/cache/config/helper-pod.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: v1
kind: Pod
metadata:
name: helper-pod
spec:
containers:
- name: helper
image: quay.io/buildah/stable:v1.17.0
imagePullPolicy: IfNotPresent
securityContext:
privileged: true
hostPID: true
volumeMounts:
- name: data
mountPropagation: Bidirectional
76 changes: 76 additions & 0 deletions examples/cache/config/setup
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/bin/sh

set -eu

MOUNT_NAME="$(basename "$VOL_DIR")"
CACHE_DIR="$(dirname "$VOL_DIR")/.cache"
CACHE_NAME="${PVC_ANNOTATION_CACHE_NAME:-$(echo "$PVC_NAME" | sed -E 's/^(.+)-[^-]+$/\1/')}"
CACHE_IMAGE="cache/$CACHE_NAME"

# Args: NAME VALUE
validate() {
PATTERN='^[-_a-z0-9]+$'
echo "$2" | grep -Eq "$PATTERN" \
|| (echo "invalid $1 argument provided: $2 (must match $PATTERN)" >&2; false)
}

buildah() {
/usr/bin/buildah \
--root=$CACHE_DIR/containers/storage \
--storage-driver=overlay \
"$@"
}

# Mounts a volume directory based on the latest CACHE_NAME image.
mountCache() {
echo "Creating volume $VOL_DIR from cache '$CACHE_NAME'" >&2
mkdir -m 0777 "$VOL_DIR" || exit 2
(
# Create new volume from cache's latest container image
# (The latest cache image could be pulled from a registry here)
(buildah from --pull-never --name "$MOUNT_NAME" "$CACHE_IMAGE" \
|| ([ $? -eq 125 ] && (
buildah delete "$MOUNT_NAME"
buildah from --name "$MOUNT_NAME" scratch
))) >/dev/null &&
CONTAINERDIR="$(buildah mount "$MOUNT_NAME")" &&
mount -o bind,rshared "$CONTAINERDIR" "$VOL_DIR" &&
chmod 0777 "$VOL_DIR"
) || (
umount "$VOL_DIR" 2>/dev/null 1>&2
buildah umount "$MOUNT_NAME" 2>/dev/null 1>&2
buildah delete "$MOUNT_NAME" 2>/dev/null 1>&2
rm -rf "$VOL_DIR"
false
)
echo "$VOL_DIR"
}

# Unmounts a cache volume directory, commits it and tags it as latest image for the given CACHE_NAME.
umountCache() {
# Commit volume only if dir is mounted (node restart results in unmounted volumes).
if mountpoint -q "$VOL_DIR"; then
echo "Committing volume $VOL_DIR to cache '$CACHE_NAME'" >&2
IMGID="$(buildah commit -q --timestamp 1 "$MOUNT_NAME")" &&
buildah tag "$IMGID" "$CACHE_IMAGE" &&
# The latest cache image could be pushed to a registry here
umount "$VOL_DIR"
fi

# Delete volume / container
echo "Deleting volume $VOL_DIR" >&2
buildah umount "$MOUNT_NAME" >/dev/null || true
buildah delete "$MOUNT_NAME" >/dev/null || true
rm -rf "$VOL_DIR" || (printf 'error: volume deletion blocked by mount: '; grep $MOUNT_NAME /etc/mtab; false) >&2
}


mkdir -p "$CACHE_DIR/containers/storage"
validate CACHE_NAME "$CACHE_NAME"
validate MOUNT_NAME "$MOUNT_NAME"

if [ "${1:-}" = teardown ]; then
umountCache
else
mountCache
fi
3 changes: 3 additions & 0 deletions examples/cache/config/teardown
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh

sh /script/setup teardown
Loading

0 comments on commit 0dbf8c9

Please sign in to comment.