[WIP] Start very early implementation
This commit is contained in:
commit
c98b421b03
19
.gitignore
vendored
Normal file
19
.gitignore
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
bin/
|
||||||
|
|
||||||
|
# Test binary, build with ` + "`go test -c`" + `
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# editor and IDE paraphernalia
|
||||||
|
.idea
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
12
Dockerfile
Normal file
12
Dockerfile
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
FROM golang:1.18-buster
|
||||||
|
|
||||||
|
ARG GOPROXY
|
||||||
|
|
||||||
|
WORKDIR /workspace
|
||||||
|
COPY . .
|
||||||
|
ENV GOPROXY=${GOPROXY:-https://proxy.golang.org}
|
||||||
|
|
||||||
|
RUN make csi
|
||||||
|
RUN chmod u+x /workspace/bin/csi
|
||||||
|
|
||||||
|
ENTRYPOINT ["/workspace/bin/csi"]
|
49
Makefile
Normal file
49
Makefile
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# Image URL to use all building/pushing image targets
|
||||||
|
IMG ?= csi:latest
|
||||||
|
|
||||||
|
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
|
||||||
|
ifeq (,$(shell go env GOBIN))
|
||||||
|
GOBIN=$(shell go env GOPATH)/bin
|
||||||
|
else
|
||||||
|
GOBIN=$(shell go env GOBIN)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Setting SHELL to bash allows bash commands to be executed by recipes.
|
||||||
|
# Options are set to exit when a recipe line exits non-zero or a piped command fails.
|
||||||
|
SHELL = /usr/bin/env bash -o pipefail
|
||||||
|
.SHELLFLAGS = -ec
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: build
|
||||||
|
|
||||||
|
##@ General
|
||||||
|
|
||||||
|
.PHONY: help
|
||||||
|
help: ## Display this help.
|
||||||
|
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
|
||||||
|
|
||||||
|
.PHONY: fmt
|
||||||
|
fmt: ## Run go fmt against code.
|
||||||
|
go fmt ./...
|
||||||
|
|
||||||
|
.PHONY: vet
|
||||||
|
vet: ## Run go vet against code.
|
||||||
|
go vet ./...
|
||||||
|
|
||||||
|
##@ Build
|
||||||
|
|
||||||
|
.PHONY: build
|
||||||
|
build: fmt vet ## Build manager binary.
|
||||||
|
go build -o bin/manager main.go
|
||||||
|
|
||||||
|
.PHONY: run
|
||||||
|
run: fmt vet ## Run a csi driver from your host.
|
||||||
|
go run ./main.go
|
||||||
|
|
||||||
|
.PHONY: docker-build
|
||||||
|
docker-build: test ## Build docker image with the manager.
|
||||||
|
docker build -t ${IMG} .
|
||||||
|
|
||||||
|
.PHONY: docker-push
|
||||||
|
docker-push: ## Push docker image with the manager.
|
||||||
|
docker push ${IMG}
|
7
PROJECT
Normal file
7
PROJECT
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
version:
|
||||||
|
number: 1
|
||||||
|
stage: 0
|
||||||
|
goversion: ""
|
||||||
|
name: ""
|
||||||
|
repo: csi-driver
|
||||||
|
goversion: "1.18"
|
65
deploy/clusterrole.yaml
Normal file
65
deploy/clusterrole.yaml
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: csi-node
|
||||||
|
rules: []
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: csi-controller
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- persistentvolumes
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- persistentvolumeclaims
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- storage.k8s.io
|
||||||
|
resources:
|
||||||
|
- storageclasses
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- events
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- create
|
||||||
|
- update
|
||||||
|
- patch
|
||||||
|
- apiGroups:
|
||||||
|
- storage.k8s.io
|
||||||
|
resources:
|
||||||
|
- csinodes
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- nodes
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
25
deploy/clusterrolebinding.yaml
Normal file
25
deploy/clusterrolebinding.yaml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: csi-node
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: csi-node
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: csi-node
|
||||||
|
namespace: default
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: csi-provisioner
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: csi-provisioner
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: csi-controller
|
||||||
|
namespace: default
|
7
deploy/csidriver.yaml
Normal file
7
deploy/csidriver.yaml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
apiVersion: storage.k8s.io/v1
|
||||||
|
kind: CSIDriver
|
||||||
|
metadata:
|
||||||
|
name: p5x
|
||||||
|
spec:
|
||||||
|
attachRequired: false
|
||||||
|
podInfoOnMount: false
|
104
deploy/daemonset.yaml
Normal file
104
deploy/daemonset.yaml
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
kind: DaemonSet
|
||||||
|
apiVersion: apps/v1
|
||||||
|
metadata:
|
||||||
|
name: p5x-csi-node
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: p5x-csi-node
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: p5x-csi-node
|
||||||
|
spec:
|
||||||
|
serviceAccountName: csi-node
|
||||||
|
tolerations:
|
||||||
|
- operator: Exists
|
||||||
|
priorityClassName: system-node-critical
|
||||||
|
dnsPolicy: ClusterFirstWithHostNet
|
||||||
|
containers:
|
||||||
|
- args:
|
||||||
|
- --endpoint=$(CSI_ENDPOINT)
|
||||||
|
- --logtostderr
|
||||||
|
- --nodeid=$(NODE_NAME)
|
||||||
|
env:
|
||||||
|
- name: CSI_ENDPOINT
|
||||||
|
value: unix:/csi/csi.sock
|
||||||
|
- name: NODE_NAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: spec.nodeName
|
||||||
|
image: csi-image
|
||||||
|
lifecycle:
|
||||||
|
preStop:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- rm /csi/csi.sock
|
||||||
|
livenessProbe:
|
||||||
|
failureThreshold: 5
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: healthz
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 3
|
||||||
|
name: csi-plugin
|
||||||
|
ports:
|
||||||
|
- containerPort: 9909
|
||||||
|
name: healthz
|
||||||
|
protocol: TCP
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /var/lib/kubelet
|
||||||
|
mountPropagation: Bidirectional
|
||||||
|
name: kubelet-dir
|
||||||
|
- mountPath: /csi
|
||||||
|
name: plugin-dir
|
||||||
|
- mountPath: /registration
|
||||||
|
name: registration-dir
|
||||||
|
- args:
|
||||||
|
- --csi-address=$(ADDRESS)
|
||||||
|
- --kubelet-registration-path=$(DRIVER_REG_SOCK_PATH)
|
||||||
|
- --v=5
|
||||||
|
env:
|
||||||
|
- name: ADDRESS
|
||||||
|
value: /csi/csi.sock
|
||||||
|
- name: DRIVER_REG_SOCK_PATH
|
||||||
|
value: /var/lib/kubelet/csi-plugins/demo.csi.com/csi.sock
|
||||||
|
image: quay.io/k8scsi/csi-node-driver-registrar:v2.1.0
|
||||||
|
name: node-driver-registrar
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /csi
|
||||||
|
name: plugin-dir
|
||||||
|
- mountPath: /registration
|
||||||
|
name: registration-dir
|
||||||
|
- args:
|
||||||
|
- --csi-address=$(ADDRESS)
|
||||||
|
- --health-port=$(HEALTH_PORT)
|
||||||
|
env:
|
||||||
|
- name: ADDRESS
|
||||||
|
value: /csi/csi.sock
|
||||||
|
- name: HEALTH_PORT
|
||||||
|
value: "9909"
|
||||||
|
image: quay.io/k8scsi/livenessprobe:v1.1.0
|
||||||
|
name: liveness-probe
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /csi
|
||||||
|
name: plugin-dir
|
||||||
|
volumes:
|
||||||
|
- hostPath:
|
||||||
|
path: /var/lib/kubelet
|
||||||
|
type: Directory
|
||||||
|
name: kubelet-dir
|
||||||
|
- hostPath:
|
||||||
|
path: /var/lib/kubelet/csi-plugins/demo.csi.com/
|
||||||
|
type: DirectoryOrCreate
|
||||||
|
name: plugin-dir
|
||||||
|
- hostPath:
|
||||||
|
path: /var/lib/kubelet/plugins_registry/
|
||||||
|
type: Directory
|
||||||
|
name: registration-dir
|
11
deploy/serviceaccount.yaml
Normal file
11
deploy/serviceaccount.yaml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: csi-controller
|
||||||
|
namespace: default
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: csi-node
|
||||||
|
namespace: default
|
86
deploy/statefulset.yaml
Normal file
86
deploy/statefulset.yaml
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app.kubernetes.io/name: p5x-controller
|
||||||
|
name: p5x-controller
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: p5x-csi-controller
|
||||||
|
serviceName: csi-controller
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: p5x-csi-controller
|
||||||
|
spec:
|
||||||
|
priorityClassName: system-cluster-critical
|
||||||
|
serviceAccountName: csi-controller
|
||||||
|
tolerations:
|
||||||
|
- key: CriticalAddonsOnly
|
||||||
|
operator: Exists
|
||||||
|
containers:
|
||||||
|
- args:
|
||||||
|
- --endpoint=$(CSI_ENDPOINT)
|
||||||
|
- --logtostderr
|
||||||
|
- --nodeid=$(NODE_NAME)
|
||||||
|
env:
|
||||||
|
- name: CSI_ENDPOINT
|
||||||
|
value: unix:///var/lib/csi/sockets/pluginproxy/csi.sock
|
||||||
|
- name: NODE_NAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: spec.nodeName
|
||||||
|
image: csi-image
|
||||||
|
livenessProbe:
|
||||||
|
failureThreshold: 5
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: healthz
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 3
|
||||||
|
name: csi-plugin
|
||||||
|
ports:
|
||||||
|
- containerPort: 9909
|
||||||
|
name: healthz
|
||||||
|
protocol: TCP
|
||||||
|
securityContext:
|
||||||
|
capabilities:
|
||||||
|
add:
|
||||||
|
- SYS_ADMIN
|
||||||
|
privileged: true
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /var/lib/csi/sockets/pluginproxy/
|
||||||
|
name: socket-dir
|
||||||
|
- args:
|
||||||
|
- --csi-address=$(ADDRESS)
|
||||||
|
- --timeout=60s
|
||||||
|
- --v=5
|
||||||
|
env:
|
||||||
|
- name: ADDRESS
|
||||||
|
value: /var/lib/csi/sockets/pluginproxy/csi.sock
|
||||||
|
image: quay.io/k8scsi/csi-provisioner:v1.6.0
|
||||||
|
name: csi-provisioner
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /var/lib/csi/sockets/pluginproxy/
|
||||||
|
name: socket-dir
|
||||||
|
- args:
|
||||||
|
- --csi-address=$(ADDRESS)
|
||||||
|
- --health-port=$(HEALTH_PORT)
|
||||||
|
env:
|
||||||
|
- name: ADDRESS
|
||||||
|
value: /csi/csi.sock
|
||||||
|
- name: HEALTH_PORT
|
||||||
|
value: "9909"
|
||||||
|
image: quay.io/k8scsi/livenessprobe:v1.1.0
|
||||||
|
name: liveness-probe
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /csi
|
||||||
|
name: socket-dir
|
||||||
|
volumes:
|
||||||
|
- emptyDir: {}
|
||||||
|
name: socket-dir
|
20
go.mod
Normal file
20
go.mod
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
module csi-driver
|
||||||
|
|
||||||
|
go 1.21
|
||||||
|
|
||||||
|
toolchain go1.21.12
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/container-storage-interface/spec v1.10.0
|
||||||
|
google.golang.org/grpc v1.67.0
|
||||||
|
k8s.io/klog v1.0.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/kubernetes-csi/csi-test v1.1.1 // indirect
|
||||||
|
golang.org/x/net v0.28.0 // indirect
|
||||||
|
golang.org/x/sys v0.24.0 // indirect
|
||||||
|
golang.org/x/text v0.17.0 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect
|
||||||
|
google.golang.org/protobuf v1.34.2 // indirect
|
||||||
|
)
|
21
go.sum
Normal file
21
go.sum
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
github.com/container-storage-interface/spec v1.10.0 h1:YkzWPV39x+ZMTa6Ax2czJLLwpryrQ+dPesB34mrRMXA=
|
||||||
|
github.com/container-storage-interface/spec v1.10.0/go.mod h1:DtUvaQszPml1YJfIK7c00mlv6/g4wNMLanLgiUbKFRI=
|
||||||
|
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/kubernetes-csi/csi-test v1.1.1 h1:L4RPre34ICeoQW7ez4X5t0PnFKaKs8K5q0c1XOrvXEM=
|
||||||
|
github.com/kubernetes-csi/csi-test v1.1.1/go.mod h1:YxJ4UiuPWIhMBkxUKY5c267DyA0uDZ/MtAimhx/2TA0=
|
||||||
|
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||||
|
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||||
|
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||||
|
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||||
|
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||||
|
google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw=
|
||||||
|
google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
|
||||||
|
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||||
|
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||||
|
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
||||||
|
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
15
hack/boilerplate.go.txt
Normal file
15
hack/boilerplate.go.txt
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024 p5x.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
63
main.go
Normal file
63
main.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024 p5x.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"k8s.io/klog"
|
||||||
|
|
||||||
|
"csi-driver/pkg/csi"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
endpoint = flag.String("endpoint", "unix://tmp/csi.sock", "CSI Endpoint")
|
||||||
|
version = flag.Bool("version", false, "Print the version and exit.")
|
||||||
|
nodeID = flag.String("nodeid", "", "Node ID")
|
||||||
|
p5xEndpoint = flag.String("p5x-endpoint", "", "HTTPS endpoint of the P5x API server (e.g. https://p5x.example.local)")
|
||||||
|
p5xToken = flag.String("p5x-token", "", "P5x API access token")
|
||||||
|
p5xPort = flag.Int64("p5x-port", 3450, "P5x API port")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *version {
|
||||||
|
info, err := csi.GetVersionJSON()
|
||||||
|
if err != nil {
|
||||||
|
klog.Fatalln(err)
|
||||||
|
}
|
||||||
|
fmt.Println(info)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
if *nodeID == "" {
|
||||||
|
klog.Fatalln("--nodeid must be provided")
|
||||||
|
}
|
||||||
|
if *p5xEndpoint == "" {
|
||||||
|
klog.Fatalln("--p5x-endpoint must be provided")
|
||||||
|
}
|
||||||
|
if *p5xToken == "" {
|
||||||
|
klog.Fatalln("--p5x-token must be provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
drv := csi.NewDriver(*endpoint, *nodeID, *p5xEndpoint, *p5xToken, *p5xPort)
|
||||||
|
if err := drv.Run(); err != nil {
|
||||||
|
klog.Fatalln(err)
|
||||||
|
}
|
||||||
|
}
|
167
pkg/csi/controller.go
Normal file
167
pkg/csi/controller.go
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024 p5x.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package csi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
controllerCaps = []csi.ControllerServiceCapability_RPC_Type{
|
||||||
|
csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type controllerService struct {
|
||||||
|
p5x *p5xApi
|
||||||
|
csi.UnimplementedControllerServer
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ csi.ControllerServer = &controllerService{}
|
||||||
|
|
||||||
|
func newControllerService(p5x *p5xApi) controllerService {
|
||||||
|
return controllerService{
|
||||||
|
p5x: p5x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateVolume creates a volume
|
||||||
|
func (d *controllerService) CreateVolume(ctx context.Context, request *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) {
|
||||||
|
if len(request.Name) == 0 {
|
||||||
|
return nil, status.Error(codes.InvalidArgument, "Volume Name cannot be empty")
|
||||||
|
}
|
||||||
|
if request.VolumeCapabilities == nil {
|
||||||
|
return nil, status.Error(codes.InvalidArgument, "Volume Capabilities cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
requiredCap := request.CapacityRange.GetRequiredBytes()
|
||||||
|
|
||||||
|
vol, err := d.p5x.CreateVolume(request.Name, requiredCap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
csiVol := csi.Volume{
|
||||||
|
VolumeId: vol.Name,
|
||||||
|
CapacityBytes: vol.SizeInBytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &csi.CreateVolumeResponse{Volume: &csiVol}, nil
|
||||||
|
|
||||||
|
// volCtx := make(map[string]string)
|
||||||
|
// for k, v := range request.Parameters {
|
||||||
|
// volCtx[k] = v
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// volCtx["subPath"] = request.Name
|
||||||
|
//
|
||||||
|
// volume := csi.Volume{
|
||||||
|
// VolumeId: request.Name,
|
||||||
|
// CapacityBytes: requiredCap,
|
||||||
|
// VolumeContext: volCtx,
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteVolume deletes a volume
|
||||||
|
func (d *controllerService) DeleteVolume(ctx context.Context, request *csi.DeleteVolumeRequest) (*csi.DeleteVolumeResponse, error) {
|
||||||
|
vol, err := d.p5x.GetVolumeByName(request.VolumeId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.p5x.DeleteVolume(vol)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &csi.DeleteVolumeResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ControllerGetCapabilities get controller capabilities
|
||||||
|
func (d *controllerService) ControllerGetCapabilities(ctx context.Context, request *csi.ControllerGetCapabilitiesRequest) (*csi.ControllerGetCapabilitiesResponse, error) {
|
||||||
|
var caps []*csi.ControllerServiceCapability
|
||||||
|
for _, cap := range controllerCaps {
|
||||||
|
c := &csi.ControllerServiceCapability{
|
||||||
|
Type: &csi.ControllerServiceCapability_Rpc{
|
||||||
|
Rpc: &csi.ControllerServiceCapability_RPC{
|
||||||
|
Type: cap,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
caps = append(caps, c)
|
||||||
|
}
|
||||||
|
return &csi.ControllerGetCapabilitiesResponse{Capabilities: caps}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ControllerPublishVolume publish a volume
|
||||||
|
func (d *controllerService) ControllerPublishVolume(ctx context.Context, request *csi.ControllerPublishVolumeRequest) (*csi.ControllerPublishVolumeResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ControllerUnpublishVolume unpublish a volume
|
||||||
|
func (d *controllerService) ControllerUnpublishVolume(ctx context.Context, request *csi.ControllerUnpublishVolumeRequest) (*csi.ControllerUnpublishVolumeResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateVolumeCapabilities validate volume capabilities
|
||||||
|
func (d *controllerService) ValidateVolumeCapabilities(ctx context.Context, request *csi.ValidateVolumeCapabilitiesRequest) (*csi.ValidateVolumeCapabilitiesResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListVolumes list volumes
|
||||||
|
func (d *controllerService) ListVolumes(ctx context.Context, request *csi.ListVolumesRequest) (*csi.ListVolumesResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCapacity get capacity
|
||||||
|
func (d *controllerService) GetCapacity(ctx context.Context, request *csi.GetCapacityRequest) (*csi.GetCapacityResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSnapshot create a snapshot
|
||||||
|
func (d *controllerService) CreateSnapshot(ctx context.Context, request *csi.CreateSnapshotRequest) (*csi.CreateSnapshotResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteSnapshot delete a snapshot
|
||||||
|
func (d *controllerService) DeleteSnapshot(ctx context.Context, request *csi.DeleteSnapshotRequest) (*csi.DeleteSnapshotResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListSnapshots list snapshots
|
||||||
|
func (d *controllerService) ListSnapshots(ctx context.Context, request *csi.ListSnapshotsRequest) (*csi.ListSnapshotsResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ControllerExpandVolume expand a volume
|
||||||
|
func (d *controllerService) ControllerExpandVolume(ctx context.Context, request *csi.ControllerExpandVolumeRequest) (*csi.ControllerExpandVolumeResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ControllerGetVolume get a volume
|
||||||
|
func (d *controllerService) ControllerGetVolume(ctx context.Context, request *csi.ControllerGetVolumeRequest) (*csi.ControllerGetVolumeResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ControllerModifyVolume modify a volume
|
||||||
|
func (d *controllerService) ControllerModifyVolume(ctx context.Context, request *csi.ControllerModifyVolumeRequest) (*csi.ControllerModifyVolumeResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "")
|
||||||
|
}
|
121
pkg/csi/driver.go
Normal file
121
pkg/csi/driver.go
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024 p5x.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package csi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
csi "github.com/container-storage-interface/spec/lib/go/csi"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"k8s.io/klog"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DriverName to be registered
|
||||||
|
DriverName = "p5x"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Driver struct {
|
||||||
|
controllerService
|
||||||
|
nodeService
|
||||||
|
|
||||||
|
srv *grpc.Server
|
||||||
|
endpoint string
|
||||||
|
|
||||||
|
P5x *p5xApi
|
||||||
|
|
||||||
|
csi.UnimplementedIdentityServer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDriver creates a new driver
|
||||||
|
func NewDriver(endpoint string, nodeID string, p5xEndpoint string, p5xToken string, p5xPort int64) *Driver {
|
||||||
|
klog.Infof("Driver: %v version %v commit %v date %v", DriverName, driverVersion, gitCommit, buildDate)
|
||||||
|
|
||||||
|
p5x := &p5xApi{
|
||||||
|
endpoint: p5xEndpoint,
|
||||||
|
token: p5xToken,
|
||||||
|
port: p5xPort,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Driver{
|
||||||
|
endpoint: endpoint,
|
||||||
|
controllerService: newControllerService(p5x),
|
||||||
|
nodeService: newNodeService(nodeID),
|
||||||
|
P5x: p5x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Driver) Run() error {
|
||||||
|
scheme, addr, err := ParseEndpoint(d.endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
listener, err := net.Listen(scheme, addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logErr := func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||||
|
resp, err := handler(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("GRPC error: %v", err)
|
||||||
|
}
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
opts := []grpc.ServerOption{
|
||||||
|
grpc.UnaryInterceptor(logErr),
|
||||||
|
}
|
||||||
|
d.srv = grpc.NewServer(opts...)
|
||||||
|
|
||||||
|
csi.RegisterIdentityServer(d.srv, d)
|
||||||
|
csi.RegisterControllerServer(d.srv, d)
|
||||||
|
csi.RegisterNodeServer(d.srv, d)
|
||||||
|
|
||||||
|
klog.Infof("Listening for connection on address: %#v", listener.Addr())
|
||||||
|
return d.srv.Serve(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseEndpoint(endpoint string) (string, string, error) {
|
||||||
|
u, err := url.Parse(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf("could not parse endpoint: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := path.Join(u.Host, filepath.FromSlash(u.Path))
|
||||||
|
|
||||||
|
scheme := strings.ToLower(u.Scheme)
|
||||||
|
switch scheme {
|
||||||
|
case "tcp":
|
||||||
|
case "unix":
|
||||||
|
addr = path.Join("/", addr)
|
||||||
|
if err := os.Remove(addr); err != nil && !os.IsNotExist(err) {
|
||||||
|
return "", "", fmt.Errorf("could not remove unix domain socket %q: %v", addr, err)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return "", "", fmt.Errorf("unsupported protocol: %s", scheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
return scheme, addr, nil
|
||||||
|
}
|
53
pkg/csi/identity.go
Normal file
53
pkg/csi/identity.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024 p5x.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package csi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetPluginInfo returns the name and version of the plugin
|
||||||
|
func (d *Driver) GetPluginInfo(ctx context.Context, request *csi.GetPluginInfoRequest) (*csi.GetPluginInfoResponse, error) {
|
||||||
|
resp := &csi.GetPluginInfoResponse{
|
||||||
|
Name: DriverName,
|
||||||
|
VendorVersion: "v1",
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPluginCapabilities returns the capabilities of the plugin
|
||||||
|
func (d *Driver) GetPluginCapabilities(ctx context.Context, request *csi.GetPluginCapabilitiesRequest) (*csi.GetPluginCapabilitiesResponse, error) {
|
||||||
|
resp := &csi.GetPluginCapabilitiesResponse{
|
||||||
|
Capabilities: []*csi.PluginCapability{
|
||||||
|
{
|
||||||
|
Type: &csi.PluginCapability_Service_{
|
||||||
|
Service: &csi.PluginCapability_Service{
|
||||||
|
Type: csi.PluginCapability_Service_CONTROLLER_SERVICE,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Probe returns the health and readiness of the plugin
|
||||||
|
func (d *Driver) Probe(ctx context.Context, request *csi.ProbeRequest) (*csi.ProbeResponse, error) {
|
||||||
|
return &csi.ProbeResponse{}, nil
|
||||||
|
}
|
168
pkg/csi/node.go
Normal file
168
pkg/csi/node.go
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024 p5x.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package csi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
volumeCaps = []csi.VolumeCapability_AccessMode{
|
||||||
|
{
|
||||||
|
Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type nodeService struct {
|
||||||
|
nodeID string
|
||||||
|
csi.UnimplementedNodeServer
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ csi.NodeServer = &nodeService{}
|
||||||
|
|
||||||
|
func newNodeService(nodeID string) nodeService {
|
||||||
|
return nodeService{
|
||||||
|
nodeID: nodeID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeStageVolume is called by the CO when a workload that wants to use the specified volume is placed (scheduled) on a node.
|
||||||
|
func (n *nodeService) NodeStageVolume(ctx context.Context, request *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeUnstageVolume is called by the CO when a workload that was using the specified volume is being moved to a different node.
|
||||||
|
func (n *nodeService) NodeUnstageVolume(ctx context.Context, request *csi.NodeUnstageVolumeRequest) (*csi.NodeUnstageVolumeResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodePublishVolume mounts the volume on the node.
|
||||||
|
func (n *nodeService) NodePublishVolume(ctx context.Context, request *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) {
|
||||||
|
volumeID := request.GetVolumeId()
|
||||||
|
if len(volumeID) == 0 {
|
||||||
|
return nil, status.Error(codes.InvalidArgument, "Volume id not provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
target := request.GetTargetPath()
|
||||||
|
if len(target) == 0 {
|
||||||
|
return nil, status.Error(codes.InvalidArgument, "Target path not provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
volCap := request.GetVolumeCapability()
|
||||||
|
if volCap == nil {
|
||||||
|
return nil, status.Error(codes.InvalidArgument, "Volume capability not provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isValidVolumeCapabilities([]*csi.VolumeCapability{volCap}) {
|
||||||
|
return nil, status.Error(codes.InvalidArgument, "Volume capability not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
readOnly := false
|
||||||
|
if request.GetReadonly() || request.VolumeCapability.AccessMode.GetMode() == csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY {
|
||||||
|
readOnly = true
|
||||||
|
}
|
||||||
|
|
||||||
|
options := make(map[string]string)
|
||||||
|
if m := volCap.GetMount(); m != nil {
|
||||||
|
for _, f := range m.MountFlags {
|
||||||
|
// get mountOptions from PV.spec.mountOptions
|
||||||
|
options[f] = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if readOnly {
|
||||||
|
// Todo add readonly in your mount options
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO modify your volume mount logic here
|
||||||
|
|
||||||
|
return &csi.NodePublishVolumeResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeUnpublishVolume unmount the volume from the target path
|
||||||
|
func (n *nodeService) NodeUnpublishVolume(ctx context.Context, request *csi.NodeUnpublishVolumeRequest) (*csi.NodeUnpublishVolumeResponse, error) {
|
||||||
|
target := request.GetTargetPath()
|
||||||
|
if len(target) == 0 {
|
||||||
|
return nil, status.Error(codes.InvalidArgument, "Target path not provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO modify your volume umount logic here
|
||||||
|
|
||||||
|
return &csi.NodeUnpublishVolumeResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeGetVolumeStats get the volume stats
|
||||||
|
func (n *nodeService) NodeGetVolumeStats(ctx context.Context, request *csi.NodeGetVolumeStatsRequest) (*csi.NodeGetVolumeStatsResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeExpandVolume expand the volume
|
||||||
|
func (n *nodeService) NodeExpandVolume(ctx context.Context, request *csi.NodeExpandVolumeRequest) (*csi.NodeExpandVolumeResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeGetCapabilities get the node capabilities
|
||||||
|
func (n *nodeService) NodeGetCapabilities(ctx context.Context, request *csi.NodeGetCapabilitiesRequest) (*csi.NodeGetCapabilitiesResponse, error) {
|
||||||
|
return &csi.NodeGetCapabilitiesResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeGetInfo get the node info
|
||||||
|
func (n *nodeService) NodeGetInfo(ctx context.Context, request *csi.NodeGetInfoRequest) (*csi.NodeGetInfoResponse, error) {
|
||||||
|
return &csi.NodeGetInfoResponse{NodeId: n.nodeID}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidVolumeCapabilities(volCaps []*csi.VolumeCapability) bool {
|
||||||
|
hasSupport := func(cap *csi.VolumeCapability) bool {
|
||||||
|
if csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER == cap.AccessMode.GetMode() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER == cap.AccessMode.GetMode() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY == cap.AccessMode.GetMode() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// for _, c := range volumeCaps {
|
||||||
|
// if c.GetMode() == cap.AccessMode.GetMode() {
|
||||||
|
// return true
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
foundAll := true
|
||||||
|
for _, c := range volCaps {
|
||||||
|
if !hasSupport(c) {
|
||||||
|
foundAll = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return foundAll
|
||||||
|
}
|
97
pkg/csi/p5x.go
Normal file
97
pkg/csi/p5x.go
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
package csi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
"io"
|
||||||
|
"k8s.io/klog"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type p5xApi struct {
|
||||||
|
endpoint string
|
||||||
|
port int64
|
||||||
|
token string
|
||||||
|
}
|
||||||
|
|
||||||
|
type p5xVolume struct {
|
||||||
|
VolumeId int64 `json:"volumeId"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
SizeInBytes int64 `json:"sizeInBytes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p5x *p5xApi) CreateVolume(name string, sizeInBytes int64) (*p5xVolume, error) {
|
||||||
|
vol := &p5xVolume{
|
||||||
|
VolumeId: 0,
|
||||||
|
Name: name,
|
||||||
|
SizeInBytes: sizeInBytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := json.Marshal(vol)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resBody, err := p5x.MakeRequest(http.MethodPost, "volumes", body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(resBody, vol)
|
||||||
|
klog.Info("Successfully created volume: ", vol)
|
||||||
|
|
||||||
|
return vol, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p5x *p5xApi) GetVolumeByName(name string) (*p5xVolume, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p5x *p5xApi) GetVolumeById(volumeId int64) (*p5xVolume, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p5x *p5xApi) DeleteVolume(volume *p5xVolume) error {
|
||||||
|
route := fmt.Sprintf("volumes/%s", volume.Name)
|
||||||
|
resBody, err := p5x.MakeRequest(http.MethodDelete, route, []byte(`{}`))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
klog.Infof("Successfully deleted volume %s: %s", volume.Name, resBody)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p5x *p5xApi) MakeRequest(method string, route string, body []byte) ([]byte, error) {
|
||||||
|
bodyReader := bytes.NewReader(body)
|
||||||
|
|
||||||
|
url := fmt.Sprintf("%s:%d/api/v1/%s", p5x.endpoint, p5x.port, route)
|
||||||
|
klog.Infof("p5x.MakeRequest: [%s] %s", method, url)
|
||||||
|
klog.Infof("p5x.MakeRequest: %s", body)
|
||||||
|
req, err := http.NewRequest(method, url, bodyReader)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("p5x.MakeRequest: could not create request: %s\n", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Accept", "application/json")
|
||||||
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", p5x.token))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
res, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("p5x.MakeRequest: error executing request: %s\n", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resBody, err := io.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("p5x.MakeRequest: could not read response body: %s\n", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
klog.Infof("p5x.MakeRequest: response body: %s\n", resBody)
|
||||||
|
return resBody, nil
|
||||||
|
}
|
62
pkg/csi/version.go
Normal file
62
pkg/csi/version.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024 p5x.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package csi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// These are set during build time via -ldflags
|
||||||
|
var (
|
||||||
|
driverVersion string
|
||||||
|
gitCommit string
|
||||||
|
buildDate string
|
||||||
|
)
|
||||||
|
|
||||||
|
// VersionInfo struct
|
||||||
|
type VersionInfo struct {
|
||||||
|
DriverVersion string
|
||||||
|
GitCommit string
|
||||||
|
BuildDate string
|
||||||
|
GoVersion string
|
||||||
|
Compiler string
|
||||||
|
Platform string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersion returns VersionInfo
|
||||||
|
func GetVersion() VersionInfo {
|
||||||
|
return VersionInfo{
|
||||||
|
DriverVersion: driverVersion,
|
||||||
|
GitCommit: gitCommit,
|
||||||
|
BuildDate: buildDate,
|
||||||
|
GoVersion: runtime.Version(),
|
||||||
|
Compiler: runtime.Compiler,
|
||||||
|
Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersionJSON returns version in JSON
|
||||||
|
func GetVersionJSON() (string, error) {
|
||||||
|
info := GetVersion()
|
||||||
|
marshalled, err := json.MarshalIndent(&info, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(marshalled), nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user