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 -
- {childs} -
- } - } - } - - 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 - } -
- - {class_param} - -
- - } - } - - 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 )