Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit e821731

Browse files
committedFeb 21, 2021
Enhance helper Pod interface and configuration.
* 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 rancher#164 Closes rancher#165 Signed-off-by: Max Goltzsche <[email protected]>
1 parent 01eaa8c commit e821731

28 files changed

+685
-481
lines changed
 

‎.dockerignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
./.dapper
22
./.cache
33
./dist
4+
./examples/cache/testmount

‎.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,7 @@
55
*.swp
66
.idea
77
.vscode/
8-
Dockerfile.dapper[0-9]*
8+
Dockerfile.dapper[0-9]*
9+
local-path-provisioner
10+
/examples/cache/testmount
11+

‎README.md

+21-41
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ Now you've verified that the provisioner works as expected.
119119

120120
### Customize the ConfigMap
121121

122-
The configuration of the provisioner is a json file `config.json` and two bash scripts `setup` and `teardown`, stored in the a config map, e.g.:
122+
The configuration of the provisioner is a json file `config.json`, a Pod template `helper-pod.yaml` and two bash scripts `setup` and `teardown`, e.g.:
123123
```
124124
kind: ConfigMap
125125
apiVersion: v1
@@ -146,41 +146,11 @@ data:
146146
}
147147
setup: |-
148148
#!/bin/sh
149-
while getopts "m:s:p:" opt
150-
do
151-
case $opt in
152-
p)
153-
absolutePath=$OPTARG
154-
;;
155-
s)
156-
sizeInBytes=$OPTARG
157-
;;
158-
m)
159-
volMode=$OPTARG
160-
;;
161-
esac
162-
done
163-
164-
mkdir -m 0777 -p ${absolutePath}
149+
mkdir -m 0777 -p "$VOL_DIR"
165150
teardown: |-
166151
#!/bin/sh
167-
while getopts "m:s:p:" opt
168-
do
169-
case $opt in
170-
p)
171-
absolutePath=$OPTARG
172-
;;
173-
s)
174-
sizeInBytes=$OPTARG
175-
;;
176-
m)
177-
volMode=$OPTARG
178-
;;
179-
esac
180-
done
181-
182-
rm -rf ${absolutePath}
183-
helperPod.yaml: |-
152+
rm -rf "$VOL_DIR"
153+
helper-pod.yaml: |-
184154
apiVersion: v1
185155
kind: Pod
186156
metadata:
@@ -209,16 +179,26 @@ The configuration must obey following rules:
209179
3. No duplicate paths allowed for one node.
210180
4. No duplicate node allowed.
211181

212-
#### Scripts `setup` and `teardown` and `helperPod.yaml`
182+
#### Scripts `setup` and `teardown` and the `helper-pod.yaml` template
213183

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

216-
The script `teardown` will be executed after the volume is deleted, to cleanup the directory on the node for the volume.
188+
The scripts receive their input as environment variables:
217189

218-
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>` :
219-
* path: the absolute path provisioned on the node
220-
- size: pvc.Spec.resources.requests.storage in bytes
221-
* mode: pvc.Spec.VolumeMode
190+
| Environment variable | Description |
191+
| -------------------- | ----------- |
192+
| `VOL_DIR` | Volume directory that should be created or removed. |
193+
| `VOL_NAME` | Name of the PersistentVolume. |
194+
| `VOL_TYPE` | Type of the PersistentVolume (`Block` or `Filesystem`). |
195+
| `VOL_SIZE_BYTES` | Requested volume size in bytes. |
196+
| `PVC_NAME` | Name of the PersistentVolumeClaim. |
197+
| `PVC_NAMESPACE` | Namespace of the PersistentVolumeClaim. |
198+
| `PVC_ANNOTATION` | Value of the PersistentVolumeClaim annotation specified by the manager's `--pvc-annotation` option. |
199+
| `PVC_ANNOTATION_{SUFFIX}` | Value of the PersistentVolumeClaim annotation with the prefix specified by the manager's `--pvc-annotation` option. The `SUFFIX` is the normalized path within the annotation name after the `/`. E.g. if `local-path-provisioner` 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`. If the helper Pod requires such an annotation `local-path-provisioner` can be run with e.g. `--pvc-annotation-required=storage.example.org/cache-name`. |
200+
201+
Additional environment variables and defaults for the optional `PVC_ANNOTATION*` can be specified within the helper Pod template.
222202

223203
#### Reloading
224204

‎debug/config.yaml

+3-33
Original file line numberDiff line numberDiff line change
@@ -39,41 +39,11 @@ data:
3939
}
4040
setup: |-
4141
#!/bin/sh
42-
while getopts "m:s:p:" opt
43-
do
44-
case $opt in
45-
p)
46-
absolutePath=$OPTARG
47-
;;
48-
s)
49-
sizeInBytes=$OPTARG
50-
;;
51-
m)
52-
volMode=$OPTARG
53-
;;
54-
esac
55-
done
56-
57-
mkdir -m 0777 -p ${absolutePath}
42+
mkdir -m 0777 -p "$VOL_DIR"
5843
teardown: |-
5944
#!/bin/sh
60-
while getopts "m:s:p:" opt
61-
do
62-
case $opt in
63-
p)
64-
absolutePath=$OPTARG
65-
;;
66-
s)
67-
sizeInBytes=$OPTARG
68-
;;
69-
m)
70-
volMode=$OPTARG
71-
;;
72-
esac
73-
done
74-
75-
rm -rf ${absolutePath}
76-
helperPod.yaml: |-
45+
rm -rf "$VOL_DIR"
46+
helper-pod.yaml: |-
7747
apiVersion: v1
7848
kind: Pod
7949
metadata:

‎deploy/chart/templates/configmap.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ data:
1313
{{ .Values.configmap.setup | nindent 4 }}
1414
teardown: |-
1515
{{ .Values.configmap.teardown | nindent 4 }}
16-
helperPod.yaml: |-
16+
helper-pod.yaml: |-
1717
{{ .Values.configmap.helperPod | nindent 4 }}
1818

‎deploy/chart/values.yaml

+2-33
Original file line numberDiff line numberDiff line change
@@ -93,41 +93,10 @@ configmap:
9393
# specify the custom script for setup and teardown
9494
setup: |-
9595
#!/bin/sh
96-
while getopts "m:s:p:" opt
97-
do
98-
case $opt in
99-
p)
100-
absolutePath=$OPTARG
101-
;;
102-
s)
103-
sizeInBytes=$OPTARG
104-
;;
105-
m)
106-
volMode=$OPTARG
107-
;;
108-
esac
109-
done
110-
111-
mkdir -m 0777 -p ${absolutePath}
96+
mkdir -m 0777 -p "$VOL_DIR"
11297
teardown: |-
11398
#!/bin/sh
114-
while getopts "m:s:p:" opt
115-
do
116-
case $opt in
117-
p)
118-
absolutePath=$OPTARG
119-
;;
120-
s)
121-
sizeInBytes=$OPTARG
122-
;;
123-
m)
124-
volMode=$OPTARG
125-
;;
126-
esac
127-
done
128-
129-
rm -rf ${absolutePath}
130-
# specify the custom helper pod yaml
99+
rm -rf "$VOL_DIR"
131100
helperPod: |-
132101
apiVersion: v1
133102
kind: Pod

‎deploy/example-config.yaml

+3-35
Original file line numberDiff line numberDiff line change
@@ -23,41 +23,11 @@ data:
2323
}
2424
setup: |-
2525
#!/bin/sh
26-
while getopts "m:s:p:" opt
27-
do
28-
case $opt in
29-
p)
30-
absolutePath=$OPTARG
31-
;;
32-
s)
33-
sizeInBytes=$OPTARG
34-
;;
35-
m)
36-
volMode=$OPTARG
37-
;;
38-
esac
39-
done
40-
41-
mkdir -m 0777 -p ${absolutePath}
26+
mkdir -m 0777 -p "$VOL_DIR"
4227
teardown: |-
4328
#!/bin/sh
44-
while getopts "m:s:p:" opt
45-
do
46-
case $opt in
47-
p)
48-
absolutePath=$OPTARG
49-
;;
50-
s)
51-
sizeInBytes=$OPTARG
52-
;;
53-
m)
54-
volMode=$OPTARG
55-
;;
56-
esac
57-
done
58-
59-
rm -rf ${absolutePath}
60-
helperPod.yaml: |-
29+
rm -rf "$VOL_DIR"
30+
helper-pod.yaml: |-
6131
apiVersion: v1
6232
kind: Pod
6333
metadata:
@@ -66,5 +36,3 @@ data:
6636
containers:
6737
- name: helper-pod
6838
image: busybox
69-
70-

‎deploy/local-path-storage.yaml

+3-35
Original file line numberDiff line numberDiff line change
@@ -110,41 +110,11 @@ data:
110110
}
111111
setup: |-
112112
#!/bin/sh
113-
while getopts "m:s:p:" opt
114-
do
115-
case $opt in
116-
p)
117-
absolutePath=$OPTARG
118-
;;
119-
s)
120-
sizeInBytes=$OPTARG
121-
;;
122-
m)
123-
volMode=$OPTARG
124-
;;
125-
esac
126-
done
127-
128-
mkdir -m 0777 -p ${absolutePath}
113+
mkdir -m 0777 -p "$VOL_DIR"
129114
teardown: |-
130115
#!/bin/sh
131-
while getopts "m:s:p:" opt
132-
do
133-
case $opt in
134-
p)
135-
absolutePath=$OPTARG
136-
;;
137-
s)
138-
sizeInBytes=$OPTARG
139-
;;
140-
m)
141-
volMode=$OPTARG
142-
;;
143-
esac
144-
done
145-
146-
rm -rf ${absolutePath}
147-
helperPod.yaml: |-
116+
rm -rf "$VOL_DIR"
117+
helper-pod.yaml: |-
148118
apiVersion: v1
149119
kind: Pod
150120
metadata:
@@ -153,5 +123,3 @@ data:
153123
containers:
154124
- name: helper-pod
155125
image: busybox
156-
157-

‎examples/cache/README.md

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Example cache provisioner
2+
3+
This example shows how to use short-lived PersistentVolumes for caching.
4+
A [buildah](https://github.com/containers/buildah)-based helper Pod is used to provision a container file system based on an image as PersistentVolume and commit it when deprovisioning.
5+
Users can select the desired cache or rather the image using a PersistentVolumeClaim annotation that is passed through to the helper Pod as environment variable.
6+
7+
While it is not part of this example caches could also be synchronized across nodes using an image registry.
8+
The [cache-provisioner](https://github.com/mgoltzsche/cache-provisioner) project aims to achieve this as well as other cache management features.
9+
10+
## Test
11+
12+
### Test the helper Pod separately
13+
14+
The helper Pod can be tested separately using docker locally:
15+
```sh
16+
./helper-test.sh
17+
```
18+
19+
### Test the integration
20+
21+
_Please note that a non-overlayfs storage directory (`/data/example-cache-storage`) must be configured._
22+
_The provided configuration is known to work with minikube (`minikube start`) and kind (`kind create cluster; kind export kubeconfig`)._
23+
24+
Install the example kustomization:
25+
```sh
26+
kustomize build . | kubectl apply -f -
27+
```
28+
29+
If you want to test changes to the `local-path-provisioner` binary locally:
30+
```sh
31+
kubectl delete -n example-cache-storage deploy example-cache-local-path-provisioner
32+
(
33+
cd ../..
34+
go build .
35+
./local-path-provisioner --debug start \
36+
--namespace=example-cache-storage \
37+
--configmap-name=example-cache-local-path-config \
38+
--service-account-name=example-cache-local-path-provisioner-service-account \
39+
--provisioner-name=storage.example.org/cache \
40+
--pvc-annotation=storage.example.org \
41+
--pvc-annotation-required=storage.example.org/cache-name
42+
)
43+
```
44+
45+
Within another terminal create an example Pod and PVC that pulls and runs a container image using [podman](https://github.com/containers/podman):
46+
```sh
47+
kubectl apply -f test-pod.yaml
48+
kubectl logs -f cached-build
49+
```
50+
51+
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:
52+
```sh
53+
kubectl delete -f test-pod.yaml
54+
kubectl apply -f test-pod.yaml
55+
kubectl logs -f cached-build
56+
```

‎examples/cache/config/config.json

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"nodePathMap": [
3+
{
4+
"node": "DEFAULT_PATH_FOR_NON_LISTED_NODES",
5+
"paths": ["/data/example-cache-storage"]
6+
},
7+
{
8+
"node": "minikube",
9+
"paths": ["/data/example-cache-storage"]
10+
},
11+
{
12+
"node": "kind-control-plane",
13+
"paths": ["/var/opt/example-cache-storage"]
14+
}
15+
]
16+
}

‎examples/cache/config/helper-pod.yaml

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
apiVersion: v1
2+
kind: Pod
3+
metadata:
4+
name: helper-pod
5+
spec:
6+
containers:
7+
- name: helper
8+
image: quay.io/buildah/stable:v1.18.0
9+
imagePullPolicy: IfNotPresent
10+
securityContext:
11+
privileged: true
12+
hostPID: true
13+
volumeMounts:
14+
- name: data
15+
mountPropagation: Bidirectional

‎examples/cache/config/setup

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#!/bin/sh
2+
3+
set -eu
4+
5+
MOUNT_NAME="$(basename "$VOL_DIR")"
6+
CACHE_DIR="$(dirname "$VOL_DIR")/.cache"
7+
CACHE_NAME="${PVC_ANNOTATION_CACHE_NAME:-$(echo "$PVC_NAME" | sed -E 's/^(.+)-[^-]+$/\1/')}"
8+
CACHE_IMAGE="cache/$CACHE_NAME"
9+
10+
# Args: NAME VALUE
11+
validate() {
12+
PATTERN='^[-_a-z0-9]+$'
13+
echo "$2" | grep -Eq "$PATTERN" \
14+
|| (echo "invalid $1 argument provided: $2 (must match $PATTERN)" >&2; false)
15+
}
16+
17+
buildah() {
18+
/usr/bin/buildah \
19+
--root=$CACHE_DIR/containers/storage \
20+
--storage-driver=overlay \
21+
"$@"
22+
}
23+
24+
# Mounts a volume directory based on the latest CACHE_NAME image.
25+
mountCache() {
26+
echo "Creating volume $VOL_DIR from cache '$CACHE_NAME'" >&2
27+
mkdir -m 0777 "$VOL_DIR" || exit 2
28+
(
29+
# Create new volume from cache's latest container image
30+
# (The latest cache image could be pulled from a registry here)
31+
(buildah from --pull-never --name "$MOUNT_NAME" "$CACHE_IMAGE" \
32+
|| ([ $? -eq 125 ] && (
33+
buildah delete "$MOUNT_NAME"
34+
buildah from --name "$MOUNT_NAME" scratch
35+
))) >/dev/null &&
36+
CONTAINERDIR="$(buildah mount "$MOUNT_NAME")" &&
37+
mount -o bind,rshared "$CONTAINERDIR" "$VOL_DIR" &&
38+
chmod 0777 "$VOL_DIR"
39+
) || (
40+
umount "$VOL_DIR" 2>/dev/null 1>&2
41+
buildah umount "$MOUNT_NAME" 2>/dev/null 1>&2
42+
buildah delete "$MOUNT_NAME" 2>/dev/null 1>&2
43+
rm -rf "$VOL_DIR"
44+
false
45+
)
46+
echo "$VOL_DIR"
47+
}
48+
49+
# Unmounts a cache volume directory, commits it and tags it as latest image for the given CACHE_NAME.
50+
umountCache() {
51+
# Commit volume only if dir is mounted (node restart results in unmounted volumes).
52+
if mountpoint -q "$VOL_DIR"; then
53+
echo "Committing volume $VOL_DIR to cache '$CACHE_NAME'" >&2
54+
IMGID="$(buildah commit -q --timestamp 1 "$MOUNT_NAME")" &&
55+
buildah tag "$IMGID" "$CACHE_IMAGE" &&
56+
# The latest cache image could be pushed to a registry here
57+
umount "$VOL_DIR"
58+
fi
59+
60+
# Delete volume / container
61+
echo "Deleting volume $VOL_DIR" >&2
62+
buildah umount "$MOUNT_NAME" >/dev/null || true
63+
buildah delete "$MOUNT_NAME" >/dev/null || true
64+
rm -rf "$VOL_DIR" || (printf 'error: volume deletion blocked by mount: '; grep $MOUNT_NAME /etc/mtab; false) >&2
65+
}
66+
67+
68+
mkdir -p "$CACHE_DIR/containers/storage"
69+
validate CACHE_NAME "$CACHE_NAME"
70+
validate MOUNT_NAME "$MOUNT_NAME"
71+
72+
if [ "${1:-}" = teardown ]; then
73+
umountCache
74+
else
75+
mountCache
76+
fi

‎examples/cache/config/teardown

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/sh
2+
3+
sh /script/setup teardown

‎examples/cache/helper-test.sh

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#!/bin/sh
2+
3+
cd "$(dirname "$0")"
4+
5+
VOLNAME=pv-xyz1_default_build-cache
6+
EXPECTED_CONTENT="testcontent $(date)"
7+
8+
# ARGS: SCRIPTNAME
9+
runScript() {
10+
mkdir -p testmount
11+
docker run --rm --privileged \
12+
-e VOL_DIR=/data/$VOLNAME \
13+
-e VOL_NAME=pv-xyz \
14+
-e VOL_SIZE_BYTES=12345678 \
15+
-e PVC_NAME=pvc-xyz \
16+
-e PVC_NAMESPACE=test-namespace \
17+
-e PVC_ANNOTATION_CACHE_NAME=mycache \
18+
--mount "type=bind,source=`pwd`/config,target=/script" \
19+
--mount "type=bind,source=`pwd`/testmount,target=/data,bind-propagation=rshared" \
20+
--entrypoint=/bin/sh \
21+
quay.io/buildah/stable:v1.18.0 \
22+
/script/$1
23+
}
24+
25+
set -e
26+
27+
mkdir -p testmount
28+
rm -rf testmount/$VOLNAME
29+
30+
echo
31+
echo TEST setup
32+
echo
33+
(
34+
set -ex
35+
runScript setup
36+
37+
echo "$EXPECTED_CONTENT" > testmount/$VOLNAME/testfile
38+
)
39+
40+
echo
41+
echo TEST teardown
42+
echo
43+
(
44+
set -ex
45+
runScript teardown
46+
47+
[ ! -d testmount/$VOLNAME ] || (echo fail: volume should be removed >&2; false)
48+
)
49+
50+
echo
51+
echo TEST restore
52+
echo
53+
(
54+
set -ex
55+
VOLNAME=pv-xyz2_default_build-cache
56+
57+
runScript setup
58+
59+
CONTENT="$(cat testmount/$VOLNAME/testfile)"
60+
[ "$CONTENT" = "$EXPECTED_CONTENT" ] || (echo fail: volume should return what was last written into that cache key >&2; false)
61+
62+
runScript teardown
63+
)

‎examples/cache/kustomization.yaml

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
apiVersion: kustomize.config.k8s.io/v1beta1
2+
kind: Kustomization
3+
4+
namespace: example-cache-storage
5+
namePrefix: example-cache-
6+
7+
commonLabels:
8+
app: example-cache-provisioner
9+
10+
resources:
11+
- ../../deploy
12+
13+
patchesStrategicMerge:
14+
- provisioner-patch.yaml
15+
16+
patchesJson6902:
17+
- target:
18+
version: v1
19+
kind: Namespace
20+
name: local-path-storage
21+
path: namespace-patch.yaml
22+
- target:
23+
group: storage.k8s.io
24+
version: v1
25+
kind: StorageClass
26+
name: local-path
27+
path: storageclass-patch.yaml
28+
29+
configMapGenerator:
30+
- name: local-path-config
31+
namespace: local-path-storage
32+
behavior: merge
33+
files:
34+
- config/config.json
35+
- config/helper-pod.yaml
36+
- config/setup
37+
- config/teardown
38+
39+
generatorOptions:
40+
disableNameSuffixHash: true

‎examples/cache/namespace-patch.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
- op: replace
2+
path: /metadata/name
3+
value: example-cache-storage

‎examples/cache/provisioner-patch.yaml

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: local-path-provisioner
5+
namespace: local-path-storage
6+
spec:
7+
template:
8+
spec:
9+
containers:
10+
- name: local-path-provisioner
11+
env:
12+
- name: CONFIGMAP_NAME
13+
value: example-cache-local-path-config
14+
- name: SERVICE_ACCOUNT_NAME
15+
value: example-cache-local-path-provisioner-service-account
16+
- name: PROVISIONER_NAME
17+
value: storage.example.org/cache
18+
- name: PVC_ANNOTATION
19+
value: storage.example.org
20+
- name: PVC_ANNOTATION_REQUIRED
21+
value: storage.example.org/cache-name
22+
- name: HELPER_POD_TIMEOUT
23+
value: "2m"
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
- op: replace
2+
path: /metadata/name
3+
value: example-cache
4+
- op: replace
5+
path: /provisioner
6+
value: storage.example.org/cache

‎examples/cache/test-pod.yaml

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
---
2+
apiVersion: v1
3+
kind: Pod
4+
metadata:
5+
name: cached-build
6+
spec:
7+
restartPolicy: Never
8+
securityContext:
9+
runAsUser: 9000
10+
runAsGroup: 9000
11+
fsGroup: 9000
12+
containers:
13+
- name: build
14+
image: mgoltzsche/podman:2.2.1
15+
command: ["/bin/sh"]
16+
args:
17+
- -c
18+
- |
19+
set -ex
20+
mktemp -d -p $HOME
21+
([ -f $HOME/date ] || date > $HOME/date) && cat $HOME/date
22+
OOMSCORE=$(cat /proc/self/oom_score_adj)
23+
podman run --name build --rm --oom-score-adj "$OOMSCORE" alpine:3.12 echo hello from nested container
24+
env:
25+
- name: HOME
26+
value: /podman
27+
securityContext:
28+
privileged: true
29+
volumeMounts:
30+
# simply cache home directory
31+
- mountPath: /podman
32+
name: cache
33+
volumes:
34+
- name: cache
35+
persistentVolumeClaim:
36+
claimName: build-cache
37+
---
38+
apiVersion: v1
39+
kind: PersistentVolumeClaim
40+
metadata:
41+
name: build-cache
42+
annotations:
43+
storage.example.org/cache-name: example-project
44+
spec:
45+
accessModes:
46+
- ReadWriteOnce
47+
volumeMode: Filesystem
48+
resources:
49+
requests:
50+
storage: 1Gi
51+
storageClassName: example-cache

‎examples/quota/README.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
this is an example to enable quota for xfs
33

44
# Usage
5-
> 1. build a helper image using the sample dockerfile to replace helper image xxx/storage-xfs-quota:v0.1 at configmap(helperPod.yaml) of debug.yaml.
6-
> 2. use the sample setup and teardown script at configmap of debug.yaml
5+
> 1. build a helper image using the sample dockerfile to replace helper image xxx/storage-xfs-quota:v0.1 at configmap(config/helper-pod.yaml).
6+
> 2. use the sample setup and teardown scripts contained within the kustomization.
77
88
Notice:
99
> 1. make sure the path at nodePathMap is the mountpoint of xfs which enables pquota
@@ -13,6 +13,7 @@ Notice:
1313
> git clone https://github.com/rancher/local-path-provisioner.git
1414
> cd local-path-provisioner
1515
> go build
16-
> kubectl apply -f debug.yaml
16+
> kustomize build example/quota | kubectl apply -f -
17+
> kubectl delete -n local-path-storage deploy local-path-provisioner
1718
> ./local-path-provisioner --debug start --namespace=local-path-storage
1819
```
File renamed without changes.
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,10 @@
11
#!/bin/sh
22

3-
while getopts "m:s:p:" opt
4-
do
5-
case $opt in
6-
p)
7-
absolutePath=$OPTARG
8-
;;
9-
s)
10-
sizeInBytes=$OPTARG
11-
;;
12-
m)
13-
volMode=$OPTARG
14-
;;
15-
esac
16-
done
17-
18-
xfsPath=$(dirname "$absolutePath")
19-
pvcName=$(basename "$absolutePath")
20-
mkdir -p ${absolutePath}
3+
xfsPath=$(dirname "$VOL_DIR")
4+
pvcName=$(basename "$VOL_DIR")
5+
sizeInBytes=$VOL_SIZE_BYTES
6+
7+
mkdir -p "$VOL_DIR"
218

229
# support xfs quota
2310
type=`stat -f -c %T ${xfsPath}`
@@ -34,10 +21,10 @@ if [ ${type} == 'xfs' ]; then
3421
id=$[${id}+1]
3522
fi
3623

37-
echo "${id}:${absolutePath}" >> /etc/projects
24+
echo "${id}:${VOL_DIR}" >> /etc/projects
3825
echo "${pvcName}:${id}" >> /etc/projid
3926

4027
xfs_quota -x -c "project -s ${pvcName}"
4128
xfs_quota -x -c "limit -p bhard=${sizeInBytes} ${pvcName}" ${xfsPath}
4229
xfs_quota -x -c "report -pbih" ${xfsPath}
43-
fi
30+
fi
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,7 @@
11
#!/bin/sh
22

3-
while getopts "m:s:p:" opt
4-
do
5-
case $opt in
6-
p)
7-
absolutePath=$OPTARG
8-
;;
9-
s)
10-
sizeInBytes=$OPTARG
11-
;;
12-
m)
13-
volMode=$OPTARG
14-
;;
15-
esac
16-
done
17-
18-
xfsPath=$(dirname "$absolutePath")
19-
pvcName=$(basename "$absolutePath")
3+
xfsPath=$(dirname "$VOL_DIR")
4+
pvcName=$(basename "$VOL_DIR")
205

216
# support xfs quota
227
type=`stat -f -c %T ${xfsPath}`
@@ -26,10 +11,10 @@ if [ ${type} == 'xfs' ]; then
2611
xfs_quota -x -c "limit -p bhard=0 ${pvcName}" ${xfsPath}
2712
fi
2813

29-
rm -rf ${absolutePath}
14+
rm -rf "$VOL_DIR"
15+
3016
if [ ${type} == 'xfs' ]; then
3117
echo "$(sed "/${pvcName}/d" /etc/projects)" > /etc/projects
3218
echo "$(sed "/${pvcName}/d" /etc/projid)" > /etc/projid
3319
xfs_quota -x -c "report -pbih" ${xfsPath}
3420
fi
35-

‎examples/quota/debug.yaml

-140
This file was deleted.

‎examples/quota/kustomization.yaml

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
apiVersion: kustomize.config.k8s.io/v1beta1
2+
kind: Kustomization
3+
4+
resources:
5+
- ../../deploy
6+
7+
configMapGenerator:
8+
- name: local-path-config
9+
namespace: local-path-storage
10+
behavior: merge
11+
files:
12+
- config/helper-pod.yaml
13+
- config/setup
14+
- config/teardown
15+
16+
generatorOptions:
17+
disableNameSuffixHash: true

‎main.go

+75-41
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import (
55
"os"
66
"os/signal"
77
"path/filepath"
8+
"strings"
89
"syscall"
10+
"time"
911

1012
"github.com/Sirupsen/logrus"
1113
"github.com/pkg/errors"
@@ -19,26 +21,34 @@ import (
1921
)
2022

2123
var (
22-
VERSION = "0.0.1"
23-
FlagConfigFile = "config"
24-
FlagProvisionerName = "provisioner-name"
25-
EnvProvisionerName = "PROVISIONER_NAME"
26-
DefaultProvisionerName = "rancher.io/local-path"
27-
FlagNamespace = "namespace"
28-
EnvNamespace = "POD_NAMESPACE"
29-
DefaultNamespace = "local-path-storage"
30-
FlagHelperImage = "helper-image"
31-
EnvHelperImage = "HELPER_IMAGE"
32-
DefaultHelperImage = "rancher/library-busybox:1.31.1"
33-
FlagServiceAccountName = "service-account-name"
34-
DefaultServiceAccount = "local-path-provisioner-service-account"
35-
EnvServiceAccountName = "SERVICE_ACCOUNT_NAME"
36-
FlagKubeconfig = "kubeconfig"
37-
DefaultConfigFileKey = "config.json"
38-
DefaultConfigMapName = "local-path-config"
39-
FlagConfigMapName = "configmap-name"
40-
FlagHelperPodFile = "helper-pod-file"
41-
DefaultHelperPodFile = "helperPod.yaml"
24+
VERSION = "0.0.1"
25+
FlagConfigFile = "config"
26+
FlagProvisionerName = "provisioner-name"
27+
EnvProvisionerName = "PROVISIONER_NAME"
28+
DefaultProvisionerName = "rancher.io/local-path"
29+
FlagNamespace = "namespace"
30+
EnvNamespace = "POD_NAMESPACE"
31+
DefaultNamespace = "local-path-storage"
32+
FlagHelperImage = "helper-image"
33+
EnvHelperImage = "HELPER_IMAGE"
34+
DefaultHelperImage = "rancher/library-busybox:1.31.1"
35+
FlagServiceAccountName = "service-account-name"
36+
DefaultServiceAccount = "local-path-provisioner-service-account"
37+
EnvServiceAccountName = "SERVICE_ACCOUNT_NAME"
38+
FlagKubeconfig = "kubeconfig"
39+
DefaultConfigFileKey = "config.json"
40+
DefaultConfigMapName = "local-path-config"
41+
FlagConfigMapName = "configmap-name"
42+
EnvConfigMapName = "CONFIGMAP_NAME"
43+
FlagHelperPodFile = "helper-pod-file"
44+
DefaultHelperPodFile = "helper-pod.yaml"
45+
EnvHelperPodFile = "HELPER_POD_FILE"
46+
FlagHelperPodTimeout = "helper-pod-timeout"
47+
EnvHelperPodTimeout = "HELPER_POD_TIMEOUT"
48+
FlagPVCAnnotation = "pvc-annotation"
49+
EnvPVCAnnotation = "PVC_ANNOTATION"
50+
FlagPVCAnnotationRequired = "pvc-annotation-required"
51+
EnvPVCAnnotationRequired = "PVC_ANNOTATION_REQUIRED"
4252
)
4353

4454
func cmdNotFound(c *cli.Context, command string) {
@@ -50,12 +60,15 @@ func onUsageError(c *cli.Context, err error, isSubcommand bool) error {
5060
}
5161

5262
func RegisterShutdownChannel(done chan struct{}) {
53-
sigs := make(chan os.Signal, 1)
63+
sigs := make(chan os.Signal, 2)
5464
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
5565
go func() {
5666
sig := <-sigs
57-
logrus.Infof("Receive %v to exit", sig)
67+
logrus.Infof("Received %v signal - terminating", sig)
5868
close(done)
69+
<-sigs
70+
logrus.Info("Received 2nd termination signal - exiting forcefully")
71+
os.Exit(254)
5972
}()
6073
}
6174

@@ -80,21 +93,16 @@ func StartCmd() cli.Command {
8093
EnvVar: EnvNamespace,
8194
Value: DefaultNamespace,
8295
},
83-
cli.StringFlag{
84-
Name: FlagHelperImage,
85-
Usage: "Required. The helper image used for create/delete directories on the host",
86-
EnvVar: EnvHelperImage,
87-
Value: DefaultHelperImage,
88-
},
8996
cli.StringFlag{
9097
Name: FlagKubeconfig,
9198
Usage: "Paths to a kubeconfig. Only required when it is out-of-cluster.",
9299
Value: "",
93100
},
94101
cli.StringFlag{
95-
Name: FlagConfigMapName,
96-
Usage: "Required. Specify configmap name.",
97-
Value: DefaultConfigMapName,
102+
Name: FlagConfigMapName,
103+
Usage: "Required. Specify configmap name.",
104+
EnvVar: EnvConfigMapName,
105+
Value: DefaultConfigMapName,
98106
},
99107
cli.StringFlag{
100108
Name: FlagServiceAccountName,
@@ -103,9 +111,28 @@ func StartCmd() cli.Command {
103111
Value: DefaultServiceAccount,
104112
},
105113
cli.StringFlag{
106-
Name: FlagHelperPodFile,
107-
Usage: "Paths to the Helper pod yaml file",
108-
Value: "",
114+
Name: FlagHelperPodFile,
115+
Usage: "Paths to the Helper pod yaml file",
116+
EnvVar: EnvHelperPodFile,
117+
Value: "",
118+
},
119+
cli.StringFlag{
120+
Name: FlagHelperPodTimeout,
121+
Usage: "Helper pod command execution timeout",
122+
EnvVar: EnvHelperPodTimeout,
123+
Value: "2m",
124+
},
125+
cli.StringFlag{
126+
Name: FlagPVCAnnotation,
127+
Usage: "A PersistentVolumeClaim annotation or prefix passed through to the helper pod as env vars (PVC_ANNOTATION[_<ANNOTATION_PATH_SUFFIX>]=<ANNOTATION_VALUE>)",
128+
EnvVar: EnvPVCAnnotation,
129+
Value: "",
130+
},
131+
cli.StringFlag{
132+
Name: FlagPVCAnnotationRequired,
133+
Usage: "Annotation that must be specified on PersistentVolumeClaims (multiple comma-separated)",
134+
EnvVar: EnvPVCAnnotationRequired,
135+
Value: "",
109136
},
110137
},
111138
Action: func(c *cli.Context) {
@@ -196,17 +223,19 @@ func startDaemon(c *cli.Context) error {
196223
return fmt.Errorf("invalid empty flag %v and it also does not exist at ConfigMap %v/%v with err: %v", FlagConfigFile, namespace, configMapName, err)
197224
}
198225
}
199-
helperImage := c.String(FlagHelperImage)
200-
if helperImage == "" {
201-
return fmt.Errorf("invalid empty flag %v", FlagHelperImage)
202-
}
203-
204226
serviceAccountName := c.String(FlagServiceAccountName)
205227
if serviceAccountName == "" {
206228
return fmt.Errorf("invalid empty flag %v", FlagServiceAccountName)
207229
}
208230

209-
// if helper pod file is not specified, then find the helper pod by configmap with key = helperPod.yaml
231+
pvcAnnotation := c.String(FlagPVCAnnotation)
232+
pvcAnnotationsRequired := c.String(FlagPVCAnnotationRequired)
233+
var requiredPVCAnnotations []string
234+
if pvcAnnotationsRequired != "" {
235+
requiredPVCAnnotations = strings.Split(pvcAnnotationsRequired, ",")
236+
}
237+
238+
// if helper pod file is not specified, then find the helper pod by configmap with key = helper-pod.yaml
210239
// if helper pod file is specified with flag FlagHelperPodFile, then load the file
211240
helperPodFile := c.String(FlagHelperPodFile)
212241
helperPodYaml := ""
@@ -221,8 +250,13 @@ func startDaemon(c *cli.Context) error {
221250
return fmt.Errorf("could not open file %v with err: %v", helperPodFile, err)
222251
}
223252
}
253+
helperPodTimeoutStr := c.String(FlagHelperPodTimeout)
254+
helperPodTimeout, err := time.ParseDuration(helperPodTimeoutStr)
255+
if err != nil {
256+
return fmt.Errorf("invalid %s option provided: %s", FlagHelperPodTimeout, err)
257+
}
224258

225-
provisioner, err := NewProvisioner(stopCh, kubeClient, configFile, namespace, helperImage, configMapName, serviceAccountName, helperPodYaml)
259+
provisioner, err := NewProvisioner(stopCh, kubeClient, configFile, namespace, configMapName, serviceAccountName, helperPodYaml, helperPodTimeout, pvcAnnotation, requiredPVCAnnotations)
226260
if err != nil {
227261
return err
228262
}

‎provisioner.go

+185-79
Large diffs are not rendered by default.

‎util.go

+3
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,8 @@ func loadHelperPodFile(helperPodYaml string) (*v1.Pod, error) {
3333
if err != nil {
3434
return nil, fmt.Errorf("invalid unmarshal the helper pod with helperPodJson: %v", string(helperPodJSON))
3535
}
36+
if len(p.Spec.Containers) == 0 {
37+
return nil, fmt.Errorf("helper pod template does not specify any container")
38+
}
3639
return &p, nil
3740
}

0 commit comments

Comments
 (0)
Please sign in to comment.