Finish TrueNAS CSI Driver
This commit is contained in:
@@ -4,3 +4,4 @@ metadata:
|
|||||||
name: secrets
|
name: secrets
|
||||||
resources:
|
resources:
|
||||||
- linkding.yaml
|
- linkding.yaml
|
||||||
|
- truenas-csi.yaml
|
||||||
|
|||||||
@@ -4,20 +4,20 @@ metadata:
|
|||||||
name: linkding
|
name: linkding
|
||||||
namespace: linkding
|
namespace: linkding
|
||||||
stringData:
|
stringData:
|
||||||
supe_user_name: ENC[AES256_GCM,data:vBUmSZzQnDMY9GfGbzanZXE=,iv:VD99G6KvmWNmQ/ciVCrnw/pinE/83/l3gC2fLCi+vSE=,tag:XyqtSQT+uhVCf6SsH15Pgg==,type:str]
|
supe_user_name: ENC[AES256_GCM,data:XvTjgXWqxeY7kTdEu4ez3/w=,iv:7v9BWmQpqnNYYdWPyD07xIcHoJAwkrGq11d2wP49j14=,tag:GyZtZme1DheHjNFuBp7nbA==,type:str]
|
||||||
supe_user_password: ENC[AES256_GCM,data:4Ktxmh8fogYKoxSfWpEUFAhpiFTvfRq4yJ/nekQJl9cBbbVy4UqTZqlVF3A=,iv:wjkkoih66NxSVAinDmv8enoyqMnhn/8+c3KwUoe0yMg=,tag:R6vk7hMsZwdE7V8VePVM0w==,type:str]
|
supe_user_password: ENC[AES256_GCM,data:ATUaLra8h2OFUP8DkRG5kvPqR+OZKzbGZRQ60ECrCTkh+//M81o0GBrX0Nc=,iv:UzKVJRYWjKhEs50GNkijG0XiPAkiGKXWtqHZSEFYEpY=,tag:ROali/QL3ihSyWgSXh091w==,type:str]
|
||||||
sops:
|
sops:
|
||||||
age:
|
age:
|
||||||
- recipient: age1lelpkv7u2xh5wezuwp09fmf9gsa8gp4rzy92jz0t203au82a7u5sutsjwa
|
- recipient: age1lelpkv7u2xh5wezuwp09fmf9gsa8gp4rzy92jz0t203au82a7u5sutsjwa
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFUGpwMm9WcHBnRU1aeUFY
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBocEdFTVhWT0dZUGZlTHhK
|
||||||
aktaV3BYdUZ1RjkrMGVwYnlFbU1IUmR6SEVZCllVbGk4MHdXWElNT2pVRytLcFQx
|
MjcxbFpFd3lydnJPMTV5T2pqblRVdnBZZ1FRCnFlV09oaFptY2JvTGVmZ3poQ2Nz
|
||||||
ck1BN2srd0V6NGNXdEh5eUowSjY2em8KLS0tICtBZzBxU0ZjSXFUWUVmQ0NyWDZH
|
cm1IME13djMwbHJraVhPOEpBN1FqOEkKLS0tIEV5SkN1OXkxZDkrNFRhSEhoRDZC
|
||||||
dHhjZEo2VDhQMVY5WVpzQ0sxRHNkcXcK/hogutoNEBHZeHzc07uj8W7PKeX09KTS
|
RzlpNytqZGJOYW1BU0hOdFEyV3RjeEkKIWRRXhJTevlTCnlhoV3xoP6Kwtqt+aaE
|
||||||
FrEPVxt3w2tbZ8LmY19vNv/EMFd8l9wttIzVDqmqmzTZASwJUVQN9w==
|
wZECZ5N9Gk8JehsLkv5ShYxqcuenC8Rg/0Lc9Pmp6xhgJgWwJJzl+w==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
lastmodified: "2026-04-21T14:19:39Z"
|
lastmodified: "2026-04-27T20:50:36Z"
|
||||||
mac: ENC[AES256_GCM,data:kYq1vulOhGvFDJVE0sclrJpkTHTUgiE9xD7Onr5fya51nJtm+KL38zl9FxcTlbi5H5wdyCoRgNq0vBycIenaCBm7pg19gDORlgu8WvOUNV9/CGZrahPnYSty72PNlMFvZ3+SIF3glZdOhKT2bTrVXbxLbccFPR+8MJT7ETaXeTo=,iv:mKF3NZBw3nqLILexxDWypFCVF8NHn8I/RzYI9rX2Pic=,tag:SJwnv6MjLvthQHqjZLUCvw==,type:str]
|
mac: ENC[AES256_GCM,data:lh8FgtmZI59b/lHNAW6ScWG4yE/63hBkAbwhoaPwQNRSOAgTGG0xy147zqO7R/dryQmgjNBiZU8tD9KOmqoKRYvi10BxHbnT83gR3IpKSx2dTZldw2Odp1y7MJxsiG646N/CqsEKP4+K7oP4GZT/ERrq03dDDhN3ZFdsxg4Xuu0=,iv:TIswHRnyihQrrBPozXUiZv8XjXiZGqptlf7ckxLWTJo=,tag:x6z5SE94x9Ewej3XjHcUyA==,type:str]
|
||||||
encrypted_regex: ^(data|stringData)$
|
encrypted_regex: ^(data|stringData)$
|
||||||
version: 3.12.2
|
version: 3.12.2
|
||||||
|
|||||||
22
apps/hydra/secrets/truenas-csi.yaml
Normal file
22
apps/hydra/secrets/truenas-csi.yaml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: truenas-csi-api-credentials
|
||||||
|
namespace: truenas-csi
|
||||||
|
stringData:
|
||||||
|
api-key: ENC[AES256_GCM,data:rLckxqJRQRrRf5t9r/9tkGau0Jmq0GWvIS6CuIb8DSa0p3PnmWZ8XxptPf0zYylcwVmcHTypU/rQXL1cVjovj61U,iv:nD50QitcpDVJ7Xrduqg4N75qa8m6Kei7LtDc5ZO0+fI=,tag:RFKG8rZ0HLQ+skJIzAV5NA==,type:str]
|
||||||
|
sops:
|
||||||
|
age:
|
||||||
|
- recipient: age1lelpkv7u2xh5wezuwp09fmf9gsa8gp4rzy92jz0t203au82a7u5sutsjwa
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVWldlbGNyK2lHUTFQUGI2
|
||||||
|
aWxVZERyYXRDYVEwVTRyVkorSG1sMkxnWkRZCk1NM0hPNEc1YjY2Y2lFL3lMUkFk
|
||||||
|
RmZYamhJSGFUc2hXQm9IMnJFdUZoRGMKLS0tIFZWM1FTSkZnU0NEd1YycnpnYVFQ
|
||||||
|
M2NsdTdjMlZvSUluU1d5TG1CMXdpcnMKQWmdbo9Clk7SGmD6AwXfcZnbbXKrMgti
|
||||||
|
q2Cn+ZRDvZEYwQtMp/ob8iwbrl9KrUURNq/1GkmCjy73fy+MzTCnCQ==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
lastmodified: "2026-04-27T20:50:36Z"
|
||||||
|
mac: ENC[AES256_GCM,data:Rc3fcgj1BZR+jK4pHmukqPfdsZuxv/31RFLQJ8oV8XvU3eN1eedaS9DPUPss3VyLSnc0hjwlcCkd/QwNxeAUg3rHgWt5tc5m2nxIcjuHyuTMpoXvQ3xzOzTfC/DsewpKHuGR6lfF74x4SFZrwoocZztMh6i930lzfBk4FV4q0/Q=,iv:gA+XJvUYYDpPmNRmoeJvcu/J0rFvWGU+umUnem5tcfI=,tag:92ArjqfdFjV3qtJn2bK+Jw==,type:str]
|
||||||
|
encrypted_regex: ^(data|stringData)$
|
||||||
|
version: 3.12.2
|
||||||
2
go.mod
2
go.mod
@@ -3,7 +3,7 @@ module danicos.dev/daniel/homelab
|
|||||||
go 1.26.2
|
go 1.26.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
danicos.dev/daniel/go-kube v1.5.1
|
danicos.dev/daniel/go-kube v1.9.0
|
||||||
github.com/fatih/color v1.19.0
|
github.com/fatih/color v1.19.0
|
||||||
github.com/fluxcd/helm-controller/api v1.5.4
|
github.com/fluxcd/helm-controller/api v1.5.4
|
||||||
github.com/fluxcd/kustomize-controller/api v1.8.3
|
github.com/fluxcd/kustomize-controller/api v1.8.3
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -1,5 +1,5 @@
|
|||||||
danicos.dev/daniel/go-kube v1.5.1 h1:EtKHQGu0I82Sl8Ud2Tj5qXELq9zDlkOWj5oSMfX96+8=
|
danicos.dev/daniel/go-kube v1.9.0 h1:agofABwT1oa/gaxV4Q/KvUvxz1iVr8aiAB+X41WSv8s=
|
||||||
danicos.dev/daniel/go-kube v1.5.1/go.mod h1:MBGwFBrGyqkEQ55mK0PP2TdKO1oQSih4hLiPjye+8Gg=
|
danicos.dev/daniel/go-kube v1.9.0/go.mod h1:MBGwFBrGyqkEQ55mK0PP2TdKO1oQSih4hLiPjye+8Gg=
|
||||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
|||||||
11
infrastructure/hydra/truenas-csi/CSIDriver.yaml
Normal file
11
infrastructure/hydra/truenas-csi/CSIDriver.yaml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: storage.k8s.io/v1
|
||||||
|
kind: CSIDriver
|
||||||
|
metadata:
|
||||||
|
name: csi.truenas.io
|
||||||
|
spec:
|
||||||
|
attachRequired: true
|
||||||
|
fsGroupPolicy: File
|
||||||
|
podInfoOnMount: true
|
||||||
|
volumeLifecycleModes:
|
||||||
|
- Ephemeral
|
||||||
|
- Persistent
|
||||||
12
infrastructure/hydra/truenas-csi/config.yaml
Normal file
12
infrastructure/hydra/truenas-csi/config.yaml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
defaultPool: datapool
|
||||||
|
iscsiIQNBase: iqn.net.ts.orca-uaru.apex-truenas
|
||||||
|
iscsiPortal: apex-truenas.orca-uaru.ts.net:3260
|
||||||
|
nfsServer: apex-truenas.orca-uaru.ts.net
|
||||||
|
truenasInsecure: "true"
|
||||||
|
truenasURL: wss://apex-truenas.orca-uaru.ts.net/api/current
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: truenas-csi
|
||||||
|
namespace: truenas-csi
|
||||||
12
infrastructure/hydra/truenas-csi/controller-binding.yaml
Normal file
12
infrastructure/hydra/truenas-csi/controller-binding.yaml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: truenas-csi-controller-role-binding
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: truenas-csi-controller-role
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: truenas-csi-controller-sa
|
||||||
|
namespace: truenas-csi
|
||||||
138
infrastructure/hydra/truenas-csi/controller-cluster-role.yaml
Normal file
138
infrastructure/hydra/truenas-csi/controller-cluster-role.yaml
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: truenas-csi-controller-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- persistentvolumes
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- create
|
||||||
|
- update
|
||||||
|
- patch
|
||||||
|
- delete
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- persistentvolumeclaims
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- update
|
||||||
|
- patch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- persistentvolumeclaims/status
|
||||||
|
verbs:
|
||||||
|
- update
|
||||||
|
- patch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- events
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- update
|
||||||
|
- patch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- nodes
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- pods
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- storage.k8s.io
|
||||||
|
resources:
|
||||||
|
- storageclasses
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- storage.k8s.io
|
||||||
|
resources:
|
||||||
|
- csinodes
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- storage.k8s.io
|
||||||
|
resources:
|
||||||
|
- volumeattachments
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- create
|
||||||
|
- update
|
||||||
|
- patch
|
||||||
|
- delete
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- storage.k8s.io
|
||||||
|
resources:
|
||||||
|
- volumeattachments/status
|
||||||
|
verbs:
|
||||||
|
- patch
|
||||||
|
- apiGroups:
|
||||||
|
- snapshot.storage.k8s.io
|
||||||
|
resources:
|
||||||
|
- volumesnapshots
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- update
|
||||||
|
- patch
|
||||||
|
- apiGroups:
|
||||||
|
- snapshot.storage.k8s.io
|
||||||
|
resources:
|
||||||
|
- volumesnapshots/status
|
||||||
|
verbs:
|
||||||
|
- update
|
||||||
|
- patch
|
||||||
|
- apiGroups:
|
||||||
|
- snapshot.storage.k8s.io
|
||||||
|
resources:
|
||||||
|
- volumesnapshotcontents
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- create
|
||||||
|
- update
|
||||||
|
- patch
|
||||||
|
- delete
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- snapshot.storage.k8s.io
|
||||||
|
resources:
|
||||||
|
- volumesnapshotcontents/status
|
||||||
|
verbs:
|
||||||
|
- update
|
||||||
|
- patch
|
||||||
|
- apiGroups:
|
||||||
|
- snapshot.storage.k8s.io
|
||||||
|
resources:
|
||||||
|
- volumesnapshotclasses
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
153
infrastructure/hydra/truenas-csi/controller-deployment.yaml
Normal file
153
infrastructure/hydra/truenas-csi/controller-deployment.yaml
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: truenas-csi-controller
|
||||||
|
name: truenas-csi-controller
|
||||||
|
namespace: truenas-csi
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: truenas-csi-controller
|
||||||
|
strategy: {}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: truenas-csi-controller
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- args:
|
||||||
|
- --endpoint=$(CSI_ENDPOINT)
|
||||||
|
- --node-id=$(NODE_ID)
|
||||||
|
- --mode=controller
|
||||||
|
- --v=4
|
||||||
|
env:
|
||||||
|
- name: CSI_ENDPOINT
|
||||||
|
value: unix:///csi/csi.sock
|
||||||
|
- name: TRUENAS_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
key: api-key
|
||||||
|
name: truenas-csi-api-credentials
|
||||||
|
- name: NODE_ID
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: spec.nodeName
|
||||||
|
- name: TRUENAS_DEFAULT_POOL
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
key: defaultPool
|
||||||
|
name: truenas-csi
|
||||||
|
- name: TRUENAS_INSECURE_SKIP_VERIFY
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
key: truenasInsecure
|
||||||
|
name: truenas-csi
|
||||||
|
- name: TRUENAS_ISCSI_IQN_BASE
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
key: iscsiIQNBase
|
||||||
|
name: truenas-csi
|
||||||
|
- name: TRUENAS_ISCSI_PORTAL
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
key: iscsiPortal
|
||||||
|
name: truenas-csi
|
||||||
|
- name: TRUENAS_NFS_SERVER
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
key: nfsServer
|
||||||
|
name: truenas-csi
|
||||||
|
- name: TRUENAS_URL
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
key: truenasURL
|
||||||
|
name: truenas-csi
|
||||||
|
image: ghcr.io/truenas/truenas-csi:latest
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
livenessProbe:
|
||||||
|
failureThreshold: 5
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 9808
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 3
|
||||||
|
name: csi-controller
|
||||||
|
resources: {}
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /csi
|
||||||
|
name: socket-dir
|
||||||
|
- args:
|
||||||
|
- --csi-address=$(ADDRESS)
|
||||||
|
- --v=5
|
||||||
|
- --feature-gates=Topology=true
|
||||||
|
- --extra-create-metadata
|
||||||
|
- --leader-election=true
|
||||||
|
- --default-fstype=ext4
|
||||||
|
- --timeout=60s
|
||||||
|
env:
|
||||||
|
- name: ADDRESS
|
||||||
|
value: /csi/csi.sock
|
||||||
|
image: registry.k8s.io/sig-storage/csi-provisioner:v6.1.1
|
||||||
|
name: csi-provisioner
|
||||||
|
resources: {}
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /csi
|
||||||
|
name: socket-dir
|
||||||
|
- args:
|
||||||
|
- --csi-address=$(ADDRESS)
|
||||||
|
- --v=5
|
||||||
|
- --leader-election=true
|
||||||
|
- --timeout=60s
|
||||||
|
env:
|
||||||
|
- name: ADDRESS
|
||||||
|
value: /csi/csi.sock
|
||||||
|
image: registry.k8s.io/sig-storage/csi-attacher:v4.11.0
|
||||||
|
name: csi-attacher
|
||||||
|
resources: {}
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /csi
|
||||||
|
name: socket-dir
|
||||||
|
- args:
|
||||||
|
- --csi-address=$(ADDRESS)
|
||||||
|
- --v=5
|
||||||
|
- --leader-election=true
|
||||||
|
- --timeout=60s
|
||||||
|
env:
|
||||||
|
- name: ADDRESS
|
||||||
|
value: /csi/csi.sock
|
||||||
|
image: registry.k8s.io/sig-storage/csi-snapshotter:v8.5.0
|
||||||
|
name: csi-snapshotter
|
||||||
|
resources: {}
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /csi
|
||||||
|
name: socket-dir
|
||||||
|
- args:
|
||||||
|
- --csi-address=$(ADDRESS)
|
||||||
|
- --v=5
|
||||||
|
- --leader-election=true
|
||||||
|
- --timeout=60s
|
||||||
|
env:
|
||||||
|
- name: ADDRESS
|
||||||
|
value: /csi/csi.sock
|
||||||
|
image: registry.k8s.io/sig-storage/csi-resizer:v2.1.0
|
||||||
|
name: csi-resizer
|
||||||
|
resources: {}
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /csi
|
||||||
|
name: socket-dir
|
||||||
|
- args:
|
||||||
|
- --csi-address=/csi/csi.sock
|
||||||
|
- --health-port=9808
|
||||||
|
image: registry.k8s.io/sig-storage/livenessprobe:v2.18.0
|
||||||
|
name: liveness-probe
|
||||||
|
resources: {}
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /csi
|
||||||
|
name: socket-dir
|
||||||
|
serviceAccountName: truenas-csi-controller-sa
|
||||||
|
volumes:
|
||||||
|
- emptyDir: {}
|
||||||
|
name: socket-dir
|
||||||
|
status: {}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: truenas-csi-controller-sa
|
||||||
|
namespace: truenas-csi
|
||||||
@@ -4,4 +4,14 @@ metadata:
|
|||||||
name: truenas-csi
|
name: truenas-csi
|
||||||
namespace: truenas-csi
|
namespace: truenas-csi
|
||||||
resources:
|
resources:
|
||||||
|
- node-service-account.yaml
|
||||||
|
- node-binding.yaml
|
||||||
|
- node-deamonset.yaml
|
||||||
|
- config.yaml
|
||||||
- namespace.yaml
|
- namespace.yaml
|
||||||
|
- controller-deployment.yaml
|
||||||
|
- controller-cluster-role.yaml
|
||||||
|
- controller-binding.yaml
|
||||||
|
- node-cluster-role.yaml
|
||||||
|
- CSIDriver.yaml
|
||||||
|
- controller-service-account.yaml
|
||||||
|
|||||||
12
infrastructure/hydra/truenas-csi/node-binding.yaml
Normal file
12
infrastructure/hydra/truenas-csi/node-binding.yaml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: truenas-csi-node-role-binding
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: truenas-csi-node-role
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: truenas-csi-node-sa
|
||||||
|
namespace: truenas-csi
|
||||||
27
infrastructure/hydra/truenas-csi/node-cluster-role.yaml
Normal file
27
infrastructure/hydra/truenas-csi/node-cluster-role.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: truenas-csi-node-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- nodes
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- pods
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- storage.k8s.io
|
||||||
|
resources:
|
||||||
|
- volumeattachments
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
170
infrastructure/hydra/truenas-csi/node-deamonset.yaml
Normal file
170
infrastructure/hydra/truenas-csi/node-deamonset.yaml
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: DaemonSet
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: truenas-csi-node
|
||||||
|
name: truenas-csi-node
|
||||||
|
namespace: truenas-csi
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: truenas-csi-node
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: truenas-csi-node
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- args:
|
||||||
|
- --endpoint=$(CSI_ENDPOINT)
|
||||||
|
- --node-id=$(NODE_ID)
|
||||||
|
- --mode=node
|
||||||
|
- --v=4
|
||||||
|
env:
|
||||||
|
- name: CSI_ENDPOINT
|
||||||
|
value: unix:///csi/csi.sock
|
||||||
|
- name: TRUENAS_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
key: api-key
|
||||||
|
name: truenas-csi-api-credentials
|
||||||
|
- name: NODE_ID
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: spec.nodeName
|
||||||
|
- name: TRUENAS_DEFAULT_POOL
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
key: defaultPool
|
||||||
|
name: truenas-csi
|
||||||
|
- name: TRUENAS_INSECURE_SKIP_VERIFY
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
key: truenasInsecure
|
||||||
|
name: truenas-csi
|
||||||
|
- name: TRUENAS_ISCSI_IQN_BASE
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
key: iscsiIQNBase
|
||||||
|
name: truenas-csi
|
||||||
|
- name: TRUENAS_ISCSI_PORTAL
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
key: iscsiPortal
|
||||||
|
name: truenas-csi
|
||||||
|
- name: TRUENAS_NFS_SERVER
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
key: nfsServer
|
||||||
|
name: truenas-csi
|
||||||
|
- name: TRUENAS_URL
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
key: truenasURL
|
||||||
|
name: truenas-csi
|
||||||
|
image: ghcr.io/truenas/truenas-csi:latest
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
lifecycle:
|
||||||
|
postStart:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- mkdir -p /run/lock/iscsi && mv /usr/sbin/iscsiadm /usr/sbin/iscsiadm.orig
|
||||||
|
2>/dev/null; printf '#!/bin/sh\nnsenter --mount=/host/proc/1/ns/mnt
|
||||||
|
-- /usr/sbin/iscsiadm "$@"\n' > /usr/sbin/iscsiadm && chmod +x /usr/sbin/iscsiadm
|
||||||
|
livenessProbe:
|
||||||
|
failureThreshold: 5
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 9808
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 3
|
||||||
|
name: csi-node
|
||||||
|
resources: {}
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /csi
|
||||||
|
name: plugin-dir
|
||||||
|
- mountPath: /var/lib/kubelet
|
||||||
|
name: kubelet-dir
|
||||||
|
- mountPath: /dev
|
||||||
|
name: device-dir
|
||||||
|
- mountPath: /etc/iscsi
|
||||||
|
mountPropagation: Bidirectional
|
||||||
|
name: iscsi-dir
|
||||||
|
- mountPath: /var/lib/iscsi
|
||||||
|
mountPropagation: Bidirectional
|
||||||
|
name: iscsi-lib
|
||||||
|
- mountPath: /
|
||||||
|
mountPropagation: Bidirectional
|
||||||
|
name: host-root
|
||||||
|
- 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/plugins/csi.truenas.io/csi.sock
|
||||||
|
image: registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.16.0
|
||||||
|
name: csi-node-driver-registrar
|
||||||
|
resources: {}
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /csi
|
||||||
|
name: plugin-dir
|
||||||
|
- mountPath: /registration
|
||||||
|
name: registration-dir
|
||||||
|
- args:
|
||||||
|
- --csi-address=/csi/csi.sock
|
||||||
|
- --health-port=9808
|
||||||
|
image: registry.k8s.io/sig-storage/livenessprobe:v2.18.0
|
||||||
|
name: liveness-probe
|
||||||
|
resources: {}
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /csi
|
||||||
|
name: plugin-dir
|
||||||
|
hostIPC: true
|
||||||
|
hostNetwork: true
|
||||||
|
hostPID: true
|
||||||
|
priorityClassName: system-node-critical
|
||||||
|
serviceAccountName: truenas-csi-node-sa
|
||||||
|
tolerations:
|
||||||
|
- operator: Exists
|
||||||
|
volumes:
|
||||||
|
- hostPath:
|
||||||
|
path: /var/lib/kubelet/plugins_registry/
|
||||||
|
type: DirectoryOrCreate
|
||||||
|
name: registration-dir
|
||||||
|
- hostPath:
|
||||||
|
path: /var/lib/kubelet/plugins/csi.truenas.io/
|
||||||
|
type: DirectoryOrCreate
|
||||||
|
name: plugin-dir
|
||||||
|
- hostPath:
|
||||||
|
path: /var/lib/kubelet
|
||||||
|
type: Directory
|
||||||
|
name: kubelet-dir
|
||||||
|
- hostPath:
|
||||||
|
path: /dev
|
||||||
|
name: device-dir
|
||||||
|
- hostPath:
|
||||||
|
path: /etc/iscsi
|
||||||
|
type: Directory
|
||||||
|
name: iscsi-dir
|
||||||
|
- hostPath:
|
||||||
|
path: /var/lib/iscsi
|
||||||
|
type: DirectoryOrCreate
|
||||||
|
name: iscsi-lib
|
||||||
|
- hostPath:
|
||||||
|
path: /
|
||||||
|
type: Directory
|
||||||
|
name: host-root
|
||||||
|
updateStrategy: {}
|
||||||
|
status:
|
||||||
|
currentNumberScheduled: 0
|
||||||
|
desiredNumberScheduled: 0
|
||||||
|
numberMisscheduled: 0
|
||||||
|
numberReady: 0
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: truenas-csi-node-sa
|
||||||
|
namespace: truenas-csi
|
||||||
@@ -3,6 +3,7 @@ package flux
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"danicos.dev/daniel/go-kube/pkg/flux"
|
||||||
"danicos.dev/daniel/go-kube/pkg/kube"
|
"danicos.dev/daniel/go-kube/pkg/kube"
|
||||||
"danicos.dev/daniel/go-kube/pkg/stack"
|
"danicos.dev/daniel/go-kube/pkg/stack"
|
||||||
"danicos.dev/daniel/homelab/pkg/root"
|
"danicos.dev/daniel/homelab/pkg/root"
|
||||||
@@ -44,7 +45,7 @@ func kuztomization(meta kube.Metadata, path string) kz.Kustomization {
|
|||||||
Path: path,
|
Path: path,
|
||||||
Prune: true,
|
Prune: true,
|
||||||
}
|
}
|
||||||
return kube.NewFluxKustomization(meta, spec)
|
return flux.NewFluxKustomization(meta, spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
func durMin(d int64) meta.Duration {
|
func durMin(d int64) meta.Duration {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package longhorn
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"danicos.dev/daniel/go-kube/pkg/flux"
|
||||||
"danicos.dev/daniel/go-kube/pkg/kube"
|
"danicos.dev/daniel/go-kube/pkg/kube"
|
||||||
"danicos.dev/daniel/go-kube/pkg/stack"
|
"danicos.dev/daniel/go-kube/pkg/stack"
|
||||||
"danicos.dev/daniel/homelab/pkg/root"
|
"danicos.dev/daniel/homelab/pkg/root"
|
||||||
@@ -32,7 +33,7 @@ func LonghornHelmSource() source.HelmRepository {
|
|||||||
Interval: durHour(root.FLUX_HELM_MONITORING_INTERVAL),
|
Interval: durHour(root.FLUX_HELM_MONITORING_INTERVAL),
|
||||||
URL: root.HELM_LONGHORN_URL,
|
URL: root.HELM_LONGHORN_URL,
|
||||||
}
|
}
|
||||||
return kube.NewFluxHelmRepositorySource(meta, spec)
|
return flux.NewFluxHelmRepositorySource(meta, spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
func LonghornHelmRelease() helm.HelmRelease {
|
func LonghornHelmRelease() helm.HelmRelease {
|
||||||
@@ -45,7 +46,7 @@ func LonghornHelmRelease() helm.HelmRelease {
|
|||||||
Version: root.HELM_LONGHORN_CHART_VERSION,
|
Version: root.HELM_LONGHORN_CHART_VERSION,
|
||||||
Interval: &interval,
|
Interval: &interval,
|
||||||
SourceRef: helm.CrossNamespaceObjectReference{
|
SourceRef: helm.CrossNamespaceObjectReference{
|
||||||
Kind: kube.FluxHelmRepositoryMeta.Kind,
|
Kind: flux.MetaHelmRepository.Kind,
|
||||||
Name: meta.Meta().Name,
|
Name: meta.Meta().Name,
|
||||||
Namespace: Namespace.Name,
|
Namespace: Namespace.Name,
|
||||||
},
|
},
|
||||||
@@ -58,7 +59,7 @@ func LonghornHelmRelease() helm.HelmRelease {
|
|||||||
CRDs: helm.CreateReplace,
|
CRDs: helm.CreateReplace,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return kube.NewFluxHelmRelease(meta, spec)
|
return flux.NewFluxHelmRelease(meta, spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
func durHour(d int64) metav1.Duration {
|
func durHour(d int64) metav1.Duration {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"danicos.dev/daniel/go-kube/pkg/flux"
|
||||||
"danicos.dev/daniel/go-kube/pkg/kube"
|
"danicos.dev/daniel/go-kube/pkg/kube"
|
||||||
"danicos.dev/daniel/go-kube/pkg/stack"
|
"danicos.dev/daniel/go-kube/pkg/stack"
|
||||||
"danicos.dev/daniel/homelab/pkg/root"
|
"danicos.dev/daniel/homelab/pkg/root"
|
||||||
@@ -36,7 +37,7 @@ func PrometheusHelmSource() source.HelmRepository {
|
|||||||
Interval: durHour(root.FLUX_HELM_MONITORING_INTERVAL),
|
Interval: durHour(root.FLUX_HELM_MONITORING_INTERVAL),
|
||||||
URL: root.HELM_PROMETHEUS_URL,
|
URL: root.HELM_PROMETHEUS_URL,
|
||||||
}
|
}
|
||||||
return kube.NewFluxHelmRepositorySource(meta, spec)
|
return flux.NewFluxHelmRepositorySource(meta, spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
func PrometheusRelease() helm.HelmRelease {
|
func PrometheusRelease() helm.HelmRelease {
|
||||||
@@ -65,7 +66,7 @@ func PrometheusRelease() helm.HelmRelease {
|
|||||||
Version: root.HELM_PROMETHEUS_CHART_VERSION,
|
Version: root.HELM_PROMETHEUS_CHART_VERSION,
|
||||||
Interval: &interval,
|
Interval: &interval,
|
||||||
SourceRef: helm.CrossNamespaceObjectReference{
|
SourceRef: helm.CrossNamespaceObjectReference{
|
||||||
Kind: kube.FluxHelmRepositoryMeta.Kind,
|
Kind: flux.MetaHelmRepository.Kind,
|
||||||
Name: meta.Meta().Name,
|
Name: meta.Meta().Name,
|
||||||
Namespace: Namespace.Name,
|
Namespace: Namespace.Name,
|
||||||
},
|
},
|
||||||
@@ -88,7 +89,7 @@ func PrometheusRelease() helm.HelmRelease {
|
|||||||
},
|
},
|
||||||
Values: &apiextensionsv1.JSON{Raw: raw},
|
Values: &apiextensionsv1.JSON{Raw: raw},
|
||||||
}
|
}
|
||||||
return kube.NewFluxHelmRelease(meta, spec)
|
return flux.NewFluxHelmRelease(meta, spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
func durHour(d int64) metav1.Duration {
|
func durHour(d int64) metav1.Duration {
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ var Linkding = Service{
|
|||||||
SecurityContextID: 33, // www-data user, group and FS ID
|
SecurityContextID: 33, // www-data user, group and FS ID
|
||||||
}
|
}
|
||||||
|
|
||||||
var Longhorn = "longhorn"
|
var (
|
||||||
var Monitoring = "monitoring"
|
Longhorn = "longhorn"
|
||||||
var TrueNAS_CSI = "truenas-csi"
|
Monitoring = "monitoring"
|
||||||
|
TrueNAS_CSI = "truenas-csi"
|
||||||
|
TrueNASURL = "apex-truenas.orca-uaru.ts.net"
|
||||||
|
)
|
||||||
|
|||||||
39
pkg/truenas/rbac.go
Normal file
39
pkg/truenas/rbac.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package truenas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"danicos.dev/daniel/go-kube/pkg/kube"
|
||||||
|
"danicos.dev/daniel/homelab/pkg/root"
|
||||||
|
|
||||||
|
rbac "k8s.io/api/rbac/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func controllerClusterRole() rbac.ClusterRole {
|
||||||
|
verbsReadUpdate := append(kube.VerbsRead(), kube.VerbsMutate()...)
|
||||||
|
rules := []rbac.PolicyRule{
|
||||||
|
kube.PolicyRule(kube.APIGroupCore, kube.ResourcePVs, kube.VerbsAll()),
|
||||||
|
kube.PolicyRule(kube.APIGroupCore, kube.ResourcePVCs, verbsReadUpdate),
|
||||||
|
kube.PolicyRule(kube.APIGroupCore, kube.ResourcePVCsStatus, kube.VerbsMutate()),
|
||||||
|
kube.PolicyRule(kube.APIGroupCore, kube.ResourceEvents, verbsReadUpdate),
|
||||||
|
kube.PolicyRule(kube.APIGroupCore, kube.ResourceNodes, kube.VerbsRead()),
|
||||||
|
kube.PolicyRule(kube.APIGroupCore, kube.ResourcePods, kube.VerbsRead()),
|
||||||
|
kube.PolicyRule(kube.APIGroupStorage, kube.ResourceStorageClasses, kube.VerbsRead()),
|
||||||
|
kube.PolicyRule(kube.APIGroupStorage, kube.ResourceCSINodes, kube.VerbsRead()),
|
||||||
|
kube.PolicyRule(kube.APIGroupStorage, kube.ResourceVolumeAttachments, kube.VerbsAll()),
|
||||||
|
kube.PolicyRule(kube.APIGroupStorage, kube.ResourceVolumeAttachmentsStatus, []string{kube.VerbPatch}),
|
||||||
|
kube.PolicyRule(kube.APIGroupSnapshot, kube.ResourceVolumeSnapshots, verbsReadUpdate),
|
||||||
|
kube.PolicyRule(kube.APIGroupSnapshot, kube.ResourceVolumeSnapshotsStatus, kube.VerbsMutate()),
|
||||||
|
kube.PolicyRule(kube.APIGroupSnapshot, kube.ResourceVolumeSnapshotContents, kube.VerbsAll()),
|
||||||
|
kube.PolicyRule(kube.APIGroupSnapshot, kube.ResourceVolumeSnapshotContentsStatus, kube.VerbsMutate()),
|
||||||
|
kube.PolicyRule(kube.APIGroupSnapshot, kube.ResourceVolumeSnapshotClases, kube.VerbsRead()),
|
||||||
|
}
|
||||||
|
return kube.ClusterRole(root.TrueNAS_CSI+"-controller-role", rules)
|
||||||
|
}
|
||||||
|
|
||||||
|
func nodeClusterRole() rbac.ClusterRole {
|
||||||
|
rules := []rbac.PolicyRule{
|
||||||
|
kube.PolicyRule(kube.APIGroupCore, kube.ResourceNodes, []string{kube.VerbGet}),
|
||||||
|
kube.PolicyRule(kube.APIGroupCore, kube.ResourcePods, kube.VerbsRead()),
|
||||||
|
kube.PolicyRule(kube.APIGroupStorage, kube.ResourceVolumeAttachments, kube.VerbsRead()),
|
||||||
|
}
|
||||||
|
return kube.ClusterRole(root.TrueNAS_CSI+"-node-role", rules)
|
||||||
|
}
|
||||||
@@ -1,24 +1,440 @@
|
|||||||
package truenas
|
package truenas
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"danicos.dev/daniel/go-kube/pkg/kube"
|
"danicos.dev/daniel/go-kube/pkg/kube"
|
||||||
"danicos.dev/daniel/go-kube/pkg/stack"
|
"danicos.dev/daniel/go-kube/pkg/stack"
|
||||||
"danicos.dev/daniel/homelab/pkg/root"
|
"danicos.dev/daniel/homelab/pkg/root"
|
||||||
|
apps "k8s.io/api/apps/v1"
|
||||||
|
core "k8s.io/api/core/v1"
|
||||||
|
storage "k8s.io/api/storage/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
)
|
)
|
||||||
|
|
||||||
var meta kube.Metadata
|
var Secret = struct {
|
||||||
var Namespace = kube.Namespace(root.TrueNAS_CSI)
|
Name string
|
||||||
|
APIKey string
|
||||||
|
}{
|
||||||
|
Name: root.TrueNAS_CSI + "-api-credentials",
|
||||||
|
APIKey: "api-key",
|
||||||
|
}
|
||||||
|
|
||||||
|
var Config = struct {
|
||||||
|
TruenasURL string
|
||||||
|
TrueNASInsecure string
|
||||||
|
DefaultPool string
|
||||||
|
NFSServer string
|
||||||
|
ISCSIPortal string
|
||||||
|
ISCSIIQNBase string
|
||||||
|
}{
|
||||||
|
TruenasURL: "truenasURL",
|
||||||
|
TrueNASInsecure: "truenasInsecure",
|
||||||
|
DefaultPool: "defaultPool",
|
||||||
|
NFSServer: "nfsServer",
|
||||||
|
ISCSIPortal: "iscsiPortal",
|
||||||
|
ISCSIIQNBase: "iscsiIQNBase",
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Origin: https://github.com/truenas/truenas-csi
|
||||||
|
Namespace core.Namespace
|
||||||
|
meta kube.Metadata
|
||||||
|
controllerSA core.ServiceAccount
|
||||||
|
nodeSA core.ServiceAccount
|
||||||
|
config core.ConfigMap
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
Namespace = kube.Namespace(root.TrueNAS_CSI)
|
||||||
meta = kube.NewMetadata(root.TrueNAS_CSI, Namespace)
|
meta = kube.NewMetadata(root.TrueNAS_CSI, Namespace)
|
||||||
|
controllerSA = kube.ServiceAccount(root.TrueNAS_CSI+"-controller", Namespace)
|
||||||
|
nodeSA = kube.ServiceAccount(root.TrueNAS_CSI+"-node", Namespace)
|
||||||
|
split := strings.Split(root.TrueNASURL, ".")
|
||||||
|
slices.Reverse(split)
|
||||||
|
config = core.ConfigMap{
|
||||||
|
TypeMeta: kube.ConfigMapMeta,
|
||||||
|
ObjectMeta: meta.Meta(),
|
||||||
|
Data: map[string]string{
|
||||||
|
Config.TruenasURL: fmt.Sprintf("wss://%s/api/current", root.TrueNASURL),
|
||||||
|
Config.TrueNASInsecure: "true",
|
||||||
|
Config.DefaultPool: "datapool",
|
||||||
|
Config.NFSServer: root.TrueNASURL,
|
||||||
|
Config.ISCSIPortal: fmt.Sprintf("%s:3260", root.TrueNASURL),
|
||||||
|
Config.ISCSIIQNBase: fmt.Sprintf("iqn.%s", strings.Join(split, ".")),
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Stack() stack.Stack {
|
func Stack() stack.Stack {
|
||||||
|
controllerRole := controllerClusterRole()
|
||||||
|
nodeRole := nodeClusterRole()
|
||||||
kz := kube.NewKuztomizedStack(
|
kz := kube.NewKuztomizedStack(
|
||||||
meta,
|
meta,
|
||||||
map[string]any{
|
map[string]any{
|
||||||
"namespace": Namespace,
|
"namespace": Namespace,
|
||||||
|
"controller-deployment": controllerDeployment(),
|
||||||
|
"controller-service-account": controllerSA,
|
||||||
|
"controller-cluster-role": controllerRole,
|
||||||
|
"controller-binding": kube.ClusterRoleBinding(controllerRole.Name+"-binding", controllerSA, controllerRole),
|
||||||
|
"node-service-account": nodeSA,
|
||||||
|
"node-cluster-role": nodeRole,
|
||||||
|
"node-binding": kube.ClusterRoleBinding(nodeRole.Name+"-binding", nodeSA, nodeRole),
|
||||||
|
"node-deamonset": nodeCSI(),
|
||||||
|
"CSIDriver": CSIDriver("csi.truenas.io"),
|
||||||
|
"config": config,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
return kz.Stack(root.TrueNAS_CSI)
|
return kz.Stack(root.TrueNAS_CSI)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func controllerDeployment() apps.Deployment {
|
||||||
|
meta := kube.NewMetadata(root.TrueNAS_CSI+"-controller", Namespace)
|
||||||
|
vol := core.Volume{
|
||||||
|
Name: "socket-dir",
|
||||||
|
VolumeSource: core.VolumeSource{
|
||||||
|
EmptyDir: &core.EmptyDirVolumeSource{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
spec := core.PodSpec{
|
||||||
|
ServiceAccountName: controllerSA.Name,
|
||||||
|
Containers: []core.Container{
|
||||||
|
{
|
||||||
|
Name: "csi-controller",
|
||||||
|
Image: "ghcr.io/truenas/truenas-csi:latest",
|
||||||
|
ImagePullPolicy: core.PullIfNotPresent,
|
||||||
|
Args: []string{
|
||||||
|
"--endpoint=$(CSI_ENDPOINT)",
|
||||||
|
"--node-id=$(NODE_ID)",
|
||||||
|
"--mode=controller",
|
||||||
|
"--v=4",
|
||||||
|
},
|
||||||
|
Env: controllerEnv(),
|
||||||
|
VolumeMounts: []core.VolumeMount{{Name: vol.Name, MountPath: "/csi"}},
|
||||||
|
LivenessProbe: &core.Probe{
|
||||||
|
ProbeHandler: core.ProbeHandler{
|
||||||
|
HTTPGet: &core.HTTPGetAction{
|
||||||
|
Path: "/healthz",
|
||||||
|
Port: intstr.FromInt(9808),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
InitialDelaySeconds: 10,
|
||||||
|
TimeoutSeconds: 3,
|
||||||
|
PeriodSeconds: 10,
|
||||||
|
FailureThreshold: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "csi-provisioner",
|
||||||
|
Image: "registry.k8s.io/sig-storage/csi-provisioner:v6.1.1",
|
||||||
|
Args: []string{
|
||||||
|
"--csi-address=$(ADDRESS)",
|
||||||
|
"--v=5",
|
||||||
|
"--feature-gates=Topology=true",
|
||||||
|
"--extra-create-metadata",
|
||||||
|
"--leader-election=true",
|
||||||
|
"--default-fstype=ext4",
|
||||||
|
"--timeout=60s",
|
||||||
|
},
|
||||||
|
Env: []core.EnvVar{{
|
||||||
|
Name: "ADDRESS",
|
||||||
|
Value: "/csi/csi.sock",
|
||||||
|
}},
|
||||||
|
VolumeMounts: []core.VolumeMount{{Name: vol.Name, MountPath: "/csi"}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "csi-attacher",
|
||||||
|
Image: "registry.k8s.io/sig-storage/csi-attacher:v4.11.0",
|
||||||
|
Args: []string{
|
||||||
|
"--csi-address=$(ADDRESS)",
|
||||||
|
"--v=5",
|
||||||
|
"--leader-election=true",
|
||||||
|
"--timeout=60s",
|
||||||
|
},
|
||||||
|
Env: []core.EnvVar{{
|
||||||
|
Name: "ADDRESS",
|
||||||
|
Value: "/csi/csi.sock",
|
||||||
|
}},
|
||||||
|
VolumeMounts: []core.VolumeMount{{Name: vol.Name, MountPath: "/csi"}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "csi-snapshotter",
|
||||||
|
Image: "registry.k8s.io/sig-storage/csi-snapshotter:v8.5.0",
|
||||||
|
Args: []string{
|
||||||
|
"--csi-address=$(ADDRESS)",
|
||||||
|
"--v=5",
|
||||||
|
"--leader-election=true",
|
||||||
|
"--timeout=60s",
|
||||||
|
},
|
||||||
|
Env: []core.EnvVar{{
|
||||||
|
Name: "ADDRESS",
|
||||||
|
Value: "/csi/csi.sock",
|
||||||
|
}},
|
||||||
|
VolumeMounts: []core.VolumeMount{{Name: vol.Name, MountPath: "/csi"}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "csi-resizer",
|
||||||
|
Image: "registry.k8s.io/sig-storage/csi-resizer:v2.1.0",
|
||||||
|
Args: []string{
|
||||||
|
"--csi-address=$(ADDRESS)",
|
||||||
|
"--v=5",
|
||||||
|
"--leader-election=true",
|
||||||
|
"--timeout=60s",
|
||||||
|
},
|
||||||
|
Env: []core.EnvVar{{
|
||||||
|
Name: "ADDRESS",
|
||||||
|
Value: "/csi/csi.sock",
|
||||||
|
}},
|
||||||
|
VolumeMounts: []core.VolumeMount{{Name: vol.Name, MountPath: "/csi"}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "liveness-probe",
|
||||||
|
Image: "registry.k8s.io/sig-storage/livenessprobe:v2.18.0",
|
||||||
|
Args: []string{
|
||||||
|
"--csi-address=/csi/csi.sock",
|
||||||
|
"--health-port=9808",
|
||||||
|
},
|
||||||
|
VolumeMounts: []core.VolumeMount{{Name: vol.Name, MountPath: "/csi"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Volumes: []core.Volume{vol},
|
||||||
|
}
|
||||||
|
return kube.NewDeployment(meta, spec)
|
||||||
|
}
|
||||||
|
|
||||||
|
func controllerEnv() []core.EnvVar {
|
||||||
|
envMapping := map[string]string{
|
||||||
|
"CSI_ENDPOINT": "unix:///csi/csi.sock",
|
||||||
|
}
|
||||||
|
secretMapping := map[string]string{
|
||||||
|
"TRUENAS_API_KEY": Secret.APIKey,
|
||||||
|
}
|
||||||
|
env1 := kube.NewEnvVarWithSecret(envMapping, secretMapping, Secret.Name)
|
||||||
|
envMapping = map[string]string{
|
||||||
|
"TRUENAS_URL": Config.TruenasURL,
|
||||||
|
"TRUENAS_DEFAULT_POOL": Config.DefaultPool,
|
||||||
|
"TRUENAS_NFS_SERVER": Config.NFSServer,
|
||||||
|
"TRUENAS_ISCSI_PORTAL": Config.ISCSIPortal,
|
||||||
|
"TRUENAS_ISCSI_IQN_BASE": Config.ISCSIIQNBase,
|
||||||
|
"TRUENAS_INSECURE_SKIP_VERIFY": Config.TrueNASInsecure,
|
||||||
|
}
|
||||||
|
env2 := kube.NewEnvVarWithConfig(envMapping, config)
|
||||||
|
nodeEnv := core.EnvVar{
|
||||||
|
Name: "NODE_ID", // value
|
||||||
|
ValueFrom: &core.EnvVarSource{
|
||||||
|
FieldRef: &core.ObjectFieldSelector{
|
||||||
|
FieldPath: "spec.nodeName",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
env3 := append(env1, nodeEnv)
|
||||||
|
return append(env3, env2...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CSIDriver(name string) storage.CSIDriver {
|
||||||
|
fsGroupPolicy := storage.FileFSGroupPolicy
|
||||||
|
spec := storage.CSIDriverSpec{
|
||||||
|
AttachRequired: new(true),
|
||||||
|
PodInfoOnMount: new(true),
|
||||||
|
VolumeLifecycleModes: []storage.VolumeLifecycleMode{
|
||||||
|
storage.VolumeLifecycleEphemeral,
|
||||||
|
storage.VolumeLifecyclePersistent,
|
||||||
|
},
|
||||||
|
FSGroupPolicy: &fsGroupPolicy,
|
||||||
|
}
|
||||||
|
return kube.CSIDriver(name, spec)
|
||||||
|
}
|
||||||
|
|
||||||
|
func nodeCSI() apps.DaemonSet {
|
||||||
|
registrationDir := core.Volume{
|
||||||
|
Name: "registration-dir",
|
||||||
|
VolumeSource: core.VolumeSource{
|
||||||
|
HostPath: &core.HostPathVolumeSource{
|
||||||
|
Path: "/var/lib/kubelet/plugins_registry/",
|
||||||
|
Type: new(core.HostPathDirectoryOrCreate),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
pluginDir := core.Volume{
|
||||||
|
Name: "plugin-dir",
|
||||||
|
VolumeSource: core.VolumeSource{
|
||||||
|
HostPath: &core.HostPathVolumeSource{
|
||||||
|
Path: "/var/lib/kubelet/plugins/csi.truenas.io/",
|
||||||
|
Type: new(core.HostPathDirectoryOrCreate),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
kubeletDir := core.Volume{
|
||||||
|
Name: "kubelet-dir",
|
||||||
|
VolumeSource: core.VolumeSource{
|
||||||
|
HostPath: &core.HostPathVolumeSource{
|
||||||
|
Path: "/var/lib/kubelet",
|
||||||
|
Type: new(core.HostPathDirectory),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
deviceDir := core.Volume{
|
||||||
|
Name: "device-dir",
|
||||||
|
VolumeSource: core.VolumeSource{
|
||||||
|
HostPath: &core.HostPathVolumeSource{
|
||||||
|
Path: "/dev",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
iscsiDir := core.Volume{
|
||||||
|
Name: "iscsi-dir",
|
||||||
|
VolumeSource: core.VolumeSource{
|
||||||
|
HostPath: &core.HostPathVolumeSource{
|
||||||
|
Path: "/etc/iscsi",
|
||||||
|
Type: new(core.HostPathDirectory),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
iscsiLib := core.Volume{
|
||||||
|
Name: "iscsi-lib",
|
||||||
|
VolumeSource: core.VolumeSource{
|
||||||
|
HostPath: &core.HostPathVolumeSource{
|
||||||
|
Path: "/var/lib/iscsi",
|
||||||
|
Type: new(core.HostPathDirectoryOrCreate),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
hostRoot := core.Volume{
|
||||||
|
Name: "host-root",
|
||||||
|
VolumeSource: core.VolumeSource{
|
||||||
|
HostPath: &core.HostPathVolumeSource{
|
||||||
|
Path: "/",
|
||||||
|
Type: new(core.HostPathDirectory),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
meta := kube.NewMetadata(root.TrueNAS_CSI+"-node", Namespace)
|
||||||
|
podSpec := core.PodSpec{
|
||||||
|
ServiceAccountName: nodeSA.Name,
|
||||||
|
HostNetwork: true,
|
||||||
|
HostPID: true,
|
||||||
|
HostIPC: true,
|
||||||
|
PriorityClassName: "system-node-critical",
|
||||||
|
Tolerations: []core.Toleration{{
|
||||||
|
Operator: core.TolerationOpExists,
|
||||||
|
}},
|
||||||
|
Containers: []core.Container{
|
||||||
|
{
|
||||||
|
Name: "csi-node",
|
||||||
|
Image: "ghcr.io/truenas/truenas-csi:latest",
|
||||||
|
ImagePullPolicy: core.PullIfNotPresent,
|
||||||
|
Lifecycle: &core.Lifecycle{
|
||||||
|
PostStart: &core.LifecycleHandler{
|
||||||
|
Exec: &core.ExecAction{
|
||||||
|
Command: []string{
|
||||||
|
"/bin/sh",
|
||||||
|
"-c",
|
||||||
|
"mkdir -p /run/lock/iscsi && mv /usr/sbin/iscsiadm /usr/sbin/iscsiadm.orig 2>/dev/null; printf '#!/bin/sh\\nnsenter --mount=/host/proc/1/ns/mnt -- /usr/sbin/iscsiadm \"$@\"\\n' > /usr/sbin/iscsiadm && chmod +x /usr/sbin/iscsiadm",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SecurityContext: &core.SecurityContext{Privileged: new(true)},
|
||||||
|
Args: []string{
|
||||||
|
"--endpoint=$(CSI_ENDPOINT)",
|
||||||
|
"--node-id=$(NODE_ID)",
|
||||||
|
"--mode=node",
|
||||||
|
"--v=4",
|
||||||
|
},
|
||||||
|
Env: controllerEnv(),
|
||||||
|
VolumeMounts: []core.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: pluginDir.Name,
|
||||||
|
MountPath: "/csi",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: kubeletDir.Name,
|
||||||
|
MountPath: "/var/lib/kubelet",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: deviceDir.Name,
|
||||||
|
MountPath: "/dev",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: iscsiDir.Name,
|
||||||
|
MountPath: "/etc/iscsi",
|
||||||
|
MountPropagation: new(core.MountPropagationBidirectional),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: iscsiLib.Name,
|
||||||
|
MountPath: "/var/lib/iscsi",
|
||||||
|
MountPropagation: new(core.MountPropagationBidirectional),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: hostRoot.Name,
|
||||||
|
MountPath: "/",
|
||||||
|
MountPropagation: new(core.MountPropagationBidirectional),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
LivenessProbe: &core.Probe{
|
||||||
|
ProbeHandler: core.ProbeHandler{
|
||||||
|
HTTPGet: &core.HTTPGetAction{
|
||||||
|
Path: "/healthz",
|
||||||
|
Port: intstr.FromInt(9808),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
InitialDelaySeconds: 10,
|
||||||
|
TimeoutSeconds: 3,
|
||||||
|
PeriodSeconds: 10,
|
||||||
|
FailureThreshold: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "csi-node-driver-registrar",
|
||||||
|
Image: "registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.16.0",
|
||||||
|
Args: []string{
|
||||||
|
"--csi-address=$(ADDRESS)",
|
||||||
|
"--kubelet-registration-path=$(DRIVER_REG_SOCK_PATH)",
|
||||||
|
"--v=5",
|
||||||
|
},
|
||||||
|
Env: []core.EnvVar{
|
||||||
|
{
|
||||||
|
Name: "ADDRESS",
|
||||||
|
Value: "/csi/csi.sock",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "DRIVER_REG_SOCK_PATH",
|
||||||
|
Value: "/var/lib/kubelet/plugins/csi.truenas.io/csi.sock",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
VolumeMounts: []core.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: pluginDir.Name,
|
||||||
|
MountPath: "/csi",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: registrationDir.Name,
|
||||||
|
MountPath: "/registration",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "liveness-probe",
|
||||||
|
Image: "registry.k8s.io/sig-storage/livenessprobe:v2.18.0",
|
||||||
|
Args: []string{
|
||||||
|
"--csi-address=/csi/csi.sock",
|
||||||
|
"--health-port=9808",
|
||||||
|
},
|
||||||
|
VolumeMounts: []core.VolumeMount{{Name: pluginDir.Name, MountPath: "/csi"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Volumes: []core.Volume{
|
||||||
|
registrationDir,
|
||||||
|
pluginDir,
|
||||||
|
kubeletDir,
|
||||||
|
deviceDir,
|
||||||
|
iscsiDir,
|
||||||
|
iscsiLib,
|
||||||
|
hostRoot,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return kube.NewDeamonSet(meta, podSpec)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user