Skip to content

Commit

Permalink
Merge branch 'branches/rudder/8.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
Jenkins CI committed Mar 26, 2024
2 parents 185929d + 6d76a4d commit 33adb5b
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 185 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ package bootstrap.rudder.plugin
import bootstrap.liftweb.RudderConfig
import com.normation.plugins.RudderPluginModule
import com.normation.plugins.scaleoutrelay.CheckRudderPluginEnableImpl
import com.normation.plugins.scaleoutrelay.LdapDeleteNodeEntryService
import com.normation.plugins.scaleoutrelay.ScalaOutRelayPluginDef
import com.normation.plugins.scaleoutrelay.ScaleOutRelayAgentSpecificGeneration
import com.normation.plugins.scaleoutrelay.ScaleOutRelayService
Expand All @@ -55,18 +56,16 @@ object ScalaOutRelayConf extends RudderPluginModule {

override lazy val pluginDef: ScalaOutRelayPluginDef = new ScalaOutRelayPluginDef(ScalaOutRelayConf.pluginStatusService)

lazy val api = new ScaleOutRelayApiImpl(scaleOutRelayService)
lazy val api = new ScaleOutRelayApiImpl(scaleOutRelayService, RudderConfig.stringUuidGenerator)

lazy val scaleOutRelayService = new ScaleOutRelayService(
RudderConfig.nodeInfoService,
RudderConfig.woNodeGroupRepository,
RudderConfig.woNodeRepository,
RudderConfig.nodeFactRepository,
RudderConfig.woDirectiveRepository,
RudderConfig.woRuleRepository,
RudderConfig.stringUuidGenerator,
RudderConfig.policyServerManagementService,
RudderConfig.eventLogRepository,
RudderConfig.asyncDeploymentAgent
new LdapDeleteNodeEntryService(RudderConfig.rwLdap, RudderConfig.nodeDit)
)

// add policy generation for relay
Expand Down
Original file line number Diff line number Diff line change
@@ -1,174 +1,179 @@
package com.normation.plugins.scaleoutrelay

import com.normation.errors.*
import com.normation.eventlog.EventActor
import com.normation.eventlog.ModificationId
import com.normation.inventory.domain.NodeId
import com.normation.rudder.batch.AsyncDeploymentActor
import com.normation.rudder.batch.AutomaticStartDeployment
import com.normation.ldap.sdk.LDAPConnectionProvider
import com.normation.ldap.sdk.RwLDAPConnection
import com.normation.rudder.domain.NodeDit
import com.normation.rudder.domain.nodes.NodeGroupCategoryId
import com.normation.rudder.domain.nodes.NodeInfo
import com.normation.rudder.domain.nodes.NodeKind
import com.normation.rudder.domain.policies.*
import com.normation.rudder.facts.nodes.ChangeContext
import com.normation.rudder.facts.nodes.CoreNodeFact
import com.normation.rudder.facts.nodes.NodeFactRepository
import com.normation.rudder.facts.nodes.QueryContext
import com.normation.rudder.repository.EventLogRepository
import com.normation.rudder.repository.WoDirectiveRepository
import com.normation.rudder.repository.WoNodeGroupRepository
import com.normation.rudder.repository.WoNodeRepository
import com.normation.rudder.repository.WoRuleRepository
import com.normation.rudder.services.nodes.NodeInfoService
import com.normation.rudder.services.servers.PolicyServer
import com.normation.rudder.services.servers.PolicyServerConfigurationObjects
import com.normation.rudder.services.servers.PolicyServerManagementService
import com.normation.utils.StringUuidGenerator
import com.softwaremill.quicklens.*
import zio.*
import zio.syntax.*

/*
* We need a low level tool to delete the LDAP entry under ou=Nodes,cn=rudder-configuration because we need
* to change one of its structural type in a way that is not liked by ldap and causes:
* BackendException: Error when doing action 'modify' with and LDIF change request: structural object class
* modification from 'rudderNode' to 'rudderPolicyServer' not allowed
*/
trait DeleteNodeEntryService {
def delete(nodeId: NodeId): IOResult[Unit]
}

class LdapDeleteNodeEntryService(
ldap: LDAPConnectionProvider[RwLDAPConnection],
dit: NodeDit
) extends DeleteNodeEntryService {
override def delete(nodeId: NodeId): IOResult[Unit] = {
val dn = dit.NODES.NODE.dn(nodeId.value)
for {
con <- ldap
_ <- con.delete(dn).chainError(s"Error when trying to delete node '${nodeId.value}'")
} yield ()
}
}

class ScaleOutRelayService(
nodeInfosService: NodeInfoService,
woLDAPNodeGroupRepository: WoNodeGroupRepository,
woLDAPNodeRepository: WoNodeRepository,
nodeFactRepository: NodeFactRepository,
woDirectiveRepository: WoDirectiveRepository,
woRuleRepository: WoRuleRepository,
uuidGen: StringUuidGenerator,
policyServerManagementService: PolicyServerManagementService,
actionLogger: EventLogRepository,
asyncDeploymentAgent: AsyncDeploymentActor
deleteNodeService: DeleteNodeEntryService
) {
val SYSTEM_GROUPS = "SystemGroups"

def promoteNodeToRelay(nodeId: NodeId, actor: EventActor, reason: Option[String]): IOResult[NodeInfo] = {
val modId = ModificationId(uuidGen.newUuid)
def promoteNodeToRelay(nodeId: NodeId)(implicit cc: ChangeContext): IOResult[Unit] = {
implicit val qr: QueryContext = cc.toQuery
for {
_ <- ScaleOutRelayLoggerPure.debug(s"Start promotion of node '${nodeId.value}' to relay")
nodeInfo <- nodeInfosService
.getNodeInfo(nodeId)
.notOptional(s"Node with UUID ${nodeId.value} is missing and can not be upgraded to relay")
targetedNode <- if (!nodeInfo.isPolicyServer) {
val updatedNode = NodeToPolicyServer(nodeInfo)
for {
_ <- woLDAPNodeRepository
.deleteNode(nodeInfo.node, modId, actor, reason)
.chainError(s"Remove ${nodeInfo.node.id.value} to update it to policy server failed")
newRelay <- createRelayFromNode(updatedNode, modId, actor, reason)
} yield {
newRelay
}
} else {
ScaleOutRelayLoggerPure.debug(s"Node '${nodeId.value}' is already a policy server, nothing to do.") *>
nodeInfo.succeed
}
} yield {
asyncDeploymentAgent ! AutomaticStartDeployment(modId, actor)
targetedNode
}
_ <- ScaleOutRelayLoggerPure.debug(s"Start promotion of node '${nodeId.value}' to relay")
nodeInfo <- nodeFactRepository
.get(nodeId)
.notOptional(s"Node with UUID ${nodeId.value} is missing and can not be upgraded to relay")
_ <- ZIO.when(!nodeInfo.rudderSettings.isPolicyServer) {
val updatedNode: CoreNodeFact = NodeToPolicyServer(nodeInfo)
createRelayFromNode(updatedNode)
}
} yield () // policy generation will be started if needed by NodeFact event handler
}

private[scaleoutrelay] def createRelayFromNode(
nodeInfo: NodeInfo,
modId: ModificationId,
actor: EventActor,
reason: Option[String]
) = {
nodeInfo: CoreNodeFact
)(implicit cc: ChangeContext): IOResult[Unit] = {

val objects = PolicyServerConfigurationObjects.getConfigurationObject(nodeInfo.id)

val promote = for {
_ <- ScaleOutRelayLoggerPure.trace(s"[promote ${nodeInfo.id.value}] delete node entry")
_ <- deleteNodeService.delete(nodeInfo.id)
_ <- ScaleOutRelayLoggerPure.trace(s"[promote ${nodeInfo.id.value}] create entry with relay status")
_ <- woLDAPNodeRepository.createNode(nodeInfo.node, modId, actor, reason)
// we are force to delete the node, then create it again, because we are allowed to change it's structural
// class hierarchy in LDAP, which leads to error: BackendException: Error when doing action 'modify' with and LDIF change request:
// structural object class modification from 'rudderNode' to 'rudderPolicyServer' not allowed

_ <- nodeFactRepository.save(nodeInfo)
_ <- ScaleOutRelayLoggerPure.trace(s"[promote ${nodeInfo.id.value}] create new special targets")
_ <- ZIO.foreach(objects.targets)(t => woLDAPNodeGroupRepository.createPolicyServerTarget(t, modId, actor, reason))
_ <-
ZIO.foreach(objects.targets)(t => woLDAPNodeGroupRepository.createPolicyServerTarget(t, cc.modId, cc.actor, cc.message))
_ <- ScaleOutRelayLoggerPure.trace(s"[promote ${nodeInfo.id.value}] create new system groups")
_ <- ZIO.foreach(objects.groups) { g =>
woLDAPNodeGroupRepository.create(g, NodeGroupCategoryId(SYSTEM_GROUPS), modId, actor, reason)
woLDAPNodeGroupRepository.create(g, NodeGroupCategoryId(SYSTEM_GROUPS), cc.modId, cc.actor, cc.message)
}
_ <- ScaleOutRelayLoggerPure.trace(s"[promote ${nodeInfo.id.value}] create new system directives")
_ <- ZIO.foreach(objects.directives.toList) {
case (t, d) =>
woDirectiveRepository.saveSystemDirective(ActiveTechniqueId(t.value), d, modId, actor, reason)
woDirectiveRepository.saveSystemDirective(ActiveTechniqueId(t.value), d, cc.modId, cc.actor, cc.message)
}
_ <- ScaleOutRelayLoggerPure.trace(s"[promote ${nodeInfo.id.value}] create new system rules")
_ <- ZIO.foreach(objects.rules)(r => woRuleRepository.create(r, modId, actor, reason))
_ <- ZIO.foreach(objects.rules)(r => woRuleRepository.create(r, cc.modId, cc.actor, cc.message))

// save the relay in the rudder_policy_servers entry in LDAP
existingPolicyServers <- policyServerManagementService.getPolicyServers()
newPolicyServers = existingPolicyServers.copy(relays = PolicyServer(nodeInfo.id, Nil) :: existingPolicyServers.relays)
_ <- policyServerManagementService.savePolicyServers(newPolicyServers)

_ <- actionLogger.savePromoteToRelay(modId, actor, nodeInfo, reason)
} yield {
nodeInfo
}
_ <- actionLogger.savePromoteToRelay(cc.modId, cc.actor, nodeInfo.toNodeInfo, cc.message)
} yield {}

promote.catchAll { err =>
val msg = s"Promote node ${nodeInfo.node.id.value} have failed. Change were reverted. Cause was: ${err.fullMsg}"
val msg = s"Promote node ${nodeInfo.id.value} have failed. Change were reverted. Cause was: ${err.fullMsg}"
(for {
_ <- ScaleOutRelayLoggerPure.debug(s"[promote ${nodeInfo.id.value}] error, start reverting")
_ <- demoteRelay(nodeInfo, objects, modId, actor)
_ <- demoteRelay(nodeInfo, objects).catchAll(err =>
ScaleOutRelayLoggerPure.debug(s"Error when reverting node promotion: ${err.fullMsg}")
)
_ <- ScaleOutRelayLoggerPure.error(msg)
} yield {
nodeInfo
}) *> Unexpected(msg).fail
} yield {}) *> Unexpected(msg).fail
}
}

def demoteRelayToNode(nodeId: NodeId, actor: EventActor, reason: Option[String]): IOResult[Unit] = {
val modId = ModificationId(uuidGen.newUuid)
def demoteRelayToNode(nodeId: NodeId)(implicit cc: ChangeContext): IOResult[Unit] = {
implicit val qr: QueryContext = cc.toQuery
for {
_ <- ScaleOutRelayLoggerPure.debug(s"Start demotion of relay '${nodeId.value}' to node")
nodeInfo <- nodeInfosService
.getNodeInfo(nodeId)
.notOptional(s"Relay with UUID ${nodeId.value} is missing and can not be demoted to node")
targetedNode <- if (nodeInfo.isPolicyServer) {
val configObjects = PolicyServerConfigurationObjects.getConfigurationObject(nodeInfo.id)
demoteRelay(nodeInfo, configObjects, modId, actor).unit *>
actionLogger.saveDemoteToNode(modId, actor, nodeInfo, reason)
} else {
ScaleOutRelayLoggerPure.debug(s"Node '${nodeId.value}' is already a simple node, nothing to do.") *>
ZIO.unit
}
} yield {
asyncDeploymentAgent ! AutomaticStartDeployment(modId, actor)
}
_ <- ScaleOutRelayLoggerPure.debug(s"Start demotion of relay '${nodeId.value}' to node")
nodeInfo <- nodeFactRepository
.get(nodeId)
.notOptional(s"Relay with UUID ${nodeId.value} is missing and can not be demoted to node")
_ <- if (nodeInfo.rudderSettings.isPolicyServer) {
val configObjects = PolicyServerConfigurationObjects.getConfigurationObject(nodeInfo.id)
demoteRelay(nodeInfo, configObjects).unit *>
actionLogger.saveDemoteToNode(cc.modId, cc.actor, nodeInfo.toNodeInfo, cc.message)
} else {
ScaleOutRelayLoggerPure.debug(s"Node '${nodeId.value}' is already a simple node, nothing to do.") *>
ZIO.unit
}
} yield () // policy generation will be started if needed by NodeFact event handler

}

