Merge branch 'main' of ssh://repo.prod.meissa.de:2222/meissa/provs
This commit is contained in:
commit
a336838af8
33 changed files with 723 additions and 27 deletions
|
@ -1,6 +1,6 @@
|
||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="test_incl_containtertests" type="JUnit" factoryName="JUnit">
|
<configuration default="false" name="test_incl_containtertests" type="JUnit" factoryName="JUnit">
|
||||||
<module name="provs.test" />
|
<module name="org.domaindrivenarchitecture.provs.provs.test" />
|
||||||
<option name="PACKAGE_NAME" value="org" />
|
<option name="PACKAGE_NAME" value="org" />
|
||||||
<option name="MAIN_CLASS_NAME" value="" />
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
<option name="METHOD_NAME" value="" />
|
<option name="METHOD_NAME" value="" />
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="test_incl_extensive_container_tests" type="JUnit" factoryName="JUnit">
|
<configuration default="false" name="test_incl_extensive_container_tests" type="JUnit" factoryName="JUnit">
|
||||||
<module name="provs.test" />
|
<module name="org.domaindrivenarchitecture.provs.provs.test" />
|
||||||
<option name="PACKAGE_NAME" value="org" />
|
<option name="PACKAGE_NAME" value="org" />
|
||||||
<option name="MAIN_CLASS_NAME" value="" />
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
<option name="METHOD_NAME" value="" />
|
<option name="METHOD_NAME" value="" />
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="testquick" type="JUnit" factoryName="JUnit">
|
<configuration default="false" name="testquick" type="JUnit" factoryName="JUnit">
|
||||||
<module name="provs.test" />
|
<module name="org.domaindrivenarchitecture.provs.provs.test" />
|
||||||
<option name="PACKAGE_NAME" value="org" />
|
<option name="PACKAGE_NAME" value="org" />
|
||||||
<option name="MAIN_CLASS_NAME" value="" />
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
<option name="METHOD_NAME" value="" />
|
<option name="METHOD_NAME" value="" />
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
FROM ubuntu:latest
|
# image for usage in ci pipeline
|
||||||
|
FROM ubuntu:22.04
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
RUN apt-get update && apt-get -y install apt-utils sudo
|
RUN apt-get update && apt-get -y install apt-utils sudo
|
||||||
|
|
||||||
RUN useradd -m testuser && echo "testuser:testuserpw" | chpasswd && adduser testuser sudo
|
RUN useradd -m testuser && echo "testuser:testuserpw" | chpasswd && usermod -aG sudo testuser
|
||||||
RUN echo "testuser ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/testuser
|
RUN echo "testuser ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/testuser
|
||||||
|
|
||||||
USER testuser
|
USER testuser
|
||||||
|
|
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
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version_no = "1.7.20"
|
ext.kotlin_version_no = "1.8.20"
|
||||||
ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID
|
ext.CI_PROJECT_ID = System.env.CI_PROJECT_ID
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
@ -16,7 +16,7 @@ plugins {
|
||||||
apply plugin: "maven-publish"
|
apply plugin: "maven-publish"
|
||||||
|
|
||||||
|
|
||||||
version = "0.31.1-SNAPSHOT"
|
version = "0.35.1-SNAPSHOT"
|
||||||
group = "org.domaindrivenarchitecture.provs"
|
group = "org.domaindrivenarchitecture.provs"
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,8 +76,8 @@ dependencies {
|
||||||
api('com.charleskorn.kaml:kaml:0.54.0')
|
api('com.charleskorn.kaml:kaml:0.54.0')
|
||||||
|
|
||||||
api("org.slf4j:slf4j-api:1.7.36")
|
api("org.slf4j:slf4j-api:1.7.36")
|
||||||
api('ch.qos.logback:logback-classic:1.2.11')
|
api('ch.qos.logback:logback-classic:1.4.14')
|
||||||
api('ch.qos.logback:logback-core:1.2.11')
|
api('ch.qos.logback:logback-core:1.4.14')
|
||||||
|
|
||||||
implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version_no")
|
implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version_no")
|
||||||
implementation("com.hierynomus:sshj:0.32.0")
|
implementation("com.hierynomus:sshj:0.32.0")
|
||||||
|
|
3
build.py
3
build.py
|
@ -8,7 +8,7 @@ name = "provs"
|
||||||
PROJECT_ROOT_PATH = "."
|
PROJECT_ROOT_PATH = "."
|
||||||
|
|
||||||
|
|
||||||
version = "0.31.1-SNAPSHOT"
|
version = "0.35.1-dev"
|
||||||
|
|
||||||
|
|
||||||
@init
|
@init
|
||||||
|
@ -38,6 +38,7 @@ def initialize2(project):
|
||||||
"project_root_path": PROJECT_ROOT_PATH,
|
"project_root_path": PROJECT_ROOT_PATH,
|
||||||
"build_types": [],
|
"build_types": [],
|
||||||
"mixin_types": ["RELEASE"],
|
"mixin_types": ["RELEASE"],
|
||||||
|
"release_main_branch": "main",
|
||||||
"release_primary_build_file": "build.gradle",
|
"release_primary_build_file": "build.gradle",
|
||||||
"release_secondary_build_files": ["build.py"],
|
"release_secondary_build_files": ["build.py"],
|
||||||
# release artifacts
|
# release artifacts
|
||||||
|
|
|
@ -5,7 +5,7 @@ release-1.2 or release-1.2.3
|
||||||
|
|
||||||
I.e.: release-X.X.Z where X, Y, Z are the major, minor resp. the patch level of the release. Z can be omitted.
|
I.e.: release-X.X.Z where X, Y, Z are the major, minor resp. the patch level of the release. Z can be omitted.
|
||||||
|
|
||||||
**Note:** Such kind of release tags should only be applied to commits in the master branch.
|
**Note:** Such kind of release tags should only be applied to commits in the main branch.
|
||||||
|
|
||||||
```
|
```
|
||||||
#adjust [version]
|
#adjust [version]
|
||||||
|
|
10
doc/dev/upgradingGradleWrapper.md
Normal file
10
doc/dev/upgradingGradleWrapper.md
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
### Howto update gradle wrapper
|
||||||
|
|
||||||
|
1. To *latest* version (be aware for deprecated parts in future versions):
|
||||||
|
```shell
|
||||||
|
./gradlew wrapper --gradle-version latest
|
||||||
|
```
|
||||||
|
2. To *specific version:
|
||||||
|
```shell
|
||||||
|
./gradlew wrapper --gradle-version 8.6
|
||||||
|
```
|
|
@ -161,4 +161,6 @@ fun Prov.provisionIdeDesktop() {
|
||||||
// IDEs
|
// IDEs
|
||||||
installVSC("python", "clojure")
|
installVSC("python", "clojure")
|
||||||
installIntelliJ()
|
installIntelliJ()
|
||||||
|
|
||||||
|
installKubeconform()
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,9 +49,33 @@ fun Prov.installKubectlAndTools(): ProvResult = task {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task("installKubeconform") {
|
||||||
|
|
||||||
|
installKubeconform()
|
||||||
|
}
|
||||||
installDevopsScripts()
|
installDevopsScripts()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Prov.installKubeconform() = task {
|
||||||
|
// check for latest stable release on: https://github.com/yannh/kubeconform/releases
|
||||||
|
val version = "0.6.4"
|
||||||
|
val installationPath = "/usr/local/bin/"
|
||||||
|
val tmpDir = "~/tmp"
|
||||||
|
val filename = "kubeconform-linux-amd64"
|
||||||
|
val packedFilename = "$filename.tar.gz"
|
||||||
|
|
||||||
|
if ( !chk("kubeconform -v") || "v$version" != cmd("kubeconform -v").out?.trim() ) {
|
||||||
|
downloadFromURL(
|
||||||
|
"https://github.com/yannh/kubeconform/releases/download/v$version/$packedFilename",
|
||||||
|
path = tmpDir,
|
||||||
|
sha256sum = "2b4ebeaa4d5ac4843cf8f7b7e66a8874252b6b71bc7cbfc4ef1cbf85acec7c07"
|
||||||
|
)
|
||||||
|
cmd("sudo tar -xzf $packedFilename -C $installationPath", tmpDir)
|
||||||
|
} else {
|
||||||
|
ProvResult(true, out = "Kubeconform $version already installed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun Prov.installKubectl(): ProvResult = task {
|
fun Prov.installKubectl(): ProvResult = task {
|
||||||
|
|
||||||
// see https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/
|
// see https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/
|
||||||
|
|
|
@ -17,12 +17,12 @@ class UbuntuPlusUser(private val userName: String = "testuser") : DockerImage {
|
||||||
|
|
||||||
override fun imageText(): String {
|
override fun imageText(): String {
|
||||||
return """
|
return """
|
||||||
FROM ubuntu:20.04
|
FROM ubuntu:22.04
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
RUN apt-get update && apt-get -y install sudo
|
RUN apt-get update && apt-get -y install sudo
|
||||||
RUN useradd -m $userName && echo "$userName:$userName" | chpasswd && adduser $userName sudo
|
RUN useradd -m $userName && echo "$userName:$userName" | chpasswd && usermod -aG sudo $userName
|
||||||
RUN echo "$userName ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/$userName
|
RUN echo "$userName ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/$userName
|
||||||
|
|
||||||
USER $userName
|
USER $userName
|
||||||
|
|
|
@ -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()
|
||||||
|
))
|
||||||
|
cmd("kubectl apply -f hcloud-api-token-secret.yaml", k3sManualManifestsDir)
|
||||||
|
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()
|
||||||
|
))
|
||||||
|
cmd("kubectl apply -f hcloud-encryption-secret.yaml", k3sManualManifestsDir)
|
||||||
|
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()) }
|
|
@ -39,7 +39,6 @@ private val selfSignedCertificate = File(k3sManualManifestsDir, "selfsigned-cert
|
||||||
|
|
||||||
private val localPathProvisionerConfig = File(k3sManualManifestsDir, "local-path-provisioner-config.yaml")
|
private val localPathProvisionerConfig = File(k3sManualManifestsDir, "local-path-provisioner-config.yaml")
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------- public functions --------------------------------
|
// ----------------------------------- public functions --------------------------------
|
||||||
|
|
||||||
fun Prov.testConfigExists(): Boolean {
|
fun Prov.testConfigExists(): Boolean {
|
||||||
|
@ -52,7 +51,11 @@ fun Prov.deprovisionK3sInfra() = task {
|
||||||
deleteFile(certManagerDeployment.path, sudo = true)
|
deleteFile(certManagerDeployment.path, sudo = true)
|
||||||
deleteFile(certManagerIssuer.path, sudo = true)
|
deleteFile(certManagerIssuer.path, sudo = true)
|
||||||
deleteFile(k3sKubeConfig.path, sudo = true)
|
deleteFile(k3sKubeConfig.path, sudo = true)
|
||||||
cmd("k3s-uninstall.sh")
|
|
||||||
|
val k3sUninstallScript = "k3s-uninstall.sh"
|
||||||
|
if (chk("which $k3sUninstallScript")) {
|
||||||
|
cmd(k3sUninstallScript)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -120,6 +123,7 @@ fun Prov.installK3s(k3sConfig: K3sConfig): ProvResult {
|
||||||
applyK3sFileFromResource(k3sMiddleWareHttpsRedirect)
|
applyK3sFileFromResource(k3sMiddleWareHttpsRedirect)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
|
@ -8,7 +8,7 @@ function usage() {
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
local cluster_name="${1}";
|
local cluster_name="${1}";
|
||||||
local domain_name="${2:-meissa-gmbh.de}";
|
local domain_name="${2:-meissa.de}";
|
||||||
|
|
||||||
/usr/local/bin/k3s-create-context.sh ${cluster_name} ${domain_name}
|
/usr/local/bin/k3s-create-context.sh ${cluster_name} ${domain_name}
|
||||||
kubectl config use-context ${cluster_name}
|
kubectl config use-context ${cluster_name}
|
||||||
|
|
|
@ -4,8 +4,9 @@ set -o noglob
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
local cluster_name="${1}"; shift
|
local cluster_name="${1}"; shift
|
||||||
|
local domain_name="${1:-meissa.de}"; shift
|
||||||
|
|
||||||
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@${cluster_name}.meissa-gmbh.de -L 8002:localhost:8002 -L 6443:192.168.5.1:6443
|
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@${cluster_name}.${domain_name} -L 8002:localhost:8002 -L 6443:192.168.5.1:6443
|
||||||
}
|
}
|
||||||
|
|
||||||
main $1
|
main $1
|
||||||
|
|
|
@ -4,8 +4,9 @@ set -o noglob
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
local cluster_name="${1}"; shift
|
local cluster_name="${1}"; shift
|
||||||
|
local domain_name="${1:-meissa.de}"; shift
|
||||||
|
|
||||||
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@${cluster_name}.meissa-gmbh.de
|
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@${cluster_name}.${domain_name}
|
||||||
}
|
}
|
||||||
|
|
||||||
main $1
|
main $1
|
||||||
|
|
|
@ -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
|
|
@ -2,9 +2,8 @@ kind: Ingress
|
||||||
apiVersion: networking.k8s.io/v1
|
apiVersion: networking.k8s.io/v1
|
||||||
metadata:
|
metadata:
|
||||||
name: echo-ingress
|
name: echo-ingress
|
||||||
annotations:
|
|
||||||
kubernetes.io/ingress.class: "traefik"
|
|
||||||
spec:
|
spec:
|
||||||
|
ingressClassName: traefik
|
||||||
rules:
|
rules:
|
||||||
- http:
|
- http:
|
||||||
paths:
|
paths:
|
||||||
|
|
|
@ -3,9 +3,9 @@ apiVersion: networking.k8s.io/v1
|
||||||
metadata:
|
metadata:
|
||||||
name: echo-ingress
|
name: echo-ingress
|
||||||
annotations:
|
annotations:
|
||||||
kubernetes.io/ingress.class: "traefik"
|
|
||||||
cert-manager.io/cluster-issuer: ${issuer_name}
|
cert-manager.io/cluster-issuer: ${issuer_name}
|
||||||
spec:
|
spec:
|
||||||
|
ingressClassName: traefik
|
||||||
rules:
|
rules:
|
||||||
- host: ${fqdn}
|
- host: ${fqdn}
|
||||||
http:
|
http:
|
||||||
|
|
|
@ -6,6 +6,7 @@ import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.creat
|
||||||
import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.createDirs
|
import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.createDirs
|
||||||
import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.fileContainsText
|
import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.fileContainsText
|
||||||
import org.domaindrivenarchitecture.provs.test.defaultTestContainer
|
import org.domaindrivenarchitecture.provs.test.defaultTestContainer
|
||||||
|
import org.domaindrivenarchitecture.provs.test.tags.ContainerTest
|
||||||
import org.domaindrivenarchitecture.provs.test.tags.ExtensiveContainerTest
|
import org.domaindrivenarchitecture.provs.test.tags.ExtensiveContainerTest
|
||||||
import org.junit.jupiter.api.Assertions.assertTrue
|
import org.junit.jupiter.api.Assertions.assertTrue
|
||||||
import org.junit.jupiter.api.Disabled
|
import org.junit.jupiter.api.Disabled
|
||||||
|
@ -48,4 +49,17 @@ internal class DevOpsKtTest {
|
||||||
// then
|
// then
|
||||||
assertTrue(res.success)
|
assertTrue(res.success)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ContainerTest
|
||||||
|
fun installKubeconform() {
|
||||||
|
// given
|
||||||
|
val prov = defaultTestContainer()
|
||||||
|
|
||||||
|
// when
|
||||||
|
val res = prov.installKubeconform()
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertTrue(res.success)
|
||||||
|
assertTrue(prov.checkFile("/usr/local/bin/kubeconform"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,7 +103,8 @@ internal class UbuntuProvTest {
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertFalse(result.success)
|
assertFalse(result.success)
|
||||||
assertEquals("sudo: no tty present and no askpass program specified\n", result.err)
|
val expectedMsg = "a password is required"
|
||||||
|
assertTrue(result.err?.contains(expectedMsg) ?: false, "Error: [$expectedMsg] is not found in [${result.err}]")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -119,12 +120,12 @@ class UbuntuUserNeedsPasswordForSudo(private val userName: String = "testuser")
|
||||||
|
|
||||||
override fun imageText(): String {
|
override fun imageText(): String {
|
||||||
return """
|
return """
|
||||||
FROM ubuntu:18.04
|
FROM ubuntu:22.04
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
RUN apt-get update && apt-get -y install sudo
|
RUN apt-get update && apt-get -y install sudo
|
||||||
RUN useradd -m $userName && echo "$userName:$userName" | chpasswd && adduser $userName sudo
|
RUN useradd -m $userName && echo "$userName:$userName" | chpasswd && usermod -aG sudo $userName
|
||||||
|
|
||||||
USER $userName
|
USER $userName
|
||||||
CMD /bin/bash
|
CMD /bin/bash
|
||||||
|
|
|
@ -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
|
|
@ -16,7 +16,9 @@ const val defaultTestContainerName = "provs_test"
|
||||||
private lateinit var prov: Prov
|
private lateinit var prov: Prov
|
||||||
|
|
||||||
fun defaultTestContainer(startMode: ContainerStartMode = ContainerStartMode.USE_RUNNING_ELSE_CREATE): Prov {
|
fun defaultTestContainer(startMode: ContainerStartMode = ContainerStartMode.USE_RUNNING_ELSE_CREATE): Prov {
|
||||||
if (!::prov.isInitialized || !testLocal().containerRuns(defaultTestContainerName) || (startMode == ContainerStartMode.CREATE_NEW_KILL_EXISTING)) { prov = initDefaultTestContainer(startMode) }
|
if (!::prov.isInitialized || !testLocal().containerRuns(defaultTestContainerName) || (startMode == ContainerStartMode.CREATE_NEW_KILL_EXISTING)) {
|
||||||
|
prov = initDefaultTestContainer(startMode)
|
||||||
|
}
|
||||||
return prov
|
return prov
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue