Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #23927: Migrate away from NodeInfoService #5269

Conversation

fanf
Copy link
Member

@fanf fanf commented Dec 17, 2023

https://issues.rudder.io/issues/23927

A not very exiting PR: mainly:

  • replace NodeInfoService by NodeFactRepository
  • change NodeInfo toward CoreNodeFact and adapt to the small attribute naming changes (add .rudderSettings, fqdn in place of hostname, and alway one agent and not a list)
  • add lots of implicit qr: QueryContext and weave them upword until we find a direct interaction with a user page / API.

So I tried to comment on all the things that are not trivials to make reviewable.

withDefautls,
policyServer,
withDefautls.toNodeInfo,
policyServer.toNodeInfo,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

InterpolationContext and CompareProperties are let untouched for now, so we have some conversion happening. It will be clean-up in a further iteration.

// policy servers are sorted by their promiximity with root, root first
a.sortBy(x => NodePriority(x)).map(_.id.value), // simple nodes are sorted alphanum
// policy servers are sorted by their proximity with root, root first
a.sortBy(x => NodePriority(x.id, x.isPolicyServer, x.policyServerId))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we're really just using these three attributes for priority, which are available on both NodeInfo and CoreNodeFact. It eases migration to be more specific here.

val onlySyslogSupported = {
if (versionHasSyslogOnly(Some(nodeInfo.rudderAgent.version), nodeInfo.rudderAgent.agentType)) Some(nodeInfo.rudderAgent)
else None
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We always have an agent now, so I adapted the logic

@fanf
Copy link
Member Author

fanf commented Dec 17, 2023

PR updated with a new commit

allNodeIds <- nodeInfoService.getAll().map(_.filter { case (_, n) => n.state != NodeState.Ignored }.keySet)
allNodeIds <- nodeFactRepository
.getAll()
.map(_.collect { case (_, n) if (n.rudderSettings.state != NodeState.Ignored) => n.id }.toSet)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(refactoring): we don't need to create a full collection with filter since we just want the keys, collect is better

build("n4", Some(PolicyMode.Audit))
).toMap

CoreNodeFactRepository.make(NoopFactStorage, NoopGetNodesbySofwareName, Map(), accepted, Chunk.empty).runNow
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The base repo for ReportingServiceTest is now a fact repo. It doesn't really matters since all tests are commented out in any case.

@@ -272,65 +280,66 @@ ootapja6lKOaIpqp0kmmYN7gFIhp
val rootHostname = "server.rudder.local"
val rootAdmin = "root"

val rootNode = Node(
val factRoot = CoreNodeFact(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

important change in test data creation in NodeConfigDate: now, the base data is a CoreNodeFact, and Node and NodeInfo are derived from it.

)

val node1Node = Node(
val rootNode = factRoot.toNode
val root = factRoot.toNodeInfo
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

derivation from facts for root

// node1 us a relay
val node2Node = node1Node.copy(id = id2, name = id2.value)
val node2 = node1.copy(node = node2Node, hostname = hostname2, policyServerId = root.id)
val fact2 = fact1.modify(_.id).setTo(id2).modify(_.fqdn).setTo(hostname2)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use quicklens to make more consistant update patterns on data

}
val accepted = nodes.map { case (n, _, _) => n }.toMap
val nodeFactRepo =
CoreNodeFactRepository.make(NoopFactStorage, NoopGetNodesbySofwareName, Map(), accepted, Chunk.empty).runNow
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now the base repo for CachedFindRuleNodeStatusReportsTest is a NodeFactRepository

@@ -282,6 +286,8 @@ class NodeApi(
params: DefaultParams,
authzToken: AuthzToken
): LiftResponse = {
implicit val qc: QueryContext = authzToken.qc
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

general pattern for API now: the QueryContext is weaved into authzToken, so just expose it

} yield {
optNode match {
case Some(node)
if (node.agentsName.exists(a => a.agentType == AgentType.CfeCommunity || a.agentType == AgentType.CfeEnterprise)) =>
if (node.rudderAgent.agentType == AgentType.CfeCommunity || node.rudderAgent.agentType == AgentType.CfeEnterprise) =>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we always have a rudder agent now

else if (pending.contains(id)) PendingInventory.name
else "deleted"
}
val status = nodes.get(NodeId(id)).map(_.rudderSettings.status.name).getOrElse("deleted")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(refactor)

} else {
val propMap = nodes.values.groupMapReduce(_.id)(n => n.properties.filter(_.name == property))(_ ++ _)
propMap.map { case (k, v) => (k, v.toList.map(_.toJson)) }.succeed
}): IOResult[Map[NodeId, List[JValue]]]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refactor:

  • propMap only used in the second case
  • properties are now Chunk in place of list => ++

