diff --git a/webapp/sources/rudder/rudder-web/src/main/scala/bootstrap/liftweb/RudderConfig.scala b/webapp/sources/rudder/rudder-web/src/main/scala/bootstrap/liftweb/RudderConfig.scala index 69ffef5e1b..51f61cc4d5 100644 --- a/webapp/sources/rudder/rudder-web/src/main/scala/bootstrap/liftweb/RudderConfig.scala +++ b/webapp/sources/rudder/rudder-web/src/main/scala/bootstrap/liftweb/RudderConfig.scala @@ -3214,6 +3214,8 @@ object RudderConfigInit { lazy val mainCampaignService = new MainCampaignService(campaignEventRepo, campaignRepo, uuidGen, 1, 1) lazy val jsonReportsAnalyzer = JSONReportsAnalyser(reportsRepository, propertyRepository) + lazy val instanceUuidPath = root / "opt" / "rudder" / "etc" / "instance-id" + /* * ************************************************* * Bootstrap check actions @@ -3221,6 +3223,7 @@ object RudderConfigInit { */ lazy val allBootstrapChecks = new SequentialImmediateBootStrapChecks( + new CreateInstanceUuid(instanceUuidPath, stringUuidGenerator), new CheckConnections(dataSourceProvider, rwLdap), new CheckTableScore(doobie), new CheckTableUsers(doobie), diff --git a/webapp/sources/rudder/rudder-web/src/main/scala/bootstrap/liftweb/checks/action/CreateInstanceUuid.scala b/webapp/sources/rudder/rudder-web/src/main/scala/bootstrap/liftweb/checks/action/CreateInstanceUuid.scala new file mode 100644 index 0000000000..a2feb8b9db --- /dev/null +++ b/webapp/sources/rudder/rudder-web/src/main/scala/bootstrap/liftweb/checks/action/CreateInstanceUuid.scala @@ -0,0 +1,82 @@ +/* + ************************************************************************************* + * Copyright 2024 Normation SAS + ************************************************************************************* + * + * This file is part of Rudder. + * + * Rudder is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * In accordance with the terms of section 7 (7. Additional Terms.) of + * the GNU General Public License version 3, the copyright holders add + * the following Additional permissions: + * Notwithstanding to the terms of section 5 (5. Conveying Modified Source + * Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU General + * Public License version 3, when you create a Related Module, this + * Related Module is not considered as a part of the work and may be + * distributed under the license agreement of your choice. + * A "Related Module" means a set of sources files including their + * documentation that, without modification of the Source Code, enables + * supplementary functions or services in addition to those offered by + * the Software. + * + * Rudder is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Rudder. If not, see . + + * + ************************************************************************************* + */ + +package bootstrap.liftweb.checks.action + +import better.files.File +import bootstrap.liftweb.BootstrapChecks +import bootstrap.liftweb.BootstrapLogger +import com.normation.errors.IOResult +import com.normation.errors.PureResult +import com.normation.utils.StringUuidGenerator +import com.normation.zio.UnsafeRun +import java.util.UUID +import zio.ZIO + +class CreateInstanceUuid(file: File, uuidGen: StringUuidGenerator) extends BootstrapChecks { + + private val filePath = file.pathAsString + + override def description: String = "Check if server has instance UUID, if not, create it" + + override def checks(): Unit = { + (for { + content <- getOrCreateFile() + _ <- ZIO.unless(isValidUUID(content)) { + writeFile(uuidGen.newUuid) + } + } yield ()) + .chainError(s"An error occured when creating instance UUID in ${filePath} for server") + .tapError(err => BootstrapLogger.error(err.fullMsg)) + .runNow + } + + private def getOrCreateFile(): IOResult[String] = { + IOResult.attempt("Could not create or access file")(file.createIfNotExists().contentAsString) + } + + private def writeFile(uuid: String): IOResult[Unit] = { + IOResult.attempt("Could not write to file")(file.write(uuid)) + } + + /** + * Valid UUID is a parsable one + */ + private def isValidUUID(content: String): Boolean = { + PureResult.attempt(UUID.fromString(content)).isRight + } +} diff --git a/webapp/sources/rudder/rudder-web/src/test/scala/bootstrap/liftweb/checks/action/TestCreateInstanceUuid.scala b/webapp/sources/rudder/rudder-web/src/test/scala/bootstrap/liftweb/checks/action/TestCreateInstanceUuid.scala new file mode 100644 index 0000000000..c0e65e2620 --- /dev/null +++ b/webapp/sources/rudder/rudder-web/src/test/scala/bootstrap/liftweb/checks/action/TestCreateInstanceUuid.scala @@ -0,0 +1,45 @@ +package bootstrap.liftweb.checks.action + +import better.files.File +import com.normation.utils.StringUuidGenerator +import com.normation.utils.StringUuidGeneratorImpl +import org.specs2.mutable.Specification + +class TestCreateInstanceUuid extends Specification { + + "When creating instance ID, we" should { + val uuid: String = "00000000-0000-0000-0000-000000000000" + val uuidGen = new StringUuidGenerator { + val newUuid: String = uuid + } + + "initialize file if it does not exist" in File.temporaryDirectory("rudder-test-instance-id-") { tmpDir => + val file = tmpDir / "instance-id" + val check = new CreateInstanceUuid(file, uuidGen) + file.exists must beFalse + check.checks() + + file.contentAsString must beEqualTo(uuid) + } + + "renew ID when file content is not an UUID" in File.temporaryDirectory("rudder-test-instance-id-") { tmpDir => + val file = tmpDir / "instance-id" + file.write("root-id") + val check = new CreateInstanceUuid(file, uuidGen) + check.checks() + + file.contentAsString must beEqualTo(uuid) + } + + "check for existing ID and not overwrite it" in File.temporaryDirectory("rudder-test-instance-id-") { tmpDir => + val file = tmpDir / "instance-id" + val randomUuidGen = new StringUuidGeneratorImpl() + file.write(uuid) + val check = new CreateInstanceUuid(file, randomUuidGen) + check.checks() + + file.contentAsString must beEqualTo(uuid) + } + } + +}