private[scaleoutrelay] def demoteRelay(
newInfo: NodeInfo,
objects: PolicyServerConfigurationObjects,
modId: ModificationId,
actor: EventActor
): ZIO[Any, Accumulated[RudderError], List[Unit]] = {

val old = PolicyServerToNode(newInfo)
val reason = Some("Demote relay")

val nPromoted = woLDAPNodeRepository.deleteNode(newInfo.node, modId, actor, reason).unit
val nBeforePromoted = woLDAPNodeRepository
.createNode(old.node, modId, actor, reason)
.chainError(s"Demote relay failed: restore '${newInfo.node}' configuration failed")
newInfo: CoreNodeFact,
objects: PolicyServerConfigurationObjects
)(implicit cc: ChangeContext): ZIO[Any, Accumulated[RudderError], List[Unit]] = {

val old = PolicyServerToNode(newInfo)

val nPromoted = deleteNodeService.delete(newInfo.id)
val nBeforePromoted = nodeFactRepository
.save(old)
.chainError(s"Demote relay failed: Setting node '${newInfo.id.value}' from relay to node failed")
.unit

val targets = objects.targets.map(t => {
woLDAPNodeGroupRepository
.deletePolicyServerTarget(t)
.chainError(s"Demote relay failed: removing '${newInfo.node}' configuration failed")
.chainError(s"Demote relay failed: removing policy server target for node '${newInfo.id.value}' failed")
.unit
})
val groups = objects.groups.map(g => {
woLDAPNodeGroupRepository
.delete(g.id, modId, actor, reason)
.delete(g.id, cc.modId, cc.actor, cc.message)
.chainError(s"Demote relay failed: removing node group '${g.id.serialize}' failed")
.unit
})
val directives = objects.directives.toList.map(d => {
woDirectiveRepository
.deleteSystemDirective(d._2.id.uid, modId, actor, reason)
.deleteSystemDirective(d._2.id.uid, cc.modId, cc.actor, cc.message)
.chainError(s"Demote relay failed: removing directive '${d._2.id.debugString}' failed")
.unit
})
val rules = objects.rules.map(r => {
woRuleRepository
.deleteSystemRule(r.id, modId, actor, reason)
.deleteSystemRule(r.id, cc.modId, cc.actor, cc.message)
.catchAll { err =>
ScaleOutRelayLoggerPure.info(s"Trying to remove residual object rule ${r.id.serialize}: ${err.fullMsg}")
}
Expand All @@ -178,24 +183,16 @@ class ScaleOutRelayService(
(nPromoted :: nBeforePromoted :: targets ::: groups ::: directives ::: rules).accumulate(identity)
}

private[scaleoutrelay] def NodeToPolicyServer(nodeInf: NodeInfo) = {
private[scaleoutrelay] def NodeToPolicyServer(nodeInf: CoreNodeFact) = {
nodeInf
.modify(_.node.isSystem)
.setTo(true)
.modify(_.node.isPolicyServer)
.setTo(true)
.modify(_.rudderSettings)
.setTo(nodeInf.rudderSettings.modify(_.kind).setTo(NodeKind.Relay))
}

private[scaleoutrelay] def PolicyServerToNode(nodeInf: NodeInfo) = {
private[scaleoutrelay] def PolicyServerToNode(nodeInf: CoreNodeFact) = {
nodeInf
.modify(_.node.isSystem)
.setTo(false)
.modify(_.node.isPolicyServer)
.setTo(false)
}

private[scaleoutrelay] def createPolicyServer(uuid: NodeId) = {
PolicyServerTarget(uuid)
.modify(_.rudderSettings)
.setTo(nodeInf.rudderSettings.modify(_.kind).setTo(NodeKind.Node))
}

}
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
package com.normation.plugins.scaleoutrelay.api

import com.normation.eventlog.EventActor
import com.normation.eventlog.ModificationId
import com.normation.inventory.domain.NodeId
import com.normation.plugins.scaleoutrelay.ScaleOutRelayService
import com.normation.rudder.api.ApiVersion
import com.normation.rudder.api.HttpAction.POST
import com.normation.rudder.facts.nodes.ChangeContext
import com.normation.rudder.rest.*
import com.normation.rudder.rest.EndpointSchema.syntax.*
import com.normation.rudder.rest.implicits.*
import com.normation.rudder.rest.lift.DefaultParams
import com.normation.rudder.rest.lift.LiftApiModule
import com.normation.rudder.rest.lift.LiftApiModuleProvider
import com.normation.utils.StringUuidGenerator
import enumeratum.*
import net.liftweb.http.LiftResponse
import net.liftweb.http.Req
import org.joda.time.DateTime
import sourcecode.Line

sealed trait ScaleOutRelayApi extends EnumEntry with EndpointSchema with GeneralApi with SortIndex {
Expand All @@ -39,7 +42,8 @@ object ScaleOutRelayApi extends Enum[ScaleOutRelayApi] with ApiModuleProvider[Sc
}

class ScaleOutRelayApiImpl(
scaleOutRelayService: ScaleOutRelayService
scaleOutRelayService: ScaleOutRelayService,
uuidGen: StringUuidGenerator
) extends LiftApiModuleProvider[ScaleOutRelayApi] {

override def schemas: ApiModuleProvider[ScaleOutRelayApi] = ScaleOutRelayApi
Expand All @@ -62,8 +66,16 @@ class ScaleOutRelayApiImpl(
params: DefaultParams,
authz: AuthzToken
): LiftResponse = {
implicit val cc = ChangeContext(
ModificationId(uuidGen.newUuid),
authz.qc.actor,
new DateTime(),
params.reason.orElse(Some(s"Promote node ${nodeId} to relay")),
Some(req.remoteAddr),
authz.qc.nodePerms
)
scaleOutRelayService
.promoteNodeToRelay(NodeId(nodeId), EventActor("rudder"), Some(s"Promote node ${nodeId} to relay"))
.promoteNodeToRelay(NodeId(nodeId))
.chainError(s"Error when trying to promote mode $nodeId")
.as(nodeId)
.toLiftResponseOne(params, schema, None)
Expand All @@ -80,8 +92,16 @@ class ScaleOutRelayApiImpl(
params: DefaultParams,
authz: AuthzToken
): LiftResponse = {
implicit val cc = ChangeContext(
ModificationId(uuidGen.newUuid),
authz.qc.actor,
new DateTime(),
params.reason.orElse(Some(s"Demote relay ${nodeId}")),
Some(req.remoteAddr),
authz.qc.nodePerms
)
scaleOutRelayService
.demoteRelayToNode(NodeId(nodeId), EventActor("rudder"), Some(s"Demote relay ${nodeId} to node"))
.demoteRelayToNode(NodeId(nodeId))
.chainError(s"Error when trying to demote mode $nodeId")
.as(nodeId)
.toLiftResponseOne(params, schema, None)
Expand Down
Loading

0 comments on commit 33adb5b

Please sign in to comment.