Server: Add support for hetzner csi with encryption
This commit is contained in:
parent
f0fa8d5ca5
commit
5bd824dee5
14 changed files with 644 additions and 3 deletions
18
README.md
18
README.md
|
@ -106,6 +106,24 @@ To provision the grafana agent only to an existing k8s system, ensure that the c
|
||||||
provs-server.jar k3s myuser@myhost.com -o grafana
|
provs-server.jar k3s myuser@myhost.com -o grafana
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To add the hetzner csi driver and encrypted volumes to your k3s installation add the following to the config:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
hetzner:
|
||||||
|
hcloudApiToken:
|
||||||
|
source: "PLAIN" # PLAIN, GOPASS or PROMPT
|
||||||
|
parameter: "mypassword" # the api key for the hetzner cloud
|
||||||
|
encryptionPassphrase:
|
||||||
|
source: "PLAIN" # PLAIN, GOPASS or PROMPT
|
||||||
|
parameter: "mypassword" # the encryption passphrase for created volumes
|
||||||
|
```
|
||||||
|
|
||||||
|
To provision the grafana agent only to an existing k8s system, ensure that the config (as above) is available and execute:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
provs-server.jar k3s myuser@myhost.com -o grafana
|
||||||
|
```
|
||||||
|
|
||||||
Reprovisioning the server can easily be done using the -r or --reprovision option.
|
Reprovisioning the server can easily be done using the -r or --reprovision option.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package org.domaindrivenarchitecture.provs.server.domain.hetzner_csi
|
||||||
|
|
||||||
|
import org.domaindrivenarchitecture.provs.framework.core.Prov
|
||||||
|
import org.domaindrivenarchitecture.provs.server.infrastructure.provisionHetznerCSIForK8s
|
||||||
|
|
||||||
|
fun Prov.provisionHetznerCSI(configResolved: HetznerCSIConfigResolved) =
|
||||||
|
provisionHetznerCSIForK8s(configResolved.hcloudApiToken, configResolved.encryptionPassphrase)
|
|
@ -0,0 +1,23 @@
|
||||||
|
package org.domaindrivenarchitecture.provs.server.domain.hetzner_csi
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import org.domaindrivenarchitecture.provs.framework.core.Secret
|
||||||
|
import org.domaindrivenarchitecture.provs.framework.ubuntu.secret.SecretSupplier
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class HetznerCSIConfig (
|
||||||
|
val hcloudApiToken: SecretSupplier,
|
||||||
|
val encryptionPassphrase: SecretSupplier,
|
||||||
|
) {
|
||||||
|
fun resolveSecret(): HetznerCSIConfigResolved = HetznerCSIConfigResolved(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class HetznerCSIConfigResolved(val configUnresolved: HetznerCSIConfig) {
|
||||||
|
val hcloudApiToken: Secret = configUnresolved.hcloudApiToken.secret()
|
||||||
|
val encryptionPassphrase: Secret = configUnresolved.encryptionPassphrase.secret()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class HetznerCSIConfigHolder(
|
||||||
|
val hetzner: HetznerCSIConfig
|
||||||
|
)
|
|
@ -2,6 +2,8 @@ package org.domaindrivenarchitecture.provs.server.domain.k3s
|
||||||
|
|
||||||
import org.domaindrivenarchitecture.provs.configuration.infrastructure.DefaultConfigFileRepository
|
import org.domaindrivenarchitecture.provs.configuration.infrastructure.DefaultConfigFileRepository
|
||||||
import org.domaindrivenarchitecture.provs.framework.core.Prov
|
import org.domaindrivenarchitecture.provs.framework.core.Prov
|
||||||
|
import org.domaindrivenarchitecture.provs.server.domain.hetzner_csi.HetznerCSIConfigResolved
|
||||||
|
import org.domaindrivenarchitecture.provs.server.domain.hetzner_csi.provisionHetznerCSI
|
||||||
import org.domaindrivenarchitecture.provs.server.domain.k8s_grafana_agent.GrafanaAgentConfigResolved
|
import org.domaindrivenarchitecture.provs.server.domain.k8s_grafana_agent.GrafanaAgentConfigResolved
|
||||||
import org.domaindrivenarchitecture.provs.server.domain.k8s_grafana_agent.provisionGrafanaAgent
|
import org.domaindrivenarchitecture.provs.server.domain.k8s_grafana_agent.provisionGrafanaAgent
|
||||||
import org.domaindrivenarchitecture.provs.server.infrastructure.*
|
import org.domaindrivenarchitecture.provs.server.infrastructure.*
|
||||||
|
@ -11,6 +13,7 @@ import kotlin.system.exitProcess
|
||||||
fun Prov.provisionK3sCommand(cli: K3sCliCommand) = task {
|
fun Prov.provisionK3sCommand(cli: K3sCliCommand) = task {
|
||||||
|
|
||||||
val grafanaConfigResolved: GrafanaAgentConfigResolved? = findK8sGrafanaConfig(cli.configFileName)?.resolveSecret()
|
val grafanaConfigResolved: GrafanaAgentConfigResolved? = findK8sGrafanaConfig(cli.configFileName)?.resolveSecret()
|
||||||
|
val hcloudConfigResolved: HetznerCSIConfigResolved? = findHetznerCSIConfig(cli.configFileName)?.resolveSecret()
|
||||||
|
|
||||||
if (cli.onlyModules == null) {
|
if (cli.onlyModules == null) {
|
||||||
val k3sConfig: K3sConfig = getK3sConfig(cli.configFileName)
|
val k3sConfig: K3sConfig = getK3sConfig(cli.configFileName)
|
||||||
|
@ -18,9 +21,10 @@ fun Prov.provisionK3sCommand(cli: K3sCliCommand) = task {
|
||||||
val k3sConfigReprovision = k3sConfig.copy(reprovision = cli.reprovision || k3sConfig.reprovision)
|
val k3sConfigReprovision = k3sConfig.copy(reprovision = cli.reprovision || k3sConfig.reprovision)
|
||||||
|
|
||||||
val applicationFile = cli.applicationFileName?.let { DefaultApplicationFileRepository(cli.applicationFileName).getFile() }
|
val applicationFile = cli.applicationFileName?.let { DefaultApplicationFileRepository(cli.applicationFileName).getFile() }
|
||||||
provisionK3s(k3sConfigReprovision, grafanaConfigResolved, applicationFile)
|
provisionK3s(k3sConfigReprovision, grafanaConfigResolved, hcloudConfigResolved, applicationFile)
|
||||||
} else {
|
} else {
|
||||||
provisionGrafana(cli.onlyModules, grafanaConfigResolved)
|
provisionGrafana(cli.onlyModules, grafanaConfigResolved)
|
||||||
|
provisionHetznerCSI(cli.onlyModules, hcloudConfigResolved)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +34,7 @@ fun Prov.provisionK3sCommand(cli: K3sCliCommand) = task {
|
||||||
fun Prov.provisionK3s(
|
fun Prov.provisionK3s(
|
||||||
k3sConfig: K3sConfig,
|
k3sConfig: K3sConfig,
|
||||||
grafanaConfigResolved: GrafanaAgentConfigResolved? = null,
|
grafanaConfigResolved: GrafanaAgentConfigResolved? = null,
|
||||||
|
hetznerCSIConfigResolved: HetznerCSIConfigResolved? = null,
|
||||||
applicationFile: ApplicationFile? = null
|
applicationFile: ApplicationFile? = null
|
||||||
) = task {
|
) = task {
|
||||||
|
|
||||||
|
@ -53,6 +58,10 @@ fun Prov.provisionK3s(
|
||||||
provisionGrafanaAgent(grafanaConfigResolved)
|
provisionGrafanaAgent(grafanaConfigResolved)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hetznerCSIConfigResolved != null) {
|
||||||
|
provisionHetznerCSI(hetznerCSIConfigResolved)
|
||||||
|
}
|
||||||
|
|
||||||
if (applicationFile != null) {
|
if (applicationFile != null) {
|
||||||
provisionK3sApplication(applicationFile)
|
provisionK3sApplication(applicationFile)
|
||||||
}
|
}
|
||||||
|
@ -75,3 +84,18 @@ private fun Prov.provisionGrafana(
|
||||||
provisionGrafanaAgent(grafanaConfigResolved)
|
provisionGrafanaAgent(grafanaConfigResolved)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Prov.provisionHetznerCSI(
|
||||||
|
onlyModules: List<String>?,
|
||||||
|
hetznerCSIConfigResolved: HetznerCSIConfigResolved?
|
||||||
|
) = task {
|
||||||
|
|
||||||
|
if (onlyModules != null && onlyModules.contains(ServerOnlyModule.HETZNER_CSI.name.lowercase())) {
|
||||||
|
if (hetznerCSIConfigResolved == null) {
|
||||||
|
println("ERROR: Could not find grafana config.")
|
||||||
|
exitProcess(7)
|
||||||
|
}
|
||||||
|
provisionHetznerCSI(hetznerCSIConfigResolved)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.domaindrivenarchitecture.provs.server.domain.k3s
|
package org.domaindrivenarchitecture.provs.server.domain.k3s
|
||||||
|
|
||||||
enum class ServerOnlyModule {
|
enum class ServerOnlyModule {
|
||||||
GRAFANA
|
GRAFANA,
|
||||||
|
HETZNER_CSI
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
package org.domaindrivenarchitecture.provs.server.infrastructure
|
||||||
|
|
||||||
|
import org.domaindrivenarchitecture.provs.framework.core.Prov
|
||||||
|
import org.domaindrivenarchitecture.provs.framework.core.Secret
|
||||||
|
import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.createFileFromResource
|
||||||
|
import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.createFileFromResourceTemplate
|
||||||
|
import org.domaindrivenarchitecture.provs.server.domain.k3s.FileMode
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
private const val hetznerCSIResourceDir = "org/domaindrivenarchitecture/provs/server/infrastructure/hetznerCSI/"
|
||||||
|
fun Prov.provisionHetznerCSIForK8s(hetznerApiToken: Secret, encryptionPassphrase: Secret) {
|
||||||
|
// CSI Driver
|
||||||
|
createFileFromResourceTemplate(
|
||||||
|
k3sManualManifestsDir + "hcloud-api-token-secret.yaml",
|
||||||
|
"hcloud-api-token-secret.template.yaml",
|
||||||
|
resourcePath = hetznerCSIResourceDir,
|
||||||
|
posixFilePermission = "644",
|
||||||
|
values = mapOf(
|
||||||
|
"HETZNER_API_TOKEN" to hetznerApiToken.plain()
|
||||||
|
))
|
||||||
|
applyHetznerCSIFileFromResource(File(k3sManualManifestsDir, "hcloud-api-token-secret.yaml"))
|
||||||
|
applyHetznerCSIFileFromResource(File(k3sManualManifestsDir, "hcloud-csi.yaml"))
|
||||||
|
|
||||||
|
// Encryption
|
||||||
|
createFileFromResourceTemplate(
|
||||||
|
k3sManualManifestsDir + "hcloud-encryption-secret.yaml",
|
||||||
|
"hcloud-encryption-secret.template.yaml",
|
||||||
|
resourcePath = hetznerCSIResourceDir,
|
||||||
|
posixFilePermission = "644",
|
||||||
|
values = mapOf(
|
||||||
|
"HETZNER_ENCRYPTION_PASSPHRASE" to encryptionPassphrase.plain()
|
||||||
|
))
|
||||||
|
applyHetznerCSIFileFromResource(File(k3sManualManifestsDir, "hcloud-encryption-secret.yaml"))
|
||||||
|
applyHetznerCSIFileFromResource(File(k3sManualManifestsDir, "hcloud-encrypted-storage-class.yaml"))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Prov.createHetznerCSIFileFromResource(
|
||||||
|
file: File,
|
||||||
|
posixFilePermission: FileMode? = "644"
|
||||||
|
) = task {
|
||||||
|
createFileFromResource(
|
||||||
|
file.path,
|
||||||
|
file.name,
|
||||||
|
hetznerCSIResourceDir,
|
||||||
|
posixFilePermission,
|
||||||
|
sudo = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Prov.applyHetznerCSIFileFromResource(file: File, posixFilePermission: FileMode? = "644") = task {
|
||||||
|
createHetznerCSIFileFromResource(file, posixFilePermission)
|
||||||
|
cmd("kubectl apply -f ${file.path}", sudo = true)
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package org.domaindrivenarchitecture.provs.server.infrastructure
|
||||||
|
|
||||||
|
import com.charleskorn.kaml.MissingRequiredPropertyException
|
||||||
|
import org.domaindrivenarchitecture.provs.configuration.domain.ConfigFileName
|
||||||
|
import org.domaindrivenarchitecture.provs.framework.core.readFromFile
|
||||||
|
import org.domaindrivenarchitecture.provs.framework.core.toYaml
|
||||||
|
import org.domaindrivenarchitecture.provs.framework.core.yamlToType
|
||||||
|
import org.domaindrivenarchitecture.provs.server.domain.hetzner_csi.HetznerCSIConfig
|
||||||
|
import org.domaindrivenarchitecture.provs.server.domain.hetzner_csi.HetznerCSIConfigHolder
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileWriter
|
||||||
|
|
||||||
|
private const val DEFAULT_CONFIG_FILE = "server-config.yaml"
|
||||||
|
|
||||||
|
fun findHetznerCSIConfig(fileName: ConfigFileName? = null): HetznerCSIConfig? {
|
||||||
|
val filePath = fileName?.fileName ?: DEFAULT_CONFIG_FILE
|
||||||
|
|
||||||
|
return if(File(filePath).exists()) {
|
||||||
|
try {
|
||||||
|
readFromFile(filePath).yamlToType<HetznerCSIConfigHolder>().hetzner
|
||||||
|
} catch (e: MissingRequiredPropertyException) {
|
||||||
|
if (e.message.contains("Property 'hetzner'")) null else throw e
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
internal fun writeConfig(config: HetznerCSIConfigHolder, fileName: String = "hetzner-config.yaml") =
|
||||||
|
FileWriter(fileName).use { it.write(config.toYaml()) }
|
|
@ -37,8 +37,9 @@ private val k3sEchoWithTls = File(k3sManualManifestsDir, "echo-tls.yaml")
|
||||||
private val k3sEchoNoTls = File(k3sManualManifestsDir, "echo-no-tls.yaml")
|
private val k3sEchoNoTls = File(k3sManualManifestsDir, "echo-no-tls.yaml")
|
||||||
private val selfSignedCertificate = File(k3sManualManifestsDir, "selfsigned-certificate.yaml")
|
private val selfSignedCertificate = File(k3sManualManifestsDir, "selfsigned-certificate.yaml")
|
||||||
|
|
||||||
private val localPathProvisionerConfig = File(k3sManualManifestsDir, "local-path-provisioner-config.yaml")
|
private val hetznerCSIDriver = File(k3sManualManifestsDir, "hcloud-csi.yaml")
|
||||||
|
|
||||||
|
private val localPathProvisionerConfig = File(k3sManualManifestsDir, "local-path-provisioner-config.yaml")
|
||||||
|
|
||||||
// ----------------------------------- public functions --------------------------------
|
// ----------------------------------- public functions --------------------------------
|
||||||
|
|
||||||
|
@ -124,6 +125,10 @@ fun Prov.installK3s(k3sConfig: K3sConfig): ProvResult {
|
||||||
applyK3sFileFromResource(k3sMiddleWareHttpsRedirect)
|
applyK3sFileFromResource(k3sMiddleWareHttpsRedirect)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hetzner csi-driver
|
||||||
|
applyK3sFileFromResource(hetznerCSIDriver)
|
||||||
|
|
||||||
|
// other
|
||||||
applyK3sFileFromResource(localPathProvisionerConfig)
|
applyK3sFileFromResource(localPathProvisionerConfig)
|
||||||
|
|
||||||
cmd("kubectl set env deployment -n kube-system local-path-provisioner DEPLOY_DATE=\"$(date)\"", sudo = true)
|
cmd("kubectl set env deployment -n kube-system local-path-provisioner DEPLOY_DATE=\"$(date)\"", sudo = true)
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: hcloud
|
||||||
|
namespace: kube-system
|
||||||
|
stringData:
|
||||||
|
token: $HETZNER_API_TOKEN
|
|
@ -0,0 +1,401 @@
|
||||||
|
# Version 2.6.0
|
||||||
|
# Source: hcloud-csi/templates/controller/serviceaccount.yaml
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: hcloud-csi-controller
|
||||||
|
namespace: "kube-system"
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: hcloud-csi
|
||||||
|
app.kubernetes.io/instance: hcloud-csi
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
automountServiceAccountToken: true
|
||||||
|
---
|
||||||
|
# Source: hcloud-csi/templates/core/storageclass.yaml
|
||||||
|
kind: StorageClass
|
||||||
|
apiVersion: storage.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: hcloud-volumes
|
||||||
|
annotations:
|
||||||
|
storageclass.kubernetes.io/is-default-class: "true"
|
||||||
|
provisioner: csi.hetzner.cloud
|
||||||
|
volumeBindingMode: WaitForFirstConsumer
|
||||||
|
allowVolumeExpansion: true
|
||||||
|
reclaimPolicy: "Delete"
|
||||||
|
---
|
||||||
|
# Source: hcloud-csi/templates/controller/clusterrole.yaml
|
||||||
|
kind: ClusterRole
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: hcloud-csi-controller
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: hcloud-csi
|
||||||
|
app.kubernetes.io/instance: hcloud-csi
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
rules:
|
||||||
|
# attacher
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: [persistentvolumes]
|
||||||
|
verbs: [get, list, watch, update, patch]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: [nodes]
|
||||||
|
verbs: [get, list, watch]
|
||||||
|
- apiGroups: [csi.storage.k8s.io]
|
||||||
|
resources: [csinodeinfos]
|
||||||
|
verbs: [get, list, watch]
|
||||||
|
- apiGroups: [storage.k8s.io]
|
||||||
|
resources: [csinodes]
|
||||||
|
verbs: [get, list, watch]
|
||||||
|
- apiGroups: [storage.k8s.io]
|
||||||
|
resources: [volumeattachments]
|
||||||
|
verbs: [get, list, watch, update, patch]
|
||||||
|
- apiGroups: [storage.k8s.io]
|
||||||
|
resources: [volumeattachments/status]
|
||||||
|
verbs: [patch]
|
||||||
|
# provisioner
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: [secrets]
|
||||||
|
verbs: [get, list]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: [persistentvolumes]
|
||||||
|
verbs: [get, list, watch, create, delete, patch]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: [persistentvolumeclaims, persistentvolumeclaims/status]
|
||||||
|
verbs: [get, list, watch, update, patch]
|
||||||
|
- apiGroups: [storage.k8s.io]
|
||||||
|
resources: [storageclasses]
|
||||||
|
verbs: [get, list, watch]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: [events]
|
||||||
|
verbs: [list, watch, create, update, patch]
|
||||||
|
- apiGroups: [snapshot.storage.k8s.io]
|
||||||
|
resources: [volumesnapshots]
|
||||||
|
verbs: [get, list]
|
||||||
|
- apiGroups: [snapshot.storage.k8s.io]
|
||||||
|
resources: [volumesnapshotcontents]
|
||||||
|
verbs: [get, list]
|
||||||
|
# resizer
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: [pods]
|
||||||
|
verbs: [get, list, watch]
|
||||||
|
# node
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: [events]
|
||||||
|
verbs: [get, list, watch, create, update, patch]
|
||||||
|
---
|
||||||
|
# Source: hcloud-csi/templates/controller/clusterrolebinding.yaml
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: hcloud-csi-controller
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: hcloud-csi
|
||||||
|
app.kubernetes.io/instance: hcloud-csi
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: hcloud-csi-controller
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: hcloud-csi-controller
|
||||||
|
namespace: "kube-system"
|
||||||
|
---
|
||||||
|
# Source: hcloud-csi/templates/controller/service.yaml
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: hcloud-csi-controller-metrics
|
||||||
|
namespace: "kube-system"
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: hcloud-csi
|
||||||
|
app.kubernetes.io/instance: hcloud-csi
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: metrics
|
||||||
|
port: 9189
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: hcloud-csi
|
||||||
|
app.kubernetes.io/instance: hcloud-csi
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
---
|
||||||
|
# Source: hcloud-csi/templates/node/service.yaml
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: hcloud-csi-node-metrics
|
||||||
|
namespace: "kube-system"
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: hcloud-csi
|
||||||
|
app.kubernetes.io/instance: hcloud-csi
|
||||||
|
app.kubernetes.io/component: node
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: metrics
|
||||||
|
port: 9189
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: hcloud-csi
|
||||||
|
app.kubernetes.io/instance: hcloud-csi
|
||||||
|
app.kubernetes.io/component: node
|
||||||
|
---
|
||||||
|
# Source: hcloud-csi/templates/node/daemonset.yaml
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: DaemonSet
|
||||||
|
metadata:
|
||||||
|
name: hcloud-csi-node
|
||||||
|
namespace: "kube-system"
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: hcloud-csi
|
||||||
|
app.kubernetes.io/instance: hcloud-csi
|
||||||
|
app.kubernetes.io/component: node
|
||||||
|
app: hcloud-csi
|
||||||
|
spec:
|
||||||
|
updateStrategy:
|
||||||
|
type: RollingUpdate
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: hcloud-csi
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: hcloud-csi
|
||||||
|
app.kubernetes.io/instance: hcloud-csi
|
||||||
|
app.kubernetes.io/component: node
|
||||||
|
app: hcloud-csi
|
||||||
|
spec:
|
||||||
|
|
||||||
|
affinity:
|
||||||
|
nodeAffinity:
|
||||||
|
requiredDuringSchedulingIgnoredDuringExecution:
|
||||||
|
nodeSelectorTerms:
|
||||||
|
- matchExpressions:
|
||||||
|
- key: instance.hetzner.cloud/is-root-server
|
||||||
|
operator: NotIn
|
||||||
|
values:
|
||||||
|
- "true"
|
||||||
|
tolerations:
|
||||||
|
- effect: NoExecute
|
||||||
|
operator: Exists
|
||||||
|
- effect: NoSchedule
|
||||||
|
operator: Exists
|
||||||
|
- key: CriticalAddonsOnly
|
||||||
|
operator: Exists
|
||||||
|
securityContext:
|
||||||
|
fsGroup: 1001
|
||||||
|
initContainers:
|
||||||
|
containers:
|
||||||
|
- name: csi-node-driver-registrar
|
||||||
|
image: registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.7.0
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
args:
|
||||||
|
- --kubelet-registration-path=/var/lib/kubelet/plugins/csi.hetzner.cloud/socket
|
||||||
|
volumeMounts:
|
||||||
|
- name: plugin-dir
|
||||||
|
mountPath: /run/csi
|
||||||
|
- name: registration-dir
|
||||||
|
mountPath: /registration
|
||||||
|
resources:
|
||||||
|
limits: {}
|
||||||
|
requests: {}
|
||||||
|
- name: liveness-probe
|
||||||
|
image: registry.k8s.io/sig-storage/livenessprobe:v2.9.0
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /run/csi
|
||||||
|
name: plugin-dir
|
||||||
|
resources:
|
||||||
|
limits: {}
|
||||||
|
requests: {}
|
||||||
|
- name: hcloud-csi-driver
|
||||||
|
image: docker.io/hetznercloud/hcloud-csi-driver:v2.6.0 # x-release-please-version
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
command: [/bin/hcloud-csi-driver-node]
|
||||||
|
volumeMounts:
|
||||||
|
- name: kubelet-dir
|
||||||
|
mountPath: /var/lib/kubelet
|
||||||
|
mountPropagation: "Bidirectional"
|
||||||
|
- name: plugin-dir
|
||||||
|
mountPath: /run/csi
|
||||||
|
- name: device-dir
|
||||||
|
mountPath: /dev
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
env:
|
||||||
|
- name: CSI_ENDPOINT
|
||||||
|
value: unix:///run/csi/socket
|
||||||
|
- name: METRICS_ENDPOINT
|
||||||
|
value: "0.0.0.0:9189"
|
||||||
|
- name: ENABLE_METRICS
|
||||||
|
value: "true"
|
||||||
|
ports:
|
||||||
|
- containerPort: 9189
|
||||||
|
name: metrics
|
||||||
|
- name: healthz
|
||||||
|
protocol: TCP
|
||||||
|
containerPort: 9808
|
||||||
|
resources:
|
||||||
|
limits: {}
|
||||||
|
requests: {}
|
||||||
|
livenessProbe:
|
||||||
|
failureThreshold: 5
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 2
|
||||||
|
successThreshold: 1
|
||||||
|
timeoutSeconds: 3
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: healthz
|
||||||
|
volumes:
|
||||||
|
- name: kubelet-dir
|
||||||
|
hostPath:
|
||||||
|
path: /var/lib/kubelet
|
||||||
|
type: Directory
|
||||||
|
- name: plugin-dir
|
||||||
|
hostPath:
|
||||||
|
path: /var/lib/kubelet/plugins/csi.hetzner.cloud/
|
||||||
|
type: DirectoryOrCreate
|
||||||
|
- name: registration-dir
|
||||||
|
hostPath:
|
||||||
|
path: /var/lib/kubelet/plugins_registry/
|
||||||
|
type: Directory
|
||||||
|
- name: device-dir
|
||||||
|
hostPath:
|
||||||
|
path: /dev
|
||||||
|
type: Directory
|
||||||
|
---
|
||||||
|
# Source: hcloud-csi/templates/controller/deployment.yaml
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: hcloud-csi-controller
|
||||||
|
namespace: "kube-system"
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: hcloud-csi
|
||||||
|
app.kubernetes.io/instance: hcloud-csi
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app: hcloud-csi-controller
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
strategy:
|
||||||
|
type: RollingUpdate
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: hcloud-csi-controller
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: hcloud-csi
|
||||||
|
app.kubernetes.io/instance: hcloud-csi
|
||||||
|
app.kubernetes.io/component: controller
|
||||||
|
app: hcloud-csi-controller
|
||||||
|
spec:
|
||||||
|
serviceAccountName: hcloud-csi-controller
|
||||||
|
|
||||||
|
securityContext:
|
||||||
|
fsGroup: 1001
|
||||||
|
initContainers:
|
||||||
|
containers:
|
||||||
|
- name: csi-attacher
|
||||||
|
image: registry.k8s.io/sig-storage/csi-attacher:v4.1.0
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
resources:
|
||||||
|
limits: {}
|
||||||
|
requests: {}
|
||||||
|
args:
|
||||||
|
- --default-fstype=ext4
|
||||||
|
volumeMounts:
|
||||||
|
- name: socket-dir
|
||||||
|
mountPath: /run/csi
|
||||||
|
|
||||||
|
- name: csi-resizer
|
||||||
|
image: registry.k8s.io/sig-storage/csi-resizer:v1.7.0
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
resources:
|
||||||
|
limits: {}
|
||||||
|
requests: {}
|
||||||
|
volumeMounts:
|
||||||
|
- name: socket-dir
|
||||||
|
mountPath: /run/csi
|
||||||
|
|
||||||
|
- name: csi-provisioner
|
||||||
|
image: registry.k8s.io/sig-storage/csi-provisioner:v3.4.0
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
resources:
|
||||||
|
limits: {}
|
||||||
|
requests: {}
|
||||||
|
args:
|
||||||
|
- --feature-gates=Topology=true
|
||||||
|
- --default-fstype=ext4
|
||||||
|
volumeMounts:
|
||||||
|
- name: socket-dir
|
||||||
|
mountPath: /run/csi
|
||||||
|
|
||||||
|
- name: liveness-probe
|
||||||
|
image: registry.k8s.io/sig-storage/livenessprobe:v2.9.0
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
resources:
|
||||||
|
limits: {}
|
||||||
|
requests: {}
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /run/csi
|
||||||
|
name: socket-dir
|
||||||
|
|
||||||
|
- name: hcloud-csi-driver
|
||||||
|
image: docker.io/hetznercloud/hcloud-csi-driver:v2.6.0 # x-release-please-version
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
command: [/bin/hcloud-csi-driver-controller]
|
||||||
|
env:
|
||||||
|
- name: CSI_ENDPOINT
|
||||||
|
value: unix:///run/csi/socket
|
||||||
|
- name: METRICS_ENDPOINT
|
||||||
|
value: "0.0.0.0:9189"
|
||||||
|
- name: ENABLE_METRICS
|
||||||
|
value: "true"
|
||||||
|
- name: KUBE_NODE_NAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
apiVersion: v1
|
||||||
|
fieldPath: spec.nodeName
|
||||||
|
- name: HCLOUD_TOKEN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: hcloud
|
||||||
|
key: token
|
||||||
|
resources:
|
||||||
|
limits: {}
|
||||||
|
requests: {}
|
||||||
|
ports:
|
||||||
|
- name: metrics
|
||||||
|
containerPort: 9189
|
||||||
|
- name: healthz
|
||||||
|
protocol: TCP
|
||||||
|
containerPort: 9808
|
||||||
|
livenessProbe:
|
||||||
|
failureThreshold: 5
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 2
|
||||||
|
successThreshold: 1
|
||||||
|
timeoutSeconds: 3
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: healthz
|
||||||
|
volumeMounts:
|
||||||
|
- name: socket-dir
|
||||||
|
mountPath: /run/csi
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: socket-dir
|
||||||
|
emptyDir: {}
|
||||||
|
---
|
||||||
|
# Source: hcloud-csi/templates/core/csidriver.yaml
|
||||||
|
apiVersion: storage.k8s.io/v1
|
||||||
|
kind: CSIDriver
|
||||||
|
metadata:
|
||||||
|
name: csi.hetzner.cloud
|
||||||
|
spec:
|
||||||
|
attachRequired: true
|
||||||
|
fsGroupPolicy: File
|
||||||
|
podInfoOnMount: true
|
||||||
|
volumeLifecycleModes:
|
||||||
|
- Persistent
|
|
@ -0,0 +1,11 @@
|
||||||
|
apiVersion: storage.k8s.io/v1
|
||||||
|
kind: StorageClass
|
||||||
|
metadata:
|
||||||
|
name: hcloud-volumes-encrypted
|
||||||
|
provisioner: csi.hetzner.cloud
|
||||||
|
reclaimPolicy: Delete
|
||||||
|
volumeBindingMode: WaitForFirstConsumer
|
||||||
|
allowVolumeExpansion: true
|
||||||
|
parameters:
|
||||||
|
csi.storage.k8s.io/node-publish-secret-name: encryption-secret
|
||||||
|
csi.storage.k8s.io/node-publish-secret-namespace: kube-system
|
|
@ -0,0 +1,7 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: encryption-secret
|
||||||
|
namespace: kube-system
|
||||||
|
stringData:
|
||||||
|
encryption-passphrase: $HETZNER_ENCRYPTION_PASSPHRASE
|
|
@ -0,0 +1,35 @@
|
||||||
|
package org.domaindrivenarchitecture.provs.server.infrastructure
|
||||||
|
|
||||||
|
import org.domaindrivenarchitecture.provs.configuration.domain.ConfigFileName
|
||||||
|
import org.domaindrivenarchitecture.provs.framework.ubuntu.secret.SecretSourceType
|
||||||
|
import org.domaindrivenarchitecture.provs.framework.ubuntu.secret.SecretSupplier
|
||||||
|
import org.domaindrivenarchitecture.provs.server.domain.hetzner_csi.HetznerCSIConfig
|
||||||
|
import org.domaindrivenarchitecture.provs.server.domain.k8s_grafana_agent.GrafanaAgentConfig
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
internal class HetznerCSIRepositoryKtTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun findHetznerCSIConfig_returns_config() {
|
||||||
|
// when
|
||||||
|
val config = findHetznerCSIConfig(ConfigFileName("src/test/resources/k3s-server-config-with-hetzner.yaml"))
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(
|
||||||
|
HetznerCSIConfig(
|
||||||
|
hcloudApiToken = SecretSupplier(SecretSourceType.GOPASS, "path/to/apitoken"),
|
||||||
|
encryptionPassphrase = SecretSupplier(SecretSourceType.GOPASS, "path/to/encryption"),
|
||||||
|
), config
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun findHetznerCSIConfig_returns_null_if_no_hetzner_data_available() {
|
||||||
|
// when
|
||||||
|
val config = findHetznerCSIConfig(ConfigFileName("src/test/resources/k3s-server-config.yaml"))
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(null, config)
|
||||||
|
}
|
||||||
|
}
|
18
src/test/resources/k3s-server-config-with-hetzner.yaml
Normal file
18
src/test/resources/k3s-server-config-with-hetzner.yaml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
fqdn: statistics.test.meissa-gmbh.de
|
||||||
|
node:
|
||||||
|
ipv4: 162.55.164.138
|
||||||
|
ipv6: 2a01:4f8:c010:672f::1
|
||||||
|
certmanager:
|
||||||
|
email: admin@meissa-gmbh.de
|
||||||
|
letsencryptEndpoint: prod
|
||||||
|
echo: true
|
||||||
|
reprovision: true
|
||||||
|
|
||||||
|
|
||||||
|
hetzner:
|
||||||
|
hcloudApiToken:
|
||||||
|
source: "GOPASS" # PLAIN, GOPASS or PROMPT
|
||||||
|
parameter: "path/to/apitoken" # the api key for the hetzner cloud
|
||||||
|
encryptionPassphrase:
|
||||||
|
source: "GOPASS" # PLAIN, GOPASS or PROMPT
|
||||||
|
parameter: "path/to/encryption" # the encryption passphrase for created volumes
|
Loading…
Reference in a new issue