toJsonError(None, message)
}
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't find any use of these two methods, not even in HTML or plugins

val message = (eb ?~ ("Could not fetch pending Nodes")).msg
toJsonError(None, message)
}
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same than previously: not found any use.

private[this] val asyncDeploymentAgent = RudderConfig.asyncDeploymentAgent
private[this] val configService = RudderConfig.configService
private[this] val boxNodeInfo = nodeInfoService.getNodeInfo(nodeId).toBox

def agentPolicyModeEditForm = new AgentPolicyModeEditForm()

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here (), the removal of nodeInfoService lead to substantial, non interesting changes and some simplification since we have NodeFact around, not just sometime inventory and sometime node info.

(for {
_ <- nodeRepo.updateNode(newNodeInfo.node, modId, user, None)
_ <- nodeFactRepo.save(NodeFact.fromMinimal(newNodeFact))(cc)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now us a ChangeContext to commit update to Node.
Two observations:

  • perhaps we would like to have moreinfo in CurrentUser like user IP,
  • it seems that we want to have a nodeFactRepo.save that directly works on CoreNodeFact and manage for the us the data conversion (plus it would help it knows how to optimize save)

@@ -99,13 +100,15 @@ class AsyncComplianceService(

// Compute compliance level for all rules in a future so it will be displayed asynchronously
val futureCompliance: Future[Box[Map[Kind, Option[ComplianceLevel]]]] = {
implicit val qc: QueryContext = CurrentUser.queryContext

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if there is something special with Future and query context. I don't think it matters in our usage (the user must be logged to call that), but something that may be relevant in complicated bugs at some point.

@@ -96,18 +98,16 @@ object DisplayNode extends Loggable {
private[this] val removeNodeService = RudderConfig.removeNodeService
private[this] val asyncDeploymentAgent = RudderConfig.asyncDeploymentAgent
private[this] val uuidGen = RudderConfig.stringUuidGenerator
private[this] val nodeInfoService = RudderConfig.nodeInfoService
private[this] val linkUtil = RudderConfig.linkUtil
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like in previous file, the removal of nodeInfoService leads to simplification and some changes.

val detailsId = htmlId(jsId, "details_")
val sm = nodeFact.toFullInventory
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We keep the mapping with FullNodeInventory because that part is low risk, but big => it can be done in latter iteration.

@@ -477,32 +480,25 @@ object DisplayNode extends Loggable {

// mimic the content of server_details/ShowNodeDetailsFromNode
def showNodeDetails(
sm: FullInventory,
nodeAndGlobalMode: Option[(NodeInfo, GlobalPolicyMode)],
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now, we don't have to wonder if we have the node info - we always have it from node fat. And not getting the GlobalCompliance is a normal error, there's no special case to do for it.

</div>
{displayServerRole(sm, inventoryStatus)}
<div><label>Agent:</label> {
sm.node.agents.map { a =>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have exactly one agent now

}
</div>
<div>
<label>Inventory received:</label> {
sm.node.receiveDate.map(DateFormaterService.getDisplayDate(_)).getOrElse("Unknown")
DateFormaterService.getDisplayDate(nodeFact.factProcessedDate)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the intent is clearer now: it's not the date when the inventory was received on rudder server, but the date when it was processed by rudder web app.

case AcceptedInventory =>
val nodeInfoBox = nodeInfoService.getNodeInfo(nodeId).either.runNow
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not relevant anymore. Plus, avoid more queries when we already have the data

.rudder_global_policy_mode()
.chainError(s" Could not get global policy mode when getting node '${uuid.value}' details")
data <- historyRepos.get(uuid, selectedDate)
} yield (globalMode, data)).toBox match {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

impact of previous change about really wanting the global policy mode: it needs to be retrieved here, too.

globalMode <- configService
.rudder_global_policy_mode()
.chainError(s" Could not get global policy mode when getting node '${uuid.value}' details")
m <- historyRepos.get(uuid, date)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as previous comment

globalMode <- configService
.rudder_global_policy_mode()
.chainError(s" Could not get global policy mode when getting node '${id.value}' details")
m <- history.get(id, version)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as previous comment

@fanf
Copy link
Member Author

fanf commented Dec 17, 2023

PR updated with a new commit

@fanf fanf force-pushed the arch_23927/migrate_away_from_nodeinfoservice branch 3 times, most recently from f65ea26 to f8f32cf Compare December 20, 2023 11:26
@fanf fanf force-pushed the arch_23927/migrate_away_from_nodeinfoservice branch from f8f32cf to 22155e2 Compare December 22, 2023 09:57
@Normation-Quality-Assistant
Copy link
Contributor

OK, merging this PR

@Normation-Quality-Assistant Normation-Quality-Assistant merged commit 22155e2 into Normation:master Dec 22, 2023
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants