diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/ncf/TechniqueCompiler.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/ncf/TechniqueCompiler.scala
index d446f66458d..785a5e7283b 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/ncf/TechniqueCompiler.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/ncf/TechniqueCompiler.scala
@@ -39,37 +39,20 @@ package com.normation.rudder.ncf
import better.files.File
import cats.implicits.*
-import com.normation.box.*
import com.normation.errors.*
import com.normation.errors.IOResult
import com.normation.errors.RudderError
-import com.normation.inventory.domain.AgentType
import com.normation.rudder.domain.logger.RuddercLogger
-import com.normation.rudder.domain.logger.TechniqueWriterLoggerPure
-import com.normation.rudder.domain.logger.TimingDebugLoggerPure
-import com.normation.rudder.domain.policies.PolicyMode
import com.normation.rudder.hooks.Cmd
import com.normation.rudder.hooks.CmdResult
import com.normation.rudder.hooks.RunNuCommand
-import com.normation.rudder.ncf.ParameterType.ParameterTypeService
import com.normation.rudder.ncf.migration.MigrateJsonTechniquesService
-import com.normation.rudder.repository.xml.RudderPrettyPrinter
import com.normation.rudder.repository.xml.TechniqueFiles
-import com.normation.rudder.services.policies.InterpolatedValueCompiler
-import com.normation.utils.Control
-import com.normation.zio.currentTimeMillis
import com.normation.zio.currentTimeNanos
import enumeratum.*
import java.nio.charset.StandardCharsets
import java.nio.file.CopyOption
-import java.nio.file.Files
-import java.nio.file.Paths
import java.nio.file.StandardCopyOption
-import net.liftweb.common.Box
-import net.liftweb.common.EmptyBox
-import net.liftweb.common.Full
-import scala.xml.Node as XmlNode
-import scala.xml.NodeSeq
import zio.*
import zio.json.*
import zio.json.yaml.*
@@ -118,7 +101,6 @@ trait TechniqueCompiler {
val compileYaml = compileAtPath(techniquePath)
val success = TechniqueCompilationOutput(
TechniqueCompilerApp.Rudderc,
- fallbacked = false,
resultCode = 0,
fileStatus = Chunk.empty,
msg = "no compilation needed: artifact are up-to-date",
@@ -167,7 +149,6 @@ sealed trait TechniqueCompilerApp extends EnumEntry {
}
object TechniqueCompilerApp extends Enum[TechniqueCompilerApp] {
- case object Webapp extends TechniqueCompilerApp { val name = "webapp" }
case object Rudderc extends TechniqueCompilerApp { val name = "rudderc" }
val values: IndexedSeq[TechniqueCompilerApp] = findValues
@@ -181,14 +162,9 @@ object TechniqueCompilerApp extends Enum[TechniqueCompilerApp] {
* Information about the last compilation.
* We store compiler (info, errors) messages so that they can be used in the technique UI.
* At some point, it will be nice to have structured message in place of string.
- *
- * If the webapp fallbacked more than what was asked (ie, if the compiler asked for, either by
- * default or specifically with a local override was not used but an other was used), then the
- * fallback flag will be set.
*/
final case class TechniqueCompilationOutput(
compiler: TechniqueCompilerApp,
- fallbacked: Boolean,
resultCode: Int,
fileStatus: Chunk[ResourceFile],
msg: String,
@@ -197,8 +173,8 @@ final case class TechniqueCompilationOutput(
)
final case class TechniqueCompilationConfig(
- compiler: Option[TechniqueCompilerApp]
- // other overrides ? Verbosity ?
+ // additional param to give to rudderc
+ args: Option[String]
)
object TechniqueCompilationIO {
@@ -267,7 +243,9 @@ object RuddercResult {
* Option for rudder, like verbosity, etc
*/
final case class RuddercOptions(
- verbose: Boolean
+ verbose: Boolean,
+ // unparsed additional args as a string
+ args: String
)
sealed trait NcfError extends RudderError {
@@ -348,6 +326,7 @@ class RuddercServiceImpl(
def buildCmdLine(techniquePath: File, options: RuddercOptions): Cmd = {
val params = {
+ options.args ::
(if (options.verbose) List("-v") else Nil) :::
("--directory" :: techniquePath.pathAsString :: "build" :: Nil)
}
@@ -390,10 +369,8 @@ class RuddercServiceImpl(
* The main compiler service, which is able to choose between rudderc and webapp based on
* default & local config, and can fallback from rudder to webapp when needed.
*/
-class TechniqueCompilerWithFallback(
- fallbackCompiler: TechniqueCompiler,
+class RuddercTechniqueCompiler(
ruddercService: RuddercService,
- defaultCompiler: TechniqueCompilerApp,
getTechniqueRelativePath: EditorTechnique => String, // get the technique path relative to git root.
val baseConfigRepoPath: String // root of config repos
) extends TechniqueCompiler {
@@ -401,8 +378,8 @@ class TechniqueCompilerWithFallback(
// root of technique repository
val gitDir: File = File(baseConfigRepoPath)
- val compilationConfigFilename = "compilation-config.yml"
- val compilationOutputFilename = "compilation-output.yml"
+ val compilationConfigFilename: String = "compilation-config.yml"
+ val compilationOutputFilename: String = "compilation-output.yml"
def getCompilationOutputFile(technique: EditorTechnique): File =
gitDir / getTechniqueRelativePath(technique) / compilationOutputFilename
@@ -420,14 +397,12 @@ class TechniqueCompilerWithFallback(
_ <- ZIO.whenZIO(IOResult.attempt(outPutFile.exists)) {
IOResult.attempt(outPutFile.delete()) // clean-up previous output
}
- // if compiler app is defined, recover is forbidden
- app = config.compiler
// clean-up generated files
_ <- ZIO.foreach(TechniqueFiles.Generated.all) { name =>
IOResult.attempt((gitDir / getTechniqueRelativePath(technique) / name).delete(true))
}
- res <- compileTechniqueInternal(technique, app)
- _ <- ZIO.when(res.fallbacked == true || res.resultCode != 0) {
+ res <- compileTechniqueInternal(technique, config.args.getOrElse(""))
+ _ <- ZIO.when(res.resultCode != 0) {
writeCompilationOutputFile(technique, res)
}
} yield res
@@ -440,53 +415,24 @@ class TechniqueCompilerWithFallback(
* In case of error or fallback, the compilation.yml file is updated.
*/
def compileTechniqueInternal(
- technique: EditorTechnique,
- // if app is given, then recover is forbidden
- app: Option[TechniqueCompilerApp]
+ technique: EditorTechnique,
+ additionalArgs: String
): IOResult[TechniqueCompilationOutput] = {
val verbose = false
- val ruddercOptions = RuddercOptions(verbose)
+ val ruddercOptions = RuddercOptions(verbose, additionalArgs)
val ruddercAll = ruddercService.compile(gitDir / getTechniqueRelativePath(technique), ruddercOptions)
- // recover from rudderc if result says so
- def recoverIfNeeded(app: TechniqueCompilerApp, r: RuddercResult): IOResult[TechniqueCompilationOutput] = {
- val ltc = TechniqueCompilationOutput(
- app,
- fallbacked = false,
+ ruddercAll.map { r =>
+ TechniqueCompilationOutput(
+ TechniqueCompilerApp.Rudderc,
resultCode = r.code,
fileStatus = r.fileStatus,
msg = r.msg,
stdout = r.stdout,
stderr = r.stderr
)
- r match {
- case _: RuddercResult.Fail =>
- // fallback but keep rudderc error for logs
- fallbackCompiler
- .compileTechnique(technique) *> ltc.copy(compiler = TechniqueCompilerApp.Webapp, fallbacked = true).succeed
- case _ => ltc.succeed
- }
- }
-
- app match {
- case None =>
- ruddercAll.flatMap(res => recoverIfNeeded(defaultCompiler, res))
- case Some(TechniqueCompilerApp.Webapp) => // in that case, we can't fallback even more, so the result is final
- fallbackCompiler.compileTechnique(technique)
- case Some(TechniqueCompilerApp.Rudderc) =>
- ruddercAll.map(r => {
- TechniqueCompilationOutput(
- TechniqueCompilerApp.Rudderc,
- fallbacked = false,
- resultCode = r.code,
- fileStatus = r.fileStatus,
- msg = r.msg,
- stdout = r.stdout,
- stderr = r.stderr
- )
- })
}
}
@@ -500,7 +446,8 @@ class TechniqueCompilerWithFallback(
content <- IOResult.attempt(s"Error when writing compilation file for technique '${getTechniqueRelativePath(technique)}'") {
val config = getCompilationConfigFile(technique)
if (config.exists) { // this is optional
- Some(config.contentAsString(StandardCharsets.UTF_8))
+ val s = config.contentAsString(StandardCharsets.UTF_8)
+ if (s.strip().isEmpty) None else Some(s)
} else {
None
}
@@ -523,877 +470,3 @@ class TechniqueCompilerWithFallback(
} yield ()
}
}
-
-/*
- * This class implements the old webapp-based compiler used in Rudder 7.x.
- * It is now use as a fallback for when rudderc fails or if configured.
- */
-class WebappTechniqueCompiler(
- translater: InterpolatedValueCompiler,
- xmlPrettyPrinter: RudderPrettyPrinter,
- parameterTypeService: ParameterTypeService,
- editorTechniqueReader: EditorTechniqueReader,
- getTechniqueRelativePath: EditorTechnique => String, // get the technique path relative to git root.
- val baseConfigRepoPath: String // root of config repos
-) extends TechniqueCompiler {
- private val cfengineTechniqueWriter =
- new ClassicTechniqueWriter(baseConfigRepoPath, parameterTypeService, getTechniqueRelativePath)
- private val dscTechniqueWriter =
- new DSCTechniqueWriter(baseConfigRepoPath, translater, parameterTypeService, getTechniqueRelativePath)
- private val agentSpecific = cfengineTechniqueWriter :: dscTechniqueWriter :: Nil
-
- override def compileTechnique(technique: EditorTechnique): IOResult[TechniqueCompilationOutput] = {
- for {
- methods <- editorTechniqueReader.getMethodsMetadata
- _ <- writeAgentFiles(technique, methods, onlyPS1 = false)
- time_1 <- currentTimeMillis
- _ <- writeMetadata(technique, methods)
- time_2 <- currentTimeMillis
- _ <- TimingDebugLoggerPure.trace(
- s"writeTechnique: generating metadata for technique '${technique.name}' took ${time_2 - time_1}ms"
- )
- } yield {
- TechniqueCompilationOutput(
- TechniqueCompilerApp.Webapp,
- fallbacked = false,
- resultCode = 0,
- fileStatus = TechniqueFiles.Generated.all.map(f => ResourceFile(f, ResourceFileState.New)),
- msg = s"Technique '${getTechniqueRelativePath(technique)}' written by webapp",
- stdout = "",
- stderr = ""
- )
- }
- }
-
- def writeAgentFiles(
- technique: EditorTechnique,
- methods: Map[BundleName, GenericMethod],
- onlyPS1: Boolean
- ): IOResult[Seq[String]] = {
- val agents = if (onlyPS1) dscTechniqueWriter :: Nil else agentSpecific
-
- for {
- time_1 <- currentTimeMillis
- // Create/update agent files, filter None by flattening to list
- files <- ZIO.foreach(agents)(_.writeAgentFiles(technique, methods)).map(_.flatten)
- _ <- TechniqueWriterLoggerPure.debug(s"writeAgentFiles for technique ${technique.name} is ${files.mkString("\n")}")
- time_2 <- currentTimeMillis
- _ <- TimingDebugLoggerPure.trace(
- s"writeTechnique: writing agent files for technique '${technique.name}' took ${time_2 - time_1}ms"
- )
- } yield {
- files
- }
- }
-
- def writeMetadata(technique: EditorTechnique, methods: Map[BundleName, GenericMethod]): IOResult[String] = {
-
- val metadataPath = s"${getTechniqueRelativePath(technique)}/metadata.xml"
-
- val path = s"${baseConfigRepoPath}/${metadataPath}"
- for {
- content <- techniqueMetadataContent(technique, methods).map(n => xmlPrettyPrinter.format(n)).toIO
- _ <- IOResult.attempt(s"An error occurred while creating metadata file for Technique '${technique.name}'") {
- implicit val charSet = StandardCharsets.UTF_8
- val file = File(path).createFileIfNotExists(true)
- file.write(content)
- }
- } yield {
- metadataPath
- }
- }
-
- def techniqueMetadataContent(technique: EditorTechnique, methods: Map[BundleName, GenericMethod]): PureResult[XmlNode] = {
-
- def reportingValuePerBlock(block: MethodBlock): PureResult[NodeSeq] = {
-
- for {
- childs <- reportingSections(block.calls)
- } yield {
- if (childs.isEmpty) {
- NodeSeq.Empty
- } else {
- val reportingLogic = block.reportingLogic.value
-
- }
- }
- }
-
- def reportingValuePerMethod(call: MethodCall): PureResult[Seq[XmlNode]] = {
- for {
- method <- methods.get(call.method) match {
- case None =>
- Left(
- MethodNotFound(
- s"Cannot find method ${call.method.value} when writing a method call of Technique '${technique.id.value}'",
- None
- )
- )
- case Some(m) => Right(m)
- }
- class_param <- call.parameters.find(_._1 == method.classParameter) match {
- case None =>
- Left(
- MethodNotFound(
- s"Cannot find call parameter of ${call.method.value} when writing a method call of Technique '${technique.id.value}'",
- None
- )
- )
- case Some(m) => Right(m._2)
- }
-
- } yield {
- val name = if (call.component.isEmpty) {
- method.name
- } else {
- call.component
- }
-
-
- }
- }
-
- def reportingSections(sections: List[MethodElem]) = {
- val expectedReportingMethodsWithValues =
- sections.collect { case m: MethodCall => m }.filterNot(m => m.disabledReporting || m.method.value.startsWith("_"))
- val expectedGroupReportingMethodsWithValues = sections.collect { case m: MethodBlock => m }
-
- for {
- uniqueSection <- expectedReportingMethodsWithValues.traverse(reportingValuePerMethod)
- groupSection <- expectedGroupReportingMethodsWithValues.traverse(reportingValuePerBlock)
- } yield {
- groupSection ::: uniqueSection
- }
- }
-
- def parameterSection(parameter: TechniqueParameter): Seq[XmlNode] = {
- // Here we translate technique parameters into Rudder variables
- // ncf technique parameters ( having an id and a name, which is used inside the technique) were translated into Rudder variables spec
- // (having a name, which acts as an id, and allow to do templating on techniques, and a description which is presented to users) with the following Rule
- // ncf Parameter | Rudder variable
- // id | name
- // name | description
-
- {parameter.id.value.toUpperCase()}
- {parameter.name}
- {parameter.description.getOrElse(parameter.name)}
- {parameter.documentation.getOrElse("")}
-
- textarea
- {parameter.mayBeEmpty}
-
-
- }
- // Regroup method calls from which we expect a reporting
- // We filter those starting by _, which are internal methods
-
- for {
- reportingSection <- reportingSections(technique.calls.toList)
- agentSpecificSection <- agentSpecific.traverse(_.agentMetadata(technique, methods))
- } yield {
-
- {
- if (technique.parameters.nonEmpty) {
- separated-with-parameters
- true
- } else NodeSeq.Empty
- }{technique.description}
- {technique.documentation}
- true{agentSpecificSection}
- {reportingSection}{
- if (technique.parameters.nonEmpty) {
-
- {technique.parameters.map(parameterSection)}
-
- } else NodeSeq.Empty
- }
-
-
- }
- }
-
- // root of technique repository
- val gitDir: File = File(baseConfigRepoPath)
-
- val compilationConfigFilename = "compilation-config.yml"
- val compilationOutputFilename = "compilation-output.yml"
-
- def getCompilationOutputFile(technique: EditorTechnique): File =
- gitDir / getTechniqueRelativePath(technique) / compilationOutputFilename
-
- def getCompilationConfigFile(technique: EditorTechnique): File =
- gitDir / getTechniqueRelativePath(technique) / compilationConfigFilename
-
-}
-
-trait AgentSpecificTechniqueWriter {
-
- def writeAgentFiles(technique: EditorTechnique, methods: Map[BundleName, GenericMethod]): IOResult[Seq[String]]
-
- def agentMetadata(technique: EditorTechnique, methods: Map[BundleName, GenericMethod]): PureResult[NodeSeq]
-}
-
-class ClassicTechniqueWriter(
- basePath: String,
- parameterTypeService: ParameterTypeService,
- getTechniqueRelativePath: EditorTechnique => String // get the technique path relative to git root.
-) extends AgentSpecificTechniqueWriter {
-
- // We need to add a reporting bundle for this method to generate a na report for any method with a condition != any/cfengine (which ~= true
- def truthyCondition(condition: String): Boolean = condition.isEmpty || condition == "any" || condition == "cfengine-community"
- def methodCallNeedReporting(methods: Map[BundleName, GenericMethod], parentBlock: List[MethodBlock])(
- call: MethodCall
- ): Boolean = {
- val condition = formatCondition(call, parentBlock)
- methods
- .get(call.method)
- .map(m => !m.agentSupport.contains(AgentType.CfeCommunity) || !truthyCondition(condition))
- .getOrElse(true)
- }
-
- def elemNeedReportingBundle(methods: Map[BundleName, GenericMethod], parentBlock: List[MethodBlock])(
- elem: MethodElem
- ): Boolean = {
- elem match {
- case c: MethodCall => methodCallNeedReporting(methods, parentBlock)(c)
- case b: MethodBlock => !truthyCondition(b.condition) || b.calls.exists(elemNeedReportingBundle(methods, b :: parentBlock))
- }
- }
-
- def formatCondition(methodCall: MethodCall, parentBlock: List[MethodBlock]): String = {
- (parentBlock.map(_.condition).filterNot(truthyCondition), truthyCondition(methodCall.condition)) match {
- case (Nil, true) => "any"
- case (list, true) => list.mkString("(", ").(", ")")
- case (Nil, false) => methodCall.condition
- case (list, false) => list.mkString("(", ").(", s".${methodCall.condition})")
- }
- }
-
- def needReportingBundle(technique: EditorTechnique, methods: Map[BundleName, GenericMethod]): Boolean =
- technique.calls.exists(elemNeedReportingBundle(methods, Nil))
-
- def canonifyCondition(methodCall: MethodCall, parentBlock: List[MethodBlock]): String = {
- formatCondition(methodCall, parentBlock).replaceAll("""(\$\{[^\}]*})""", """",canonify("$1"),"""")
- }
-
- // regex to match double quote characters not preceded by a backslash, and backslash not preceded by backslash or not followed by a backslash or a quote (simple or double)
- def escapeCFEngineString(value: String): String = value.replaceAll("""\\""", """\\\\""").replaceAll(""""""", """\\"""")
-
- def reportingContextInBundle(args: Seq[String]): String = {
- s"_method_reporting_context_v4(${convertArgsToBundleCall(args)})"
- }
-
- def convertArgsToBundleCall(args: Seq[String]): String = {
- args.map(escapeCFEngineString(_)).map(""""${""" + _ + """}"""").mkString(",")
- }
-
- override def writeAgentFiles(technique: EditorTechnique, methods: Map[BundleName, GenericMethod]): IOResult[Seq[String]] = {
-
- // increment of the bundle number in the technique, used by createCallingBundle
- var bundleIncrement = 0
-
- val bundleParams =
- if (technique.parameters.nonEmpty) technique.parameters.map(_.name).mkString("(", ",", ")") else ""
-
- // generate a bundle which encapsulate the method_reporting_context + the actual method call
- // and the method to call this bundle
- // Params are:
- // * condition when to call this bundle
- // * the methodCall itself from the technique editor
- // * the class parameter _value_
- // * all the parameters already converted to cfengine format
- // * if it's for the NAReports bundle (reverse the condition from if to unless)
- // Returns the bundle agent, and the "promised_" usebundle => bundle_created
- def createCallingBundle(
- condition: String,
- call: MethodCall,
- classParameterValue: String,
- params: Seq[String],
- forNaReport: Boolean
- ) = {
- val promiser = call.id + "_${report_data.directive_id}"
-
- val filterOnMethod = forNaReport match {
- case false => "if"
- case true => "unless"
- }
-
- val method = methods.get(call.method)
-
- val name = if (call.component.isEmpty) method.map(_.name).getOrElse(call.method.value) else call.component
-
- // Reporting argument
- val reportingValues = escapeCFEngineString(name) ::
- escapeCFEngineString(classParameterValue) ::
- call.id :: Nil
- // create the bundle arguments:
- // there are 3 arguments corresponding to the reportingValues, (need to be quoted)
- // the rest is for the methodArgs.
- val allValues = reportingValues.map(x => s""""${x}"""") ::: params.toList
-
- // Get all bundle argument names
- val bundleName = (technique.id.value + "_gm_" + bundleIncrement).replaceAll("-", "_")
- bundleIncrement = bundleIncrement + 1
-
- val reportingArgs = "c_name" :: "c_key" :: "report_id" :: Nil
-
- val (bundleArgs, bundleNameAndArg) = forNaReport match {
- case false =>
- val args = params.toList.zipWithIndex.map {
- case (_, id) =>
- method.flatMap(_.parameters.get(id.toLong).map(_.id.value)).getOrElse("arg_" + id)
- }
- (args, s"""${call.method.value}(${convertArgsToBundleCall(args)});""")
-
- case true =>
- // special case for log_na_rudder; args muse be called with @
- val args = "message" :: "class_parameter" :: "unique_prefix" :: "args" :: Nil
- (args, """log_na_rudder("${message}","${class_parameter}","${unique_prefix}",@{args});""")
- }
-
- val allArgs = reportingArgs ::: bundleArgs
-
- // The bundle that will effectively act
- val bundleActing = {
- val bundleCall = {
- s""" "${promiser}" usebundle => ${reportingContextInBundle(reportingArgs)};
- | "${promiser}" usebundle => ${bundleNameAndArg}""".stripMargin('|')
- }
-
- val bundleCallWithReportOption = {
- if (call.disabledReporting) {
- s""" "${promiser}" usebundle => disable_reporting;
- |${bundleCall}
- | "${promiser}" usebundle => enable_reporting;""".stripMargin('|')
- } else {
- bundleCall
- }
- }
- val bundleCallWithReportOptionAndPolicyMode = {
- call.policyMode match {
- case Some(pm) =>
- s""" "${promiser}" usebundle => push_dry_run_mode("${pm == PolicyMode.Audit}");
- |${bundleCallWithReportOption}
- | "${promiser}" usebundle => pop_dry_run_mode();""".stripMargin('|')
- case None =>
- bundleCallWithReportOption
- }
- }
-
- s"""bundle agent ${bundleName}(${allArgs.mkString(", ")}) {
- | methods:
- |${bundleCallWithReportOptionAndPolicyMode}
- |}
- |""".stripMargin('|')
- }
-
- // the call to the bundle
- val callBundle = {
- s""" "${promiser}" usebundle => ${bundleName}(${allValues.mkString(", ")}),
- | ${promiser.map(_ => ' ')} ${filterOnMethod} => concat("${condition}");""".stripMargin('|')
-
- }
- (bundleActing, callBundle)
- }
-
- // returns the bundle acting, and the method to call the bundle
- def bundleMethodCall(parentBlocks: List[MethodBlock])(method: MethodElem): Option[(String, String)] = {
- method match {
- case call: MethodCall =>
- (for {
- method_info <- methods.get(call.method)
- (_, classParameterValue) <- call.parameters.find(_._1 == method_info.classParameter)
-
- params <- Control.traverse(method_info.parameters) { p =>
- for {
- (_, value) <- Box(call.parameters.find(_._1 == p.id))
- escaped <- parameterTypeService.translate(value, p.parameterType, AgentType.CfeCommunity).toBox
- } yield {
- escaped
- }
- }
- } yield {
- val condition = canonifyCondition(call, parentBlocks)
- createCallingBundle(condition, call, classParameterValue, params, forNaReport = false)
- })
- case block: MethodBlock =>
- val bundleAndMethodCallsList = block.calls.flatMap(bundleMethodCall(block :: parentBlocks))
- val methodBundles = bundleAndMethodCallsList.map(_._1).mkString("")
- val methodCalls = bundleAndMethodCallsList.map(_._2).mkString("\n")
- val methodCallsWithPolicyMode = {
- block.policyMode match {
- case Some(pm) =>
- val promiser = block.id + "_${report_data.directive_id}"
- s""" "${promiser}" usebundle => push_dry_run_mode("${pm == PolicyMode.Audit}");
- |${methodCalls}
- | "${promiser}" usebundle => pop_dry_run_mode();""".stripMargin('|')
- case None =>
- methodCalls
- }
- }
- Some((methodBundles, methodCallsWithPolicyMode))
- }
- }
- val bundleAndMethodCallsList = technique.calls.flatMap(bundleMethodCall(Nil))
-
- val methodBundles = bundleAndMethodCallsList.map(_._1).mkString("")
- val methodCalls = bundleAndMethodCallsList.map(_._2).mkString("\n")
-
- val content = {
- import net.liftweb.json.*
- import net.liftweb.json.JsonDSL.*
-
- s"""# @name ${technique.name}
- |# @description ${technique.description.replaceAll("\\R", "\n# ")}
- |# @version ${technique.version.value}
- |${technique.parameters.map { p =>
- val param =
- ("name" -> p.name) ~ ("id" -> p.id.value) ~ ("description" -> p.description.map(_.replaceAll("\\R", "£# ")))
- // for historical reason, we want to have real \n in the string, and not the char \n (because of how liftweb print them)
- s"""# @parameter ${compactRender(param).replaceAll("£#", "\n#")}"""
- }.mkString("\n")}
- |
- |bundle agent ${technique.id.value}${bundleParams}
- |{
- | vars:
- | "resources_dir" string => "$${this.promise_dirname}/resources";
- | classes:
- | "pass3" expression => "pass2";
- | "pass2" expression => "pass1";
- | "pass1" expression => "any";
- | methods:
- |${methodCalls}
- |}
- |
- |${methodBundles}""".stripMargin('|')
- }
-
- implicit val charset = StandardCharsets.UTF_8
- val techFile =
- File(basePath) / getTechniqueRelativePath(technique) / "technique.cf"
- val t = {
- IOResult.attempt(s"Could not write na reporting Technique file '${technique.name}' in path ${techFile.path.toString}") {
- techFile.createFileIfNotExists(true).write(content.stripMargin('|'))
- File(basePath).relativize(techFile.path).toString
- }
- }
-
- val t2 = if (!needReportingBundle(technique, methods)) {
- ZIO.succeed(Nil)
- } else {
-
- val bundleParams =
- if (technique.parameters.nonEmpty) technique.parameters.map(_.name).mkString("(", ",", ")") else ""
- val args = technique.parameters.map(p => s"$${${p.name}}").mkString(", ")
-
- def bundleMethodCall(parentBlocks: List[MethodBlock])(method: MethodElem): Option[(String, String)] = {
- method match {
- case c: MethodCall =>
- val call = MethodCall.renameParams(c, methods).copy(method = BundleName("log_na_rudder"))
- (for {
- method_info <- methods.get(c.method)
- // Skip that method if name starts with _
- if !c.method.value.startsWith("_")
- (_, classParameterValue) <- call.parameters.find(_._1 == method_info.classParameter)
-
- escapedClassParameterValue = escapeCFEngineString(classParameterValue)
- classPrefix = s"$${class_prefix}_${method_info.classPrefix}_${escapedClassParameterValue}"
-
- } yield {
- def naReport(condition: String, message: String) = {
-
- val params =
- s""""${message}"""" :: s""""${escapedClassParameterValue}"""" :: s""""${classPrefix}"""" :: "@{args}" :: Nil
-
- createCallingBundle(condition, call, classParameterValue, params, forNaReport = true)
- }
-
- // Write report if the method does not support CFEngine ...
- (if (!method_info.agentSupport.contains(AgentType.CfeCommunity)) {
- val message = s"""'${method_info.name}' method is not available on Linux Rudder agent, skip"""
- val condition = "false"
- Some((condition, message))
- } else {
- // ... or if the condition needs rudder_reporting
- if (methodCallNeedReporting(methods, parentBlocks)(c)) {
- val condition = formatCondition(c, parentBlocks)
- val message =
- s"""Skipping method '${method_info.name}' with key parameter '${escapedClassParameterValue}' since condition '${condition}' is not reached"""
- val canon_condition = s"${canonifyCondition(call, parentBlocks)}"
- Some((canon_condition, message))
- } else {
- None
- }
- }).map((naReport _).tupled)
- }).flatten
- case block: MethodBlock =>
- val bundleAndMethodCallsList = block.calls.flatMap(bundleMethodCall(block :: parentBlocks))
- val methodBundles = bundleAndMethodCallsList.map(_._1).mkString("")
- val methodCalls = bundleAndMethodCallsList.map(_._2).mkString("\n")
- val methodCallsWithPolicyMode = {
- block.policyMode match {
- case Some(pm) =>
- val promiser = block.id + "_${report_data.directive_id}"
- s""" "${promiser}" usebundle => push_dry_run_mode("${pm == PolicyMode.Audit}");
- |${methodCalls}
- | "${promiser}" usebundle => pop_dry_run_mode();""".stripMargin('|')
- case None =>
- methodCalls
- }
- }
- Some((methodBundles, methodCallsWithPolicyMode))
- }
- }
- val bundleAndMethodCallsList = technique.calls.flatMap(bundleMethodCall(Nil))
-
- val methodBundles = bundleAndMethodCallsList.map(_._1).mkString("")
- val methodCalls = bundleAndMethodCallsList.map(_._2).mkString("\n")
-
- val content = {
- s"""bundle agent ${technique.id.value}_rudder_reporting${bundleParams}
- |{
- | vars:
- | "args" slist => { ${args} };
- | "report_param" string => join("_", args);
- | "full_class_prefix" string => canonify("${technique.id.value}_rudder_reporting_$${report_param}");
- | "class_prefix" string => string_head("$${full_class_prefix}", "1000");
- |
- | methods:
- |${methodCalls}
- |}
- |
- |${methodBundles}"""
- }
-
- val reportingFile = File(
- basePath
- ) / getTechniqueRelativePath(technique) / TechniqueFiles.Generated.cfengineReporting
- IOResult.attempt(
- s"Could not write na reporting Technique file '${technique.name}' in path ${reportingFile.path.toString}"
- ) {
- reportingFile.createFileIfNotExists(true).write(content.stripMargin('|'))
- Seq(File(basePath).relativize(reportingFile.path).toString)
- }
- }
-
- for {
- tech <- t
- repo <- t2
- } yield {
- tech +: repo
- }
- }
-
- override def agentMetadata(technique: EditorTechnique, methods: Map[BundleName, GenericMethod]): PureResult[NodeSeq] = {
-
- val needReporting = needReportingBundle(technique, methods)
- val xml =
-
- {technique.id.value}
- {if (needReporting) {technique.id.value}_rudder_reporting else NodeSeq.Empty}
-
-
-
- true
-
- {
- if (needReporting) {
-
- true
-
- } else NodeSeq.Empty
- }
- {
- for {
- resource <- technique.resources
- if resource.state != ResourceFileState.Deleted
- } yield {
-
- false
- {technique.id.value}/{technique.version.value}/resources/{resource.path}
-
- }
- }
-
-
- Right(xml)
- }
-
-}
-
-class DSCTechniqueWriter(
- basePath: String,
- translater: InterpolatedValueCompiler,
- parameterTypeService: ParameterTypeService,
- getTechniqueRelativePath: EditorTechnique => String // get the technique path relative to git root.
-
-) extends AgentSpecificTechniqueWriter {
- implicit class IndentString(s: String) {
- // indent all lines EXCLUDING THE FIRST by the given number of spaces
- def indentNextLines(spaces: Int): String = s.linesIterator.mkString("\n" + " " * spaces)
- }
-
- // WARNING: this is extremely likely false, it MUST be done in the technique editor or
- // via a full fledge parser of conditions
- def canonifyCondition(methodCall: MethodCall, parentBlocks: List[MethodBlock]): String = {
- formatCondition(methodCall, parentBlocks).replaceAll(
- """(\$\{[^\}]*})""",
- """" + ([Rudder.Condition]::canonify($1)) + """"
- )
- }
-
- def truthyCondition(condition: String): Boolean = condition.isEmpty || condition == "any"
-
- def formatCondition(methodCall: MethodCall, parentBlock: List[MethodBlock]): String = {
- (parentBlock.map(_.condition).filterNot(truthyCondition), truthyCondition(methodCall.condition)) match {
- case (Nil, true) => "any"
- case (list, true) => list.mkString("(", ").(", ")")
- case (Nil, false) => methodCall.condition
- case (list, false) => list.mkString("(", ").(", s".${methodCall.condition})")
- }
- }
-
- // needed for override in tests
- def pathOf(technique: EditorTechnique, filename: String): String =
- s"${getTechniqueRelativePath(technique)}}/${filename}"
-
- def formatDscMethodBlock(techniqueName: String, methods: Map[BundleName, GenericMethod], parentBlocks: List[MethodBlock])(
- method: MethodElem
- ): PureResult[List[String]] = {
- method match {
- case c: MethodCall =>
- val call = MethodCall.renameParams(c, methods)
- if (call.method.value.startsWith("_")) {
- Right(Nil)
- } else {
-
- (for {
-
- // First translate parameters to Dsc values
- params <-
- ((call.parameters.toList).traverse {
- case (id, arg) =>
- translater.translateToAgent(arg, AgentType.Dsc) match {
- case Full(dscValue) =>
- parameterTypeService
- .translate(
- dscValue,
- methods
- .get(call.method)
- .flatMap(_.parameters.find(_.id == id))
- .map(_.parameterType)
- .getOrElse(ParameterType.StringParameter),
- AgentType.Dsc
- )
- .map(dscValue => (id, dscValue))
-
- case eb: EmptyBox =>
- val fail =
- eb ?~! s"Error when translating parameter '${arg}' of technique of method ${call.method} of technique ${techniqueName}"
- Left(IOError(fail.messageChain, None))
- }
- }).map(_.toMap)
-
- // Translate condition
- condition <- translater.translateToAgent(canonifyCondition(call, parentBlocks), AgentType.Dsc) match {
- case Full(c) => Right(c)
- case eb: EmptyBox =>
- val fail =
- eb ?~! s"Error when translating condition '${call.condition}' of technique of method ${call.method} of technique ${techniqueName}"
- Left(IOError(fail.messageChain, None))
- }
-
- // Check if method exists
- method <- methods.get(call.method) match {
- case Some(method) =>
- Right(method)
- case None =>
- Left(
- MethodNotFound(
- s"Method '${call.method.value}' not found when writing dsc Technique '${techniqueName}' methods calls",
- None
- )
- )
- }
- // Check if class parameter is correctly defined
- classParameter <- params.get(method.classParameter) match {
- case Some(classParameter) =>
- Right(classParameter)
- case None =>
- Left(
- MethodNotFound(
- s"Parameter '${method.classParameter.value}' for method '${method.id.value}' not found when writing dsc Technique '${techniqueName}' methods calls",
- None
- )
- )
- }
-
- methodParams = params.map { case (id, arg) => s"""-${id.validDscName} ${arg}""" }.mkString(" ")
- effectiveCall =
- s"""$$call = ${call.method.validDscName} ${methodParams} -PolicyMode $$policyMode""" // methodParams can be multiline text
- // so we should never indent it
- methodContext = s"""$$methodContext = Compute-Method-Call @reportParams -MethodCall $$call
- |$$localContext.merge($$methodContext)
- |""".stripMargin
-
- } yield {
-
- val name = if (call.component.isEmpty) {
- method.name
- } else {
- call.component
- }
- val componentName = name.replaceAll("\"", "`\"")
- val disableReporting = if (call.disabledReporting) {
- "true"
- } else {
- "false"
- }
- val methodCall = if (method.agentSupport.contains(AgentType.Dsc)) {
- if (condition == "any") {
- s""" ${effectiveCall}
- | ${methodContext.indentNextLines(2)}""".stripMargin
- } else {
- s""" $$class = "${condition}"
- | if ($$localContext.Evaluate($$class)) {
- | ${effectiveCall}
- | ${methodContext.indentNextLines(4)}
- | } else {
- | Rudder-Report-NA @reportParams
- | }""".stripMargin('|')
- }
- } else {
- s" Rudder-Report-NA @reportParams"
- }
-
- val policyMode = {
- call.policyMode match {
- case None =>
- parentBlocks.find(_.policyMode.isDefined).flatMap(_.policyMode) match {
- case None =>
- "$policyMode"
- case Some(PolicyMode.Audit) => "([Rudder.PolicyMode]::Audit)"
- case Some(PolicyMode.Enforce) => "([Rudder.PolicyMode]::Enforce)"
- }
- case Some(PolicyMode.Audit) => "([Rudder.PolicyMode]::Audit)"
- case Some(PolicyMode.Enforce) => "([Rudder.PolicyMode]::Enforce)"
- }
- }
- s"""| $$reportId=$$reportIdBase+"${call.id}"
- | $$componentKey = ${classParameter}
- | $$reportParams = @{
- | ClassPrefix = ([Rudder.Condition]::canonify(("${method.classPrefix}_" + $$componentKey)))
- | ComponentKey = $$componentKey
- | ComponentName = "${componentName}"
- | PolicyMode = ${policyMode}
- | ReportId = $$reportId
- | DisableReporting = $$${disableReporting}
- | TechniqueName = $$techniqueName
- | }
- |${methodCall}""".stripMargin('|') :: Nil
-
- })
- }
-
- case block: MethodBlock =>
- block.calls.flatTraverse(formatDscMethodBlock(techniqueName, methods, block :: parentBlocks))
- }
- }
-
- override def writeAgentFiles(technique: EditorTechnique, methods: Map[BundleName, GenericMethod]): IOResult[Seq[String]] = {
-
- val parameters = technique.parameters.map { p =>
- val mandatory = if (p.mayBeEmpty) "$false" else "$true"
- s""" [parameter(Mandatory=${mandatory})]
- | [string]$$${p.name},"""
- }.mkString("\n").stripMargin('|')
-
- val techniquePath = getTechniqueRelativePath(technique) + "/technique.ps1"
- val techniqueParameters =
- technique.parameters.map(p => s""" "${p.name}" = $$${p.name}""").mkString("\n")
-
- for {
-
- calls <- technique.calls.toList.flatTraverse(formatDscMethodBlock(technique.name, methods, Nil)).toIO
-
- content = {
- s"""|function ${technique.id.validDscName} {
- | [CmdletBinding()]
- | param (
- | [parameter(Mandatory=$$true)]
- | [string]$$reportId,
- | [parameter(Mandatory=$$true)]
- | [string]$$techniqueName,
- |${parameters}
- | [Rudder.PolicyMode]$$policyMode
- | )
- | $$techniqueParams = @{
- |${techniqueParameters}
- | }
- | BeginTechniqueCall -Name $$techniqueName -Parameters $$techniqueParams
- | $$reportIdBase = $$reportId.Substring(0,$$reportId.Length-1)
- | $$localContext = New-Object -TypeName "Rudder.Context" -ArgumentList @($$techniqueName)
- | $$localContext.Merge($$system_classes)
- | $$resources_dir = $$PSScriptRoot + "\\resources"
- |
- |${calls.mkString("\n\n")}
- | EndTechniqueCall -Name $$techniqueName
- |}""".stripMargin('|')
- }
-
- path <- IOResult.attempt(s"Could not find dsc Technique '${technique.name}' in path ${basePath}/${techniquePath}")(
- Paths.get(s"${basePath}/${techniquePath}")
- )
- // Powershell files needs to have a BOM added at the beginning of all files when using UTF8 enoding
- // See https://docs.microsoft.com/en-us/windows/desktop/intl/using-byte-order-marks
- // Bom, three bytes: EF BB BF https://en.wikipedia.org/wiki/Byte_order_mark
- contentWithBom = Array(239.toByte, 187.toByte, 191.toByte) ++ content.getBytes(StandardCharsets.UTF_8)
-
- files <- IOResult.attempt(s"Could not write dsc Technique file '${technique.name}' in path ${basePath}/${techniquePath}") {
- Files.createDirectories(path.getParent)
- Files.write(path, contentWithBom.toArray)
- }
- } yield {
- techniquePath :: Nil
- }
- }
-
- override def agentMetadata(technique: EditorTechnique, methods: Map[BundleName, GenericMethod]): PureResult[NodeSeq] = {
- val xml =
-
- {technique.id.validDscName}
-
-
-
- true
- {
- for {
- resource <- technique.resources
- if resource.state != ResourceFileState.Deleted
- } yield {
-
- false
- {technique.id.value}/{technique.version.value}/resources/{resource.path}
-
- }
- }
-
-
- Right(xml)
- }
-
-}
diff --git a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/cfclerk/services/JGitRepositoryTest.scala b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/cfclerk/services/JGitRepositoryTest.scala
index 88b96b9232e..5dd366fc6d4 100644
--- a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/cfclerk/services/JGitRepositoryTest.scala
+++ b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/cfclerk/services/JGitRepositoryTest.scala
@@ -121,7 +121,7 @@ class JGitRepositoryTest extends Specification with Loggable with AfterAll {
}
val techniqueCompiler = new TechniqueCompiler {
override def compileTechnique(technique: EditorTechnique): IOResult[TechniqueCompilationOutput] = {
- TechniqueCompilationOutput(TechniqueCompilerApp.Rudderc, fallbacked = false, 0, Chunk.empty, "", "", "").succeed
+ TechniqueCompilationOutput(TechniqueCompilerApp.Rudderc, 0, Chunk.empty, "", "", "").succeed
}
override def getCompilationOutputFile(technique: EditorTechnique): File = File("compilation-config.yml")
diff --git a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/ncf/TestEditorTechniqueWriter.scala b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/ncf/TestEditorTechniqueWriter.scala
index 8b68f47a918..0e63b74e8e1 100644
--- a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/ncf/TestEditorTechniqueWriter.scala
+++ b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/ncf/TestEditorTechniqueWriter.scala
@@ -80,7 +80,6 @@ import com.normation.rudder.repository.CategoryWithActiveTechniques
import com.normation.rudder.repository.FullActiveTechniqueCategory
import com.normation.rudder.repository.RoDirectiveRepository
import com.normation.rudder.repository.WoDirectiveRepository
-import com.normation.rudder.repository.xml.RudderPrettyPrinter
import com.normation.rudder.repository.xml.TechniqueArchiver
import com.normation.rudder.services.nodes.PropertyEngineServiceImpl
import com.normation.rudder.services.policies.InterpolatedValueCompilerImpl
@@ -545,26 +544,16 @@ class TestEditorTechniqueWriter extends Specification with ContentMatchers with
override def updateMethodsMetadataFile: IOResult[CmdResult] = ???
}
- val webappCompiler = new WebappTechniqueCompiler(
- valueCompiler,
- new RudderPrettyPrinter(Int.MaxValue, 2),
- parameterTypeService,
- editorTechniqueReader,
- _.path,
- basePath
- )
- val compiler = new TechniqueCompilerWithFallback(
- webappCompiler,
+ val compiler = new RuddercTechniqueCompiler(
new RuddercService {
override def compile(techniqueDir: File, options: RuddercOptions): IOResult[RuddercResult] = {
RuddercResult.Fail(42, Chunk.empty, "error:see implementation of test", "", "").succeed
}
},
- TechniqueCompilerApp.Webapp,
_.path,
basePath
)
- val writer = new TechniqueWriterImpl(
+ val writer = new TechniqueWriterImpl(
TestTechniqueArchiver,
TestLibUpdater,
new DeleteEditorTechnique {
@@ -581,32 +570,10 @@ class TestEditorTechniqueWriter extends Specification with ContentMatchers with
compiler,
basePath
)
- val dscWriter = new DSCTechniqueWriter(
- basePath,
- valueCompiler,
- new ParameterType.PlugableParameterTypeService,
- _.path
- )
- val classicWriter = new ClassicTechniqueWriter(basePath, new ParameterType.PlugableParameterTypeService, _.path)
- val expectedMetadataPath: String = s"techniques/ncf_techniques/${technique.id.value}/${technique.version.value}/metadata.xml"
- val dscTechniquePath: String = s"techniques/ncf_techniques/${technique.id.value}/${technique.version.value}/technique.ps1"
- val techniquePath: String = s"techniques/ncf_techniques/${technique.id.value}/${technique.version.value}/technique.cf"
- val yamlPath: String = s"techniques/ncf_techniques/${technique.id.value}/${technique.version.value}/technique.yml"
- val reportingPath: String = s"techniques/ncf_techniques/${technique.id.value}/${technique.version.value}/rudder_reporting.cf"
+ val yamlPath: String = s"techniques/ncf_techniques/${technique.id.value}/${technique.version.value}/technique.yml"
s"Preparing files for technique ${technique.name}" should {
-
- "Should write metadata file without problem" in {
- webappCompiler.writeMetadata(technique, methods).either.runNow must beRight(expectedMetadataPath)
- }
-
- "Should generate expected metadata content for our technique" in {
- val expectedMetadataFile = new JFile(s"${expectedPath}/${expectedMetadataPath}")
- val resultMetadataFile = new JFile(s"${basePath}/${expectedMetadataPath}")
- resultMetadataFile must haveSameLinesAs(expectedMetadataFile)
- }
-
"Should write yaml file without problem" in {
writer.writeYaml(technique).either.runNow must beRight(yamlPath)
}
@@ -617,36 +584,6 @@ class TestEditorTechniqueWriter extends Specification with ContentMatchers with
resultMetadataFile must haveSameLinesAs(expectedMetadataFile)
}
- "Should write dsc technique file without problem" in {
- dscWriter.writeAgentFiles(technique, methods).either.runNow must beRight(Seq(dscTechniquePath))
- }
-
- "Should generate expected dsc technique content for our technique" in {
- val expectedDscFile = new JFile(s"${expectedPath}/${dscTechniquePath}")
- val resultDscFile = new JFile(s"${basePath}/${dscTechniquePath}")
- val mandatoryFalseRegex = """.*\Q[parameter(Mandatory=$false)]\E.*""".r
- val containsMandatoryFalse =
- better.files.File(resultDscFile.getAbsolutePath).lines.collectFirst(l => mandatoryFalseRegex.matches(l))
- (resultDscFile must haveSameLinesAs(expectedDscFile)) and
- (containsMandatoryFalse.nonEmpty must beTrue)
- }
-
- "Should write classic technique files without problem" in {
- classicWriter.writeAgentFiles(technique, methods).either.runNow must beRight(Seq(techniquePath, reportingPath))
- }
-
- "Should generate expected classic technique content for our technique" in {
- val expectedFile = new JFile(s"${expectedPath}/${techniquePath}")
- val resultFile = new JFile(s"${basePath}/${techniquePath}")
- resultFile must haveSameLinesAs(expectedFile)
- }
-
- "Should generate expected additional rudder reporting content for our technique" in {
- val expectedFile = new JFile(s"${expectedPath}/${reportingPath}")
- val resultFile = new JFile(s"${basePath}/${reportingPath}")
- resultFile must haveSameLinesAs(expectedFile)
- }
-
}
val technique_any: EditorTechnique = {
@@ -683,29 +620,11 @@ class TestEditorTechniqueWriter extends Specification with ContentMatchers with
)
}
- val expectedMetadataPath_any: String =
- s"techniques/ncf_techniques/${technique_any.id.value}/${technique_any.version.value}/metadata.xml"
- val dscTechniquePath_any: String =
- s"techniques/ncf_techniques/${technique_any.id.value}/${technique_any.version.value}/technique.ps1"
- val techniquePath_any: String =
- s"techniques/ncf_techniques/${technique_any.id.value}/${technique_any.version.value}/technique.cf"
- val techniquePath_yaml: String =
+ val techniquePath_yaml: String =
s"techniques/ncf_techniques/${technique_any.id.value}/${technique_any.version.value}/technique.yml"
- val reportingPath_any: String =
- s"techniques/ncf_techniques/${technique_any.id.value}/${technique_any.version.value}/rudder_reporting.cf"
s"Preparing files for technique ${technique.id.value}" should {
- "Should write metadata file without problem" in {
- webappCompiler.writeMetadata(technique_any, methods).either.runNow must beRight(expectedMetadataPath_any)
- }
-
- "Should generate expected metadata content for our technique" in {
- val expectedMetadataFile = new JFile(s"${expectedPath}/${expectedMetadataPath_any}")
- val resultMetadataFile = new JFile(s"${basePath}/${expectedMetadataPath_any}")
- resultMetadataFile must haveSameLinesAs(expectedMetadataFile)
- }
-
"Should write yaml file without problem" in {
writer.writeYaml(technique_any).either.runNow must beRight(techniquePath_yaml)
}
@@ -715,31 +634,6 @@ class TestEditorTechniqueWriter extends Specification with ContentMatchers with
val resultMetadataFile = new JFile(s"${basePath}/${techniquePath_yaml}")
resultMetadataFile must haveSameLinesAs(expectedMetadataFile)
}
-
- "Should write dsc technique file without problem" in {
- dscWriter.writeAgentFiles(technique_any, methods).either.runNow must beRight(Seq(dscTechniquePath_any))
- }
-
- "Should generate expected dsc technique content for our technique" in {
- val expectedDscFile = new JFile(s"${expectedPath}/${dscTechniquePath_any}")
- val resultDscFile = new JFile(s"${basePath}/${dscTechniquePath_any}")
- resultDscFile must haveSameLinesAs(expectedDscFile)
- }
-
- "Should write classic technique files without problem" in {
- classicWriter.writeAgentFiles(technique_any, methods).either.runNow must beRight(Seq(techniquePath_any))
- }
-
- "Should generate expected classic technique content for our technique" in {
- val expectedFile = new JFile(s"${expectedPath}/${techniquePath_any}")
- val resultFile = new JFile(s"${basePath}/${techniquePath_any}")
- resultFile must haveSameLinesAs(expectedFile)
- }
-
- "Should not generate expected additional rudder reporting content for our technique" in {
- val resultFile = new JFile(s"${basePath}/${reportingPath_any}")
- resultFile must not(exist)
- }
}
val technique_var_cond: EditorTechnique = {
@@ -775,136 +669,14 @@ class TestEditorTechniqueWriter extends Specification with ContentMatchers with
)
}
- val expectedMetadataPath_var_cond: String =
- s"${technique_var_cond.id.value}/${technique_var_cond.version.value}/metadata.xml"
- val dscTechniquePath_var_cond: String =
- s"${technique_var_cond.id.value}/${technique_var_cond.version.value}/technique.ps1"
- val techniquePath_var_cond: String =
- s"${technique_var_cond.id.value}/${technique_var_cond.version.value}/technique.cf"
- val techniquePath_var_cond_yaml: String =
+ val techniquePath_var_cond_yaml: String =
s"${technique_var_cond.id.value}/${technique_var_cond.version.value}/technique.yml"
- val reportingPath_var_cond: String =
- s"${technique_var_cond.id.value}/${technique_var_cond.version.value}/rudder_reporting.cf"
- val expectedPathVarCond = "src/test/resources/configuration-repository/expected-share"
- val basePathVarCond: String = s"${basePath}/techniques/ncf_techniques/"
s"Preparing files for technique ${technique.id.value}" should {
-
- "Should write metadata file without problem" in {
- webappCompiler.writeMetadata(technique_var_cond, methods).either.runNow must beRight(
- s"techniques/ncf_techniques/${expectedMetadataPath_var_cond}"
- )
- }
-
"Should write metadata file without problem" in {
writer.writeYaml(technique_var_cond).either.runNow must beRight(
s"techniques/ncf_techniques/${techniquePath_var_cond_yaml}"
)
}
- "Should generate expected metadata content for our technique" in {
- val expectedMetadataFile = new JFile(s"${expectedPathVarCond}/${expectedMetadataPath_var_cond}")
- val resultMetadataFile = new JFile(s"${basePathVarCond}/${expectedMetadataPath_var_cond}")
- resultMetadataFile must haveSameLinesAs(expectedMetadataFile)
- }
-
- "Should write dsc technique file without problem" in {
- dscWriter.writeAgentFiles(technique_var_cond, methods).either.runNow must beRight(
- Seq(s"techniques/ncf_techniques/${dscTechniquePath_var_cond}")
- )
- }
-
- "Should generate expected dsc technique content for our technique" in {
- val expectedDscFile = new JFile(s"${expectedPathVarCond}/${dscTechniquePath_var_cond}")
- val resultDscFile = new JFile(s"${basePathVarCond}/${dscTechniquePath_var_cond}")
- resultDscFile must haveSameLinesAs(expectedDscFile)
- }
-
- "Should write classic technique files without problem" in {
- classicWriter.writeAgentFiles(technique_var_cond, methods).either.runNow must beRight(
- Seq(
- s"techniques/ncf_techniques/${techniquePath_var_cond}",
- s"techniques/ncf_techniques/${reportingPath_var_cond}"
- )
- )
- }
-
- "Should generate expected yaml technique content for our technique" in {
- val expectedFile = new JFile(s"${expectedPathVarCond}/${techniquePath_var_cond_yaml}")
- val resultFile = new JFile(s"${basePathVarCond}/${techniquePath_var_cond_yaml}")
- resultFile must haveSameLinesAs(expectedFile)
- }
- "Should generate expected classic technique content for our technique" in {
- val expectedFile = new JFile(s"${expectedPathVarCond}/${techniquePath_var_cond}")
- val resultFile = new JFile(s"${basePathVarCond}/${techniquePath_var_cond}")
- resultFile must haveSameLinesAs(expectedFile)
- }
-
- }
-
- // same than previous one but with direct call to techniqueWriter.writeTechnique
- "Calling compile with no target should correctly fallback without error" >> {
- val tech = technique_any.copy(version = new Version("2.0"))
- val expectedMetadataPath_any = s"techniques/ncf_techniques/${tech.id.value}/${tech.version.value}/metadata.xml"
- val dscTechniquePath_any = s"techniques/ncf_techniques/${tech.id.value}/${tech.version.value}/technique.ps1"
- val techniquePath_any = s"techniques/ncf_techniques/${tech.id.value}/${tech.version.value}/technique.cf"
-
- "Should write everything without error" in {
- (writer.writeTechnique(tech, ModificationId("test"), EventActor("test")).either.runNow must beRight(tech))
- }
-
- "Should generate expected metadata content for our technique" in {
- val expectedMetadataFile = new JFile(s"${expectedPath}/${expectedMetadataPath_any}")
- val resultMetadataFile = new JFile(s"${basePath}/${expectedMetadataPath_any}")
- resultMetadataFile must haveSameLinesAs(expectedMetadataFile)
- }
-
- "Should generate expected classic technique content for our technique" in {
- val expectedFile = new JFile(s"${expectedPath}/${techniquePath_any}")
- val resultFile = new JFile(s"${basePath}/${techniquePath_any}")
- resultFile must haveSameLinesAs(expectedFile)
- }
-
- "Should generate expected dsc technique content for our technique" in {
- val expectedDscFile = new JFile(s"${expectedPath}/${dscTechniquePath_any}")
- val resultDscFile = new JFile(s"${basePath}/${dscTechniquePath_any}")
- resultDscFile must haveSameLinesAs(expectedDscFile)
- }
}
-
- "Constraints should" should {
- "Correctly accept non whitespace text" in {
- val value1 = "Some text"
- val value2 = {
- """Some
- |text""".stripMargin
- }
- val value3 = "S"
- val value4 = "ééé ```"
- val value5 = "sdfsqdfsqfsdf sfhdskjhdfs jkhsdkfjhksqdhf"
- val value6 = ""
-
- Constraint.AllowWhiteSpace(allow = false).check(value1) must equalTo(Constraint.OK)
- Constraint.AllowWhiteSpace(allow = false).check(value2) must equalTo(Constraint.OK)
- Constraint.AllowWhiteSpace(allow = false).check(value3) must equalTo(Constraint.OK)
- Constraint.AllowWhiteSpace(allow = false).check(value4) must equalTo(Constraint.OK)
- Constraint.AllowWhiteSpace(allow = false).check(value5) must equalTo(Constraint.OK)
- Constraint.AllowWhiteSpace(allow = false).check(value6) must equalTo(Constraint.OK)
- }
-
- "Correctly refuse text starting or ending with withspace" in {
- val value1 = " Some text"
- val value2 = {
- """ Some
- |text""".stripMargin
- }
- val value3 = " "
- val value4 = "sdfsqdfsqfsdf sfhdskjhdfs jkhsdkfjhksqdhf "
-
- Constraint.AllowWhiteSpace(allow = false).check(value1) must haveClass[Constraint.NOK]
- Constraint.AllowWhiteSpace(allow = false).check(value2) must haveClass[Constraint.NOK]
- Constraint.AllowWhiteSpace(allow = false).check(value3) must haveClass[Constraint.NOK]
- Constraint.AllowWhiteSpace(allow = false).check(value4) must haveClass[Constraint.NOK]
- }
- }
-
}
diff --git a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/ncf/TestEditorTechniqueWriterFallback.scala b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/ncf/TestEditorTechniqueWriterFallback.scala
deleted file mode 100644
index 72187d69558..00000000000
--- a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/ncf/TestEditorTechniqueWriterFallback.scala
+++ /dev/null
@@ -1,513 +0,0 @@
-/*
- *************************************************************************************
- * Copyright 2023 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 com.normation.rudder.ncf
-
-import better.files.File
-import com.normation.errors.*
-import com.normation.inventory.domain.AgentType
-import com.normation.inventory.domain.Version
-import com.normation.rudder.domain.policies.PolicyMode.Enforce
-import com.normation.rudder.hooks.CmdResult
-import com.normation.rudder.ncf.ParameterType.PlugableParameterTypeService
-import com.normation.rudder.ncf.TechniqueCompilerApp.*
-import com.normation.rudder.repository.xml.RudderPrettyPrinter
-import com.normation.rudder.services.nodes.PropertyEngineServiceImpl
-import com.normation.rudder.services.policies.InterpolatedValueCompilerImpl
-import com.normation.zio.*
-import com.softwaremill.quicklens.*
-import java.io.File as JFile
-import net.liftweb.common.Loggable
-import org.apache.commons.io.FileUtils
-import org.joda.time.DateTime
-import org.junit.runner.RunWith
-import org.specs2.matcher.ContentMatchers
-import org.specs2.matcher.MatchResult
-import org.specs2.mutable.Specification
-import org.specs2.runner.JUnitRunner
-import org.specs2.specification.BeforeAfterAll
-import scala.annotation.nowarn
-import zio.Chunk
-import zio.syntax.*
-
-/*
- * Test the fallback logic for the technique writer
- */
-@nowarn("msg=a type was inferred to be `Any`; this may indicate a programming error.")
-@RunWith(classOf[JUnitRunner])
-class TestEditorTechniqueWriterFallback extends Specification with ContentMatchers with Loggable with BeforeAfterAll {
- sequential
- lazy val basePath: String = "/tmp/test-rudder-technique-writer-fallback-" + DateTime.now.toString()
-
- override def beforeAll(): Unit = {
- new JFile(basePath).mkdirs()
- }
-
- override def afterAll(): Unit = {
- if (java.lang.System.getProperty("tests.clean.tmp") != "false") {
- FileUtils.deleteDirectory(new JFile(basePath))
- }
- }
-
- val expectedPath = "src/test/resources/configuration-repository"
-
- /// check the state of one of the file produce by technique compilation
- sealed trait OutputFileStatus
- object OutputFileStatus {
- case object Missing extends OutputFileStatus
- case object ExistsNonEmpty extends OutputFileStatus // when you just want to check that the file exists (non empty)
- case object CreatedByRudderc extends OutputFileStatus
- case object CreatedByWebapp extends OutputFileStatus
- }
- import OutputFileStatus.*
-
- object Content {
- val ruddercMetadata = "I'm not really an XML"
- val ruddercCFE = "the cfe content by rudderc"
- val ruddercPS1 = "the ps1 content by rudderc"
-
- val techniqueOK = "techniqueOK"
- val techniqueNOK_user = "techniqueNOK_user"
- val techniqueNOK_empty_pos = "techniqueNOK_empty_pos"
- val techniqueNOK_empty_neg = "techniqueNOK_empty_neg"
- val techniqueNOK_xml = "techniqueNOK_xml"
- val techniqueNOK_xml_cfe = "techniqueNOK_xml_cfe"
- val techniqueNOK_xml_cfe_ps1 = "techniqueNOK_xml_cfe_ps1"
- }
-
- /*
- * For test: it will fail on technique based on id value:
- * - techniqueOK => write all, return success
- * - techniqueNOK_user => write nothing, return an user error code (no fallback)
- * - techniqueNOK_empty => nothing written, fail
- * - techniqueNOK_xml => xml written, fail
- * - techniqueNOK_xml_cfe => xml, cfe written, fail
- * - techniqueNOK_xml_cfe_ps1 => xml, cfe, ps1 written, fail
- */
- object TestRudderc extends RuddercService {
-
- // technique path is supposed to be absolute and in the correct place in the temp directory.
- override def compile(techniqueDir: File, options: RuddercOptions): IOResult[RuddercResult] = {
- def writeXML = (techniqueDir / "metadata.xml").write(Content.ruddercMetadata)
- def writeCFE = (techniqueDir / "technique.cf").write(Content.ruddercCFE)
- def writePS1 = (techniqueDir / "technique.ps1").write(Content.ruddercPS1)
-
- techniqueDir.name match {
- case Content.techniqueOK =>
- IOResult.attempt {
- writeXML
- writeCFE
- writePS1
- RuddercResult.Ok(Chunk.empty, "ok", "stdout", "")
- }
- case Content.techniqueNOK_user =>
- IOResult.attempt {
- RuddercResult.UserError(7, Chunk.empty, "nok:user", "stdout", "stderr")
- }
- case Content.techniqueNOK_empty_neg =>
- IOResult.attempt {
- RuddercResult.Fail(-42, Chunk.empty, "nok:empty", "stdout", "stderr")
- }
- case Content.techniqueNOK_empty_pos =>
- IOResult.attempt {
- RuddercResult.Fail(50, Chunk.empty, "nok:empty", "stdout", "stderr")
- }
- case Content.techniqueNOK_xml =>
- IOResult.attempt {
- writeXML
- RuddercResult.Fail(60, Chunk.empty, "nok:xml", "stdout", "stderr")
- }
- case Content.techniqueNOK_xml_cfe =>
- IOResult.attempt {
- writeXML
- writeCFE
- RuddercResult.Fail(70, Chunk.empty, "nok:cfe", "stdout", "stderr")
- }
- case Content.techniqueNOK_xml_cfe_ps1 =>
- IOResult.attempt {
- writeXML
- writeCFE
- writePS1
- RuddercResult.Fail(80, Chunk.empty, "nok:ps1", "stdout", "stderr")
- }
- // other case must not exists in test, error.
- case other => Unexpected(s"Asking for technique with id '${other}' which is not a test case - go see TestRudderc").fail
- }
- }
- }
-
- val valueCompiler = new InterpolatedValueCompilerImpl(new PropertyEngineServiceImpl(List.empty))
- val parameterTypeService = new PlugableParameterTypeService
-
- import ParameterType.*
-
- val defaultConstraint: List[Constraint.Constraint] =
- Constraint.AllowEmpty(allow = false) :: Constraint.AllowWhiteSpace(allow = false) :: Constraint.MaxLength(16384) :: Nil
-
- val methods: Map[BundleName, GenericMethod] = (
- GenericMethod(
- BundleName("package_install"),
- "Package install",
- MethodParameter(ParameterId("package_name"), "", defaultConstraint, StringParameter) :: Nil,
- ParameterId("package_name"),
- "package_install",
- AgentType.CfeCommunity :: AgentType.CfeEnterprise :: Nil,
- "Package install",
- None,
- None,
- None,
- Seq()
- ) ::
- Nil
- ).map(m => (m.id, m)).toMap
-
- val editorTechniqueReader: EditorTechniqueReader = new EditorTechniqueReader {
- def readTechniquesMetadataFile: IOResult[(List[EditorTechnique], Map[BundleName, GenericMethod], List[RudderError])] = {
- ???
- }
-
- def getMethodsMetadata: IOResult[Map[BundleName, GenericMethod]] = methods.succeed
-
- override def updateMethodsMetadataFile: IOResult[CmdResult] = ???
- }
-
- def newCompilerWithBase(baseDir: String, defaultCompiler: TechniqueCompilerApp = Rudderc) = new TechniqueCompilerWithFallback(
- new WebappTechniqueCompiler(
- valueCompiler,
- new RudderPrettyPrinter(Int.MaxValue, 2),
- parameterTypeService,
- editorTechniqueReader,
- _.id.value, // remove useless /technique/categories/techId/1.0/ and keep only techId
- baseDir
- ),
- TestRudderc,
- defaultCompiler,
- _.id.value, // remove useless /technique/categories/techId/1.0/ and keep only techId
- baseDir
- )
-
- val techniqueTemplate: EditorTechnique = {
- EditorTechnique(
- BundleName("technique_by_Rudder"),
- new Version("1.0"),
- "Test Technique created with love",
- "ncf_techniques",
- MethodCall(
- BundleName("package_install"),
- "id4",
- Map((ParameterId("package_name"), "openssh-server")),
- "redhat",
- "Package install",
- disabledReporting = false,
- policyMode = Some(Enforce)
- ) :: Nil,
- "",
- "",
- Nil,
- Nil,
- Map(),
- None
- )
- }
-
- // File object for the configuration-config.yml for given compiler/technique
- def compilationConfigFile(implicit compiler: TechniqueCompilerWithFallback, technique: EditorTechnique): File = File(
- compiler.baseConfigRepoPath + "/" + technique.id.value + "/" + compiler.compilationConfigFilename
- )
-
- // File object for the configuration-output.yml for given compiler/technique
- def compilationOutputFile(implicit compiler: TechniqueCompilerWithFallback, technique: EditorTechnique): File = File(
- compiler.baseConfigRepoPath + "/" + technique.id.value + "/" + compiler.compilationOutputFilename
- )
-
- // check validity of file content written by rudderc
- def checkOutput(
- compiler: TechniqueCompilerWithFallback,
- filename: String,
- techniqueId: String,
- expected: String,
- status: OutputFileStatus
- ): MatchResult[Any] = {
- val f = File(compiler.baseConfigRepoPath + "/" + techniqueId + "/" + filename)
-
- status match {
- case Missing => f.exists must beFalse
- case ExistsNonEmpty => (f.exists must beTrue) and (f.nonEmpty must beTrue)
- case CreatedByRudderc => f.toJava must haveSameLinesAs(Seq(expected))
- case CreatedByWebapp => f.toJava must not(haveSameLinesAs(Seq(expected)))
- }
- }
-
- // check validity of xml file written by rudderc
- def checkXML(
- status: OutputFileStatus
- )(implicit compiler: TechniqueCompilerWithFallback, technique: EditorTechnique): MatchResult[Any] = {
- checkOutput(compiler, "metadata.xml", technique.id.value, Content.ruddercMetadata, status)
- }
-
- // check validity of .cf file written by rudderc
- def checkCFE(
- status: OutputFileStatus
- )(implicit compiler: TechniqueCompilerWithFallback, technique: EditorTechnique): MatchResult[Any] = {
- checkOutput(compiler, "technique.cf", technique.id.value, Content.ruddercCFE, status)
- }
-
- // check validity of .ps1 file written by rudderc
- def checkPS1(
- status: OutputFileStatus
- )(implicit compiler: TechniqueCompilerWithFallback, technique: EditorTechnique): MatchResult[Any] = {
- checkOutput(compiler, "technique.ps1", technique.id.value, Content.ruddercPS1, status)
- }
-
- // it is done elsewhere in rudder
- def initDirectories(compiler: TechniqueCompilerWithFallback, technique: EditorTechnique): File = {
- (compiler.gitDir / technique.id.value).createDirectories()
- }
-
- s"Compiler without local compilation override" should {
- val rootPath = basePath + "/no-override"
- implicit val compiler = newCompilerWithBase(rootPath)
-
- "Use the default compiler app and no fallback on success" in {
- implicit val technique = techniqueTemplate.modify(_.id.value).setTo(Content.techniqueOK)
- initDirectories(compiler, technique)
- val res = compiler.compileTechnique(technique).runNow
-
- (res must beEqualTo(
- TechniqueCompilationOutput(
- Rudderc,
- fallbacked = false,
- resultCode = 0,
- fileStatus = Chunk.empty,
- msg = "ok",
- stdout = "stdout",
- stderr = ""
- )
- )) and
- (compilationConfigFile.exists must beFalse) and
- (compilationOutputFile.exists must beFalse) and
- checkXML(CreatedByRudderc) and
- checkCFE(CreatedByRudderc) and
- checkPS1(CreatedByRudderc)
- }
-
- "Use the default compiler app and no fallback on user error" in {
- implicit val technique = techniqueTemplate.modify(_.id.value).setTo(Content.techniqueNOK_user)
- initDirectories(compiler, technique)
- val res = compiler.compileTechnique(technique).runNow
-
- (res must beEqualTo(
- TechniqueCompilationOutput(
- Rudderc,
- fallbacked = false,
- resultCode = 7,
- fileStatus = Chunk.empty,
- msg = "nok:user",
- stdout = "stdout",
- stderr = "stderr"
- )
- )) and
- (compilationConfigFile.exists must beFalse) and
- (compilationOutputFile.exists must beTrue) and
- checkXML(Missing) and
- checkCFE(Missing) and
- checkPS1(Missing)
- }
-
- "Fallback to webapp with compilation-output written when failing with code < 0" in {
- implicit val technique = techniqueTemplate.modify(_.id.value).setTo(Content.techniqueNOK_empty_neg)
- initDirectories(compiler, technique)
- val res = compiler.compileTechnique(technique).runNow
-
- (res must beEqualTo(
- TechniqueCompilationOutput(
- Webapp,
- fallbacked = true,
- resultCode = -42,
- fileStatus = Chunk.empty,
- msg = "nok:empty",
- stdout = "stdout",
- stderr = "stderr"
- )
- )) and
- (compilationConfigFile.exists must beFalse) and
- (compilationOutputFile.exists must beTrue) and
- checkXML(CreatedByWebapp) and
- checkCFE(CreatedByWebapp) and
- checkPS1(CreatedByWebapp)
- }
-
- "Fallback to webapp with compilation-output written when failing with code > limit code on xml" in {
- implicit val technique = techniqueTemplate.modify(_.id.value).setTo(Content.techniqueNOK_empty_pos)
- initDirectories(compiler, technique)
- val res = compiler.compileTechnique(technique).runNow
-
- (res must beEqualTo(
- TechniqueCompilationOutput(
- Webapp,
- fallbacked = true,
- resultCode = 50,
- fileStatus = Chunk.empty,
- msg = "nok:empty",
- stdout = "stdout",
- stderr = "stderr"
- )
- )) and
- (compilationConfigFile.exists must beFalse) and
- (compilationOutputFile.exists must beTrue) and
- checkXML(CreatedByWebapp) and
- checkCFE(CreatedByWebapp) and
- checkPS1(CreatedByWebapp)
- }
-
- "Fallback to webapp with compilation-output written when failing on cfe and rewrite XML written by rudderc" in {
- implicit val technique = techniqueTemplate.modify(_.id.value).setTo(Content.techniqueNOK_xml)
- initDirectories(compiler, technique)
- val res = compiler.compileTechnique(technique).runNow
-
- (res must beEqualTo(
- TechniqueCompilationOutput(
- Webapp,
- fallbacked = true,
- resultCode = 60,
- fileStatus = Chunk.empty,
- msg = "nok:xml",
- stdout = "stdout",
- stderr = "stderr"
- )
- )) and
- (compilationConfigFile.exists must beFalse) and
- (compilationOutputFile.exists must beTrue) and
- checkXML(CreatedByWebapp) and
- checkCFE(CreatedByWebapp) and
- checkPS1(CreatedByWebapp)
- }
-
- "Fallback to webapp with compilation-output written when failing on ps1 and rewrite XML,cf written by rudderc" in {
- implicit val technique = techniqueTemplate.modify(_.id.value).setTo(Content.techniqueNOK_xml_cfe)
- initDirectories(compiler, technique)
- val res = compiler.compileTechnique(technique).runNow
-
- (res must beEqualTo(
- TechniqueCompilationOutput(
- Webapp,
- fallbacked = true,
- resultCode = 70,
- fileStatus = Chunk.empty,
- msg = "nok:cfe",
- stdout = "stdout",
- stderr = "stderr"
- )
- )) and
- (compilationConfigFile.exists must beFalse) and
- (compilationOutputFile.exists must beTrue) and
- checkXML(CreatedByWebapp) and
- checkCFE(CreatedByWebapp) and
- checkPS1(CreatedByWebapp)
- }
-
- "Fallback to webapp with compilation-output written when failing on the end and rewrite XML,cf,ps1 written by rudderc" in {
- implicit val technique = techniqueTemplate.modify(_.id.value).setTo(Content.techniqueNOK_xml_cfe_ps1)
- initDirectories(compiler, technique)
- val res = compiler.compileTechnique(technique).runNow
-
- (res must beEqualTo(
- TechniqueCompilationOutput(
- Webapp,
- fallbacked = true,
- resultCode = 80,
- fileStatus = Chunk.empty,
- msg = "nok:ps1",
- stdout = "stdout",
- stderr = "stderr"
- )
- )) and
- (compilationConfigFile.exists must beFalse) and
- (compilationOutputFile.exists must beTrue) and
- checkXML(CreatedByWebapp) and
- checkCFE(CreatedByWebapp) and
- checkPS1(CreatedByWebapp)
- }
- }
-
- s"Compiler WITH local compilation override" should {
- import zio.json.yaml.*
- import TechniqueCompilationIO.*
-
- val rootPath = basePath + "/override"
- implicit val compiler = newCompilerWithBase(rootPath)
- val overrideConfig = TechniqueCompilationConfig(Some(Webapp))
- def writeConfig(implicit technique: EditorTechnique) = {
- compiler
- .getCompilationConfigFile(technique)
- .write(overrideConfig.toYaml().getOrElse(throw new IllegalArgumentException(s"Error in test")))
- }
-
- "Use the default compiler app and no fallback on success" in {
- implicit val technique = techniqueTemplate.modify(_.id.value).setTo(Content.techniqueOK)
- initDirectories(compiler, technique)
- writeConfig
- val res = compiler.compileTechnique(technique).runNow
-
- import ResourceFileState.New
-
- (res must beEqualTo(
- TechniqueCompilationOutput(
- Webapp,
- fallbacked = false,
- resultCode = 0,
- fileStatus = Chunk(
- ResourceFile("metadata.xml", New),
- ResourceFile("rudder_reporting.cf", New),
- ResourceFile("technique.cf", New),
- ResourceFile("technique.ps1", New)
- ),
- msg = "Technique 'techniqueOK' written by webapp",
- stdout = "",
- stderr = ""
- )
- )) and
- (compilationConfigFile.exists must beTrue) and
- (compilationOutputFile.exists must beFalse) and // it's not an override, there's no error: no output file
- checkXML(CreatedByWebapp) and
- checkCFE(CreatedByWebapp) and
- checkPS1(CreatedByWebapp)
- }
-
- }
-
-}
diff --git a/webapp/sources/rudder/rudder-web/src/main/elm/sources/Editor/DataTypes.elm b/webapp/sources/rudder/rudder-web/src/main/elm/sources/Editor/DataTypes.elm
index fd7f7f7988a..237970b8734 100755
--- a/webapp/sources/rudder/rudder-web/src/main/elm/sources/Editor/DataTypes.elm
+++ b/webapp/sources/rudder/rudder-web/src/main/elm/sources/Editor/DataTypes.elm
@@ -67,7 +67,6 @@ type alias Method =
type alias CompilationOutput =
{ compiler: String
- , fallbacked: Bool
, resultCode: Int
, msg: String
, stdout: String
diff --git a/webapp/sources/rudder/rudder-web/src/main/elm/sources/Editor/JsonDecoder.elm b/webapp/sources/rudder/rudder-web/src/main/elm/sources/Editor/JsonDecoder.elm
index b39bc4295df..2730c01abdf 100755
--- a/webapp/sources/rudder/rudder-web/src/main/elm/sources/Editor/JsonDecoder.elm
+++ b/webapp/sources/rudder/rudder-web/src/main/elm/sources/Editor/JsonDecoder.elm
@@ -127,7 +127,6 @@ decodeOutput : Decoder CompilationOutput
decodeOutput =
succeed CompilationOutput
|> required "compiler" string
- |> required "fallbacked" bool
|> required "resultCode" int
|> required "msg" string
|> required "stdout" string
@@ -244,4 +243,4 @@ decodeErrorDetails json =
in
case title of
Nothing -> ("" , "")
- Just s -> (s , (join " \n " (drop 1 (List.map (\err -> "\t ‣ " ++ err) errors))))
\ No newline at end of file
+ Just s -> (s , (join " \n " (drop 1 (List.map (\err -> "\t ‣ " ++ err) errors))))
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 5c2d31bb5b0..91c0c7048e6 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
@@ -1795,17 +1795,8 @@ object RudderConfigInit {
techniqueCompiler,
RUDDER_GROUP_OWNER_CONFIG_REPO
)
- lazy val techniqueCompiler: TechniqueCompiler = new TechniqueCompilerWithFallback(
- new WebappTechniqueCompiler(
- interpolationCompiler,
- prettyPrinter,
- typeParameterService,
- ncfTechniqueReader,
- _.path,
- RUDDER_GIT_ROOT_CONFIG_REPO
- ),
+ lazy val techniqueCompiler: TechniqueCompiler = new RuddercTechniqueCompiler(
new RuddercServiceImpl(RUDDERC_CMD, 5.seconds),
- TECHNIQUE_COMPILER_APP,
_.path,
RUDDER_GIT_ROOT_CONFIG_REPO
)
diff --git a/webapp/sources/rudder/rudder-web/src/test/resources/configuration-repository-migrate-json-8_0/post-migration/ncf_techniques/technique_with_error/1.0/compilation-config.yml b/webapp/sources/rudder/rudder-web/src/test/resources/configuration-repository-migrate-json-8_0/post-migration/ncf_techniques/technique_with_error/1.0/compilation-config.yml
index 570d7cbc3a2..e69de29bb2d 100644
--- a/webapp/sources/rudder/rudder-web/src/test/resources/configuration-repository-migrate-json-8_0/post-migration/ncf_techniques/technique_with_error/1.0/compilation-config.yml
+++ b/webapp/sources/rudder/rudder-web/src/test/resources/configuration-repository-migrate-json-8_0/post-migration/ncf_techniques/technique_with_error/1.0/compilation-config.yml
@@ -1 +0,0 @@
-compiler: rudderc
diff --git a/webapp/sources/rudder/rudder-web/src/test/resources/configuration-repository-migrate-json-8_0/post-migration/ncf_techniques/technique_with_error/1.0/compilation-output.yml b/webapp/sources/rudder/rudder-web/src/test/resources/configuration-repository-migrate-json-8_0/post-migration/ncf_techniques/technique_with_error/1.0/compilation-output.yml
index 3cf295b34b1..3665a2607ce 100644
--- a/webapp/sources/rudder/rudder-web/src/test/resources/configuration-repository-migrate-json-8_0/post-migration/ncf_techniques/technique_with_error/1.0/compilation-output.yml
+++ b/webapp/sources/rudder/rudder-web/src/test/resources/configuration-repository-migrate-json-8_0/post-migration/ncf_techniques/technique_with_error/1.0/compilation-output.yml
@@ -1,5 +1,4 @@
compiler: rudderc
-fallbacked: false
resultCode: 42
fileStatus:
- path: foo
diff --git a/webapp/sources/rudder/rudder-web/src/test/resources/configuration-repository-migrate-json-8_0/techniques/ncf_techniques/technique_with_error/1.0/compilation-config.yml b/webapp/sources/rudder/rudder-web/src/test/resources/configuration-repository-migrate-json-8_0/techniques/ncf_techniques/technique_with_error/1.0/compilation-config.yml
index 570d7cbc3a2..e69de29bb2d 100644
--- a/webapp/sources/rudder/rudder-web/src/test/resources/configuration-repository-migrate-json-8_0/techniques/ncf_techniques/technique_with_error/1.0/compilation-config.yml
+++ b/webapp/sources/rudder/rudder-web/src/test/resources/configuration-repository-migrate-json-8_0/techniques/ncf_techniques/technique_with_error/1.0/compilation-config.yml
@@ -1 +0,0 @@
-compiler: rudderc
diff --git a/webapp/sources/rudder/rudder-web/src/test/scala/bootstrap/liftweb/checks/migration/TestMigrateJsonTechniquesToYaml.scala b/webapp/sources/rudder/rudder-web/src/test/scala/bootstrap/liftweb/checks/migration/TestMigrateJsonTechniquesToYaml.scala
index 8694e1b297a..2af5bf2bfa7 100644
--- a/webapp/sources/rudder/rudder-web/src/test/scala/bootstrap/liftweb/checks/migration/TestMigrateJsonTechniquesToYaml.scala
+++ b/webapp/sources/rudder/rudder-web/src/test/scala/bootstrap/liftweb/checks/migration/TestMigrateJsonTechniquesToYaml.scala
@@ -52,10 +52,9 @@ import com.normation.rudder.ncf.ResourceFileState
import com.normation.rudder.ncf.RuddercOptions
import com.normation.rudder.ncf.RuddercResult
import com.normation.rudder.ncf.RuddercService
+import com.normation.rudder.ncf.RuddercTechniqueCompiler
import com.normation.rudder.ncf.TechniqueCompilationOutput
import com.normation.rudder.ncf.TechniqueCompiler
-import com.normation.rudder.ncf.TechniqueCompilerApp
-import com.normation.rudder.ncf.TechniqueCompilerWithFallback
import com.normation.rudder.ncf.TechniqueWriterImpl
import com.normation.rudder.repository.GitModificationRepository
import com.normation.rudder.repository.xml.RudderPrettyPrinter
@@ -140,10 +139,8 @@ class TestMigrateJsonTechniquesToYaml extends Specification with ContentMatchers
override def getCompilationConfigFile(technique: EditorTechnique): File = null
}
- val techniqueCompiler = new TechniqueCompilerWithFallback(
- webappCompiler,
+ val techniqueCompiler = new RuddercTechniqueCompiler(
rudderc,
- TechniqueCompilerApp.Rudderc,
_.path,
gitMock.configurationRepositoryRoot.pathAsString
)