Skip to content

Commit 4d0f260

Browse files
authored
Add support for dm-integrity on lvm raids (#106)
1 parent bc11f72 commit 4d0f260

File tree

10 files changed

+130
-3
lines changed

10 files changed

+130
-3
lines changed

.github/workflows/docker.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ jobs:
4343
- name: Checkout
4444
uses: actions/checkout@v4
4545

46+
- name: load modules for dm-raid and dm-integrity
47+
run: lsmod && sudo modprobe dm-raid && sudo modprobe dm-integrity && lsmod
48+
4649
- name: Set up Go 1.23
4750
uses: actions/setup-go@v5
4851
with:

cmd/provisioner/createlv.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ func createLVCmd() *cli.Command {
3232
Name: flagDevicesPattern,
3333
Usage: "Required. comma-separated grok patterns of the physical volumes to use.",
3434
},
35+
&cli.BoolFlag{
36+
Name: flagIntegrity,
37+
Usage: "Optional. if set type must be mirrored. Inserts a dm-integrity layer.",
38+
},
3539
},
3640
Action: func(c *cli.Context) error {
3741
if err := createLV(c); err != nil {
@@ -64,6 +68,7 @@ func createLV(c *cli.Context) error {
6468
if devicesPattern == "" {
6569
return fmt.Errorf("invalid empty flag %v", flagDevicesPattern)
6670
}
71+
integrity := c.Bool(flagIntegrity)
6772

6873
klog.Infof("create lv %s size:%d vg:%s devicespattern:%s type:%s", lvName, lvSize, vgName, devicesPattern, lvmType)
6974

@@ -72,7 +77,7 @@ func createLV(c *cli.Context) error {
7277
return fmt.Errorf("unable to create vg: %w output:%s", err, output)
7378
}
7479

75-
output, err = lvm.CreateLVS(vgName, lvName, lvSize, lvmType)
80+
output, err = lvm.CreateLVS(vgName, lvName, lvSize, lvmType, integrity)
7681
if err != nil {
7782
return fmt.Errorf("unable to create lv: %w output:%s", err, output)
7883
}

cmd/provisioner/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const (
1414
flagVGName = "vgname"
1515
flagDevicesPattern = "devices"
1616
flagLVMType = "lvmtype"
17+
flagIntegrity = "integrity"
1718
)
1819

1920
func cmdNotFound(c *cli.Context, command string) {

pkg/lvm/controllerserver.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
114114
if !(lvmType == "linear" || lvmType == "mirror" || lvmType == "striped") {
115115
return nil, status.Errorf(codes.Internal, "lvmType is incorrect: %s", lvmType)
116116
}
117+
integrity, err := strconv.ParseBool(req.GetParameters()["integrity"])
118+
if err != nil {
119+
klog.Warningf("Could not parse 'integrity' request parameter, assuming false: %s", err)
120+
}
117121

118122
volumeContext := req.GetParameters()
119123
size := strconv.FormatInt(req.GetCapacityRange().GetRequiredBytes(), 10)
@@ -140,6 +144,7 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
140144
namespace: cs.namespace,
141145
vgName: cs.vgName,
142146
hostWritePath: cs.hostWritePath,
147+
integrity: integrity,
143148
}
144149
if err := createProvisionerPod(ctx, va); err != nil {
145150
klog.Errorf("error creating provisioner pod :%v", err)

pkg/lvm/lvm.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ type volumeAction struct {
7878
namespace string
7979
vgName string
8080
hostWritePath string
81+
integrity bool
8182
}
8283

8384
const (
@@ -264,6 +265,9 @@ func createProvisionerPod(ctx context.Context, va volumeAction) (err error) {
264265
args := []string{}
265266
if va.action == actionTypeCreate {
266267
args = append(args, "createlv", "--lvsize", fmt.Sprintf("%d", va.size), "--devices", va.devicesPattern, "--lvmtype", va.lvmType)
268+
if va.integrity {
269+
args = append(args, "--integrity")
270+
}
267271
}
268272
if va.action == actionTypeDelete {
269273
args = append(args, "deletelv")
@@ -511,7 +515,7 @@ func CreateVG(name string, devicesPattern string) (string, error) {
511515

512516
// CreateLVS creates the new volume
513517
// used by lvcreate provisioner pod and by nodeserver for ephemeral volumes
514-
func CreateLVS(vg string, name string, size uint64, lvmType string) (string, error) {
518+
func CreateLVS(vg string, name string, size uint64, lvmType string, integrity bool) (string, error) {
515519

516520
if lvExists(vg, name) {
517521
klog.Infof("logicalvolume: %s already exists\n", name)
@@ -550,6 +554,15 @@ func CreateLVS(vg string, name string, size uint64, lvmType string) (string, err
550554
return "", fmt.Errorf("unsupported lvmtype: %s", lvmType)
551555
}
552556

557+
if integrity {
558+
switch lvmType {
559+
case mirrorType:
560+
args = append(args, "--raidintegrity", "y")
561+
default:
562+
return "", fmt.Errorf("integrity is only supported if type is mirror")
563+
}
564+
}
565+
553566
tags := []string{"lv.metal-stack.io/csi-lvm-driver"}
554567
for _, tag := range tags {
555568
args = append(args, "--addtag", tag)

pkg/lvm/nodeserver.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ func (ns *nodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis
122122
return nil, fmt.Errorf("unable to create vg: %w output:%s", err, output)
123123
}
124124

125-
output, err = CreateLVS(ns.vgName, volID, size, req.GetVolumeContext()["type"])
125+
output, err = CreateLVS(ns.vgName, volID, size, req.GetVolumeContext()["type"], false)
126126
if err != nil {
127127
return nil, fmt.Errorf("unable to create lv: %w output:%s", err, output)
128128
}

tests/bats/test.bats

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,51 @@
109109
[ "$status" -eq 0 ]
110110
}
111111

112+
@test "create storageclass mirror-integrity" {
113+
# Requires kernel modules:
114+
# modprobe dm-raid && modprobe dm-integrity
115+
run kubectl apply -f files/storageclass.mirror-integrity.yaml --wait --timeout=10s
116+
[ "$status" -eq 0 ]
117+
}
118+
119+
@test "create pvc mirror-integrity" {
120+
run kubectl apply -f files/pvc.mirror-integrity.yaml --wait --timeout=10s
121+
[ "$status" -eq 0 ]
122+
123+
run kubectl wait --for=jsonpath='{.status.phase}'=Pending -f files/pvc.mirror-integrity.yaml --timeout=10s
124+
[ "$status" -eq 0 ]
125+
}
126+
127+
@test "deploy mirror-integrity pod" {
128+
run kubectl apply -f files/pod.mirror-integrity.vol.yaml --wait --timeout=10s
129+
[ "$status" -eq 0 ]
130+
}
131+
132+
@test "mirror-integrity pod running" {
133+
run kubectl wait --for=jsonpath='{.status.phase}'=Running -f files/pod.mirror-integrity.vol.yaml --timeout=30s
134+
[ "$status" -eq 0 ]
135+
}
136+
137+
@test "pvc mirror-integrity bound" {
138+
run kubectl wait --for=jsonpath='{.status.phase}'=Bound -f files/pvc.mirror-integrity.yaml --timeout=10s
139+
[ "$status" -eq 0 ]
140+
}
141+
142+
@test "delete mirror-integrity pod" {
143+
run kubectl delete -f files/pod.mirror-integrity.vol.yaml --grace-period=0 --wait --timeout=10s
144+
[ "$status" -eq 0 ]
145+
}
146+
147+
@test "delete mirror-integrity pvc" {
148+
run kubectl delete -f files/pvc.mirror-integrity.yaml --grace-period=0 --wait --timeout=10s
149+
[ "$status" -eq 0 ]
150+
}
151+
152+
@test "delete storageclass mirror-integrity" {
153+
run kubectl delete -f files/storageclass.mirror-integrity.yaml --wait --timeout=20s
154+
[ "$status" -eq 0 ]
155+
}
156+
112157
@test "deploy inline xfs pod with ephemeral volume" {
113158
run kubectl apply -f files/pod.inline.vol.xfs.yaml --wait --timeout=20s
114159
[ "$status" -eq 0 ]
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
apiVersion: v1
2+
kind: Pod
3+
metadata:
4+
name: volume-test
5+
spec:
6+
containers:
7+
- name: volume-test
8+
image: alpine
9+
imagePullPolicy: IfNotPresent
10+
command:
11+
- tail
12+
- -f
13+
- /etc/hosts
14+
securityContext:
15+
allowPrivilegeEscalation: false
16+
runAsNonRoot: true
17+
runAsUser: 10014
18+
seccompProfile:
19+
type: RuntimeDefault
20+
capabilities:
21+
drop:
22+
- ALL
23+
volumeMounts:
24+
- name: mirror-integrity
25+
mountPath: /mirror-integrity
26+
resources:
27+
limits:
28+
cpu: 100m
29+
memory: 100M
30+
volumes:
31+
- name: mirror-integrity
32+
persistentVolumeClaim:
33+
claimName: lvm-pvc-mirror-integrity
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
apiVersion: v1
2+
kind: PersistentVolumeClaim
3+
metadata:
4+
name: lvm-pvc-mirror-integrity
5+
spec:
6+
accessModes:
7+
- ReadWriteOnce
8+
storageClassName: csi-driver-lvm-mirror-integrity
9+
resources:
10+
requests:
11+
storage: 100Mi
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
apiVersion: storage.k8s.io/v1
2+
kind: StorageClass
3+
metadata:
4+
name: csi-driver-lvm-mirror-integrity
5+
parameters:
6+
type: mirror
7+
integrity: "true"
8+
provisioner: lvm.csi.metal-stack.io
9+
allowVolumeExpansion: true
10+
reclaimPolicy: Delete
11+
volumeBindingMode: WaitForFirstConsumer

0 commit comments

Comments
 (0)