From e6db481ac911583e8892e0ddda309d8d4ac5c616 Mon Sep 17 00:00:00 2001 From: ansgarz Date: Sat, 23 Nov 2024 18:48:25 +0100 Subject: [PATCH] add possibility for monthly reboot of host system of k3s server --- .../ubuntu/cron/infrastructure/CronJobs.kt | 40 +++++++++++++++++++ .../ubuntu/cron/infrstructure/CronJob.kt | 22 ---------- .../provs/server/domain/k3s/K3sConfig.kt | 3 +- .../provs/server/domain/k3s/K3sService.kt | 5 +++ .../CronJobsKtTest.kt} | 31 +++++++++++++- 5 files changed, 76 insertions(+), 25 deletions(-) create mode 100644 src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/cron/infrastructure/CronJobs.kt delete mode 100644 src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/cron/infrstructure/CronJob.kt rename src/test/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/cron/{infrstructure/CronJobKtTest.kt => infrastructure/CronJobsKtTest.kt} (75%) diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/cron/infrastructure/CronJobs.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/cron/infrastructure/CronJobs.kt new file mode 100644 index 0000000..f96ff0f --- /dev/null +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/cron/infrastructure/CronJobs.kt @@ -0,0 +1,40 @@ +package org.domaindrivenarchitecture.provs.framework.ubuntu.cron.infrastructure + +import org.domaindrivenarchitecture.provs.framework.core.Prov +import org.domaindrivenarchitecture.provs.framework.core.ProvResult +import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.checkFile +import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.createDirs +import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.createFile +import org.domaindrivenarchitecture.provs.framework.ubuntu.user.base.whoami + + +/** + * Creates a cron job. + * @param cronFilename e.g. "90_my_cron"; file is created in folder /etc/cron.d/ + * @param schedule in the usual cron-format, examples: "0 * * * *" for each hour, "0 3 1-7 * 1" for the first Monday each month at 3:00, etc + * @param command the executed command + * @param user the user with whom the command will be executed, if null the current user is used + */ +fun Prov.createCronJob(cronFilename: String, schedule: String, command: String, user: String? = null) = task { + val cronUser = user ?: whoami() + val cronLine = "$schedule $cronUser $command\n" + + createDirs("/etc/cron.d/", sudo = true) + createFile("/etc/cron.d/$cronFilename", cronLine, "644", sudo = true, overwriteIfExisting = true) +} + + +/** + * Adds a cronJob for a monthly reboot of the (Linux) system. + * ATTENTION: Use with care!! + */ +fun Prov.scheduleMonthlyReboot() = task { + val shutdown = "/sbin/shutdown" + if (checkFile(shutdown, sudo = true)) { + // reboot each first Tuesday in a month at 3:00 + // use controlled "shutdown" instead of direct "reboot" + createCronJob("50_monthly_reboot", "0 3 1-7 * 2", "shutdown -r now", "root") + } else { + addResultToEval(ProvResult(false, err = "$shutdown not found.")) + } +} diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/cron/infrstructure/CronJob.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/cron/infrstructure/CronJob.kt deleted file mode 100644 index 2d3ccb4..0000000 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/cron/infrstructure/CronJob.kt +++ /dev/null @@ -1,22 +0,0 @@ -package org.domaindrivenarchitecture.provs.framework.ubuntu.cron.infrstructure - -import org.domaindrivenarchitecture.provs.framework.core.Prov -import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.createDirs -import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.createFile -import org.domaindrivenarchitecture.provs.framework.ubuntu.user.base.whoami - - -/** - * Creates a cron job. - * @param cronFilename e.g. "90_my_cron", file is created in folder /etc/cron.d/ - * @param schedule in the usual cron-format, examples: "0 * * * *" for each hour, "0 3 1-7 * 1" for the first Monday each month at 3:00, etc - * @param cronCommand the executed command - * @param cronUser the user with whom the command will be executed, if null the current user is used - */ -fun Prov.createCronJob(cronFilename: String, schedule: String, cronCommand: String, cronUser: String? = null) = task { - val user = cronUser ?: whoami() - val cronLine = "$schedule $user $cronCommand\n" - - createDirs("/etc/cron.d/", sudo = true) - createFile("/etc/cron.d/$cronFilename", cronLine, "644", sudo = true, overwriteIfExisting = true) -} diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/server/domain/k3s/K3sConfig.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/server/domain/k3s/K3sConfig.kt index 49efed1..46746f6 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/server/domain/k3s/K3sConfig.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/server/domain/k3s/K3sConfig.kt @@ -10,7 +10,8 @@ data class K3sConfig( val loopback: Loopback = Loopback(ipv4 = "192.168.5.1", ipv6 = "fc00::5:1"), val certmanager: Certmanager? = null, val echo: Echo? = null, - val reprovision: Reprovision = false + val reprovision: Reprovision = false, + val monthlyReboot: Boolean = false, ) { fun isDualStack(): Boolean { return node.ipv6 != null && loopback.ipv6 != null diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/server/domain/k3s/K3sService.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/server/domain/k3s/K3sService.kt index 8cd97ea..a41a96c 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/server/domain/k3s/K3sService.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/server/domain/k3s/K3sService.kt @@ -2,6 +2,7 @@ package org.domaindrivenarchitecture.provs.server.domain.k3s import org.domaindrivenarchitecture.provs.configuration.infrastructure.DefaultConfigFileRepository import org.domaindrivenarchitecture.provs.framework.core.Prov +import org.domaindrivenarchitecture.provs.framework.ubuntu.cron.infrastructure.scheduleMonthlyReboot 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 @@ -70,6 +71,10 @@ fun Prov.provisionK3s( provisionServerCliConvenience() } + if (k3sConfig.monthlyReboot) { + scheduleMonthlyReboot() + } + installK9s() } diff --git a/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/cron/infrstructure/CronJobKtTest.kt b/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/cron/infrastructure/CronJobsKtTest.kt similarity index 75% rename from src/test/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/cron/infrstructure/CronJobKtTest.kt rename to src/test/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/cron/infrastructure/CronJobsKtTest.kt index 1225274..8944d09 100644 --- a/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/cron/infrstructure/CronJobKtTest.kt +++ b/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/cron/infrastructure/CronJobsKtTest.kt @@ -1,7 +1,8 @@ -package org.domaindrivenarchitecture.provs.framework.ubuntu.cron.infrstructure +package org.domaindrivenarchitecture.provs.framework.ubuntu.cron.infrastructure import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.checkFile import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.createDirs +import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.createFile import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.fileContent import org.domaindrivenarchitecture.provs.framework.ubuntu.install.base.aptInstall import org.domaindrivenarchitecture.provs.framework.ubuntu.user.base.whoami @@ -10,7 +11,8 @@ import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test -class CronJobKtTest { +class CronJobsKtTest { + @Test fun createCronJob_creates_cron_file() { // given @@ -29,6 +31,7 @@ class CronJobKtTest { assertEquals("0 * * * * $expectedUser echo hello > /dev/null 2>&1\n", actualFileContent) } + @Test @Disabled // only for manual execution and manual check for the created files // Test if cron-job is actually running, but needs manual checks @@ -65,4 +68,28 @@ class CronJobKtTest { // after a minute check manually if files exist, e.g. with: sudo docker exec provs_test /bin/bash -c "ls -l tmp" // each minute a new file should be created with the timestamp } + + + @Test + fun scheduleMonthlyReboot() { + // given + val prov = defaultTestContainer() + // create dummy shutdown in test container if missing (containers do usually not have shutdown installed) + prov.createFile( + "/sbin/shutdown", + "dummy file for test of scheduleMonthlyReboot", + sudo = true, + overwriteIfExisting = false + ) + + // when + val result = prov.scheduleMonthlyReboot() + + // then + assertTrue(result.success) + val fqFilename = "/etc/cron.d/50_monthly_reboot" + assertTrue(prov.checkFile(fqFilename), "") + val actualFileContent = prov.fileContent(fqFilename, sudo = true) + assertEquals("0 3 1-7 * 2 root shutdown -r now\n", actualFileContent) + } } \ No newline at end of file