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 #7288: Policy does not get deleted when changing relays #2591

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
fc7b7e1
Fixes #7288: Policy does not get deleted when changing relays
VinceMacBuche Nov 13, 2019
578a373
fixup! Fixes #7288: Policy does not get deleted when changing relays
VinceMacBuche Nov 21, 2019
bd70692
fixup! fixup! Fixes #7288: Policy does not get deleted when changing …
VinceMacBuche Nov 21, 2019
2349b88
fixup! fixup! fixup! Fixes #7288: Policy does not get deleted when ch…
VinceMacBuche Nov 21, 2019
2e8ebc7
fixup! fixup! fixup! fixup! Fixes #7288: Policy does not get deleted …
VinceMacBuche Nov 21, 2019
3277ceb
fixup! fixup! fixup! fixup! fixup! Fixes #7288: Policy does not get d…
VinceMacBuche Nov 21, 2019
cf1930f
fixup! fixup! fixup! fixup! fixup! fixup! Fixes #7288: Policy does no…
VinceMacBuche Nov 21, 2019
a27e025
fixup! fixup! fixup! fixup! fixup! fixup! fixup! Fixes #7288: Policy …
VinceMacBuche Nov 21, 2019
4f8e8f2
fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! Fixes #7288: …
VinceMacBuche Nov 22, 2019
6bceb27
fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! Fixes …
VinceMacBuche Nov 22, 2019
e56e922
fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup!…
VinceMacBuche Nov 22, 2019
9c0b029
fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup!…
VinceMacBuche Nov 22, 2019
8676f22
fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup!…
VinceMacBuche Nov 22, 2019
4059b49
fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup!…
VinceMacBuche Nov 22, 2019
3f97eff
fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup!…
VinceMacBuche Jan 14, 2020
7296979
fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup!…
VinceMacBuche Jan 14, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion webapp/sources/rudder-parent-pom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ limitations under the License.
<bcpkix-jdk15on-version>1.60</bcpkix-jdk15on-version>
<monix-version>2.3.3</monix-version>
<silencer-lib-version>1.2</silencer-lib-version>
<better-files-version>3.7.0</better-files-version>
<better-files-version>3.8.0</better-files-version>
<!--
These one must be updated to work together
We declare cats in "test" here, because it is not directly needed
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
*************************************************************************************
* Copyright 2019 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 <http://www.gnu.org/licenses/>.

*
*************************************************************************************
*/

package com.normation.rudder.batch

import java.nio.file.FileSystemException

import better.files.File
import com.normation.inventory.domain.NodeId
import com.normation.rudder.domain.logger.ScheduledJobLogger
import com.normation.rudder.domain.nodes.NodeInfo
import com.normation.rudder.services.nodes.NodeInfoService
import monix.execution.Scheduler.{global => scheduler}
import net.liftweb.common._

import scala.concurrent.duration._


class CleanPoliciesService (nodeInfoService: NodeInfoService) extends Loggable {

def cleanPolicies: Box[List[String]] = {

def initPolicyCleaning (currentNodes : Map[NodeId,NodeInfo]) : Box[List[String]] = {
import File._

def cleanPoliciesRec(file: File, parentId: String): Iterator[String] = {
file.children.flatMap {
nodeFolder =>
if (logger.isDebugEnabled) {
val openfd = try {
(root / "proc" / "self").children.size
} catch {
case fse: FileSystemException =>
-1
}
logger.debug(s"Currently ${openfd} open files by Rudder Server process")
}
val nodeId = NodeId(nodeFolder.name)
def delete = {
nodeFolder.delete()
Iterator(nodeFolder.name)
}
currentNodes.get(nodeId) match {
// Node was deleted
case None =>
delete
// Node was moved to another relay
case Some(nodeInfo) if parentId != nodeInfo.policyServerId.value =>
delete
// Node is a policy server and has some policies generated below, let's explore those policies
case Some(nodeInfo) if nodeInfo.isPolicyServer && (nodeFolder / "share").exists =>
cleanPoliciesRec(nodeFolder / "share", nodeFolder.name)
// Just a valid node, or a relay without nodes, skip
case Some(_) =>
Iterator.empty
}
}
}

try {
Full(cleanPoliciesRec(root / "var" / "rudder" / "share", "root").toList )
} catch {
case fse : FileSystemException =>

val openfd = try {
(root / "proc" / "self").children.size
} catch {
case fse: FileSystemException =>
-1
}
Failure(s"Too many files open (currently ${openfd}, but number may have dropped), maybe you can raise open file descriptor limit (ulimit -n)", Full(fse), Empty)
}
}


(for {
nodes <- nodeInfoService.getAll()
deleted <- initPolicyCleaning(nodes)
} yield {
deleted
}) match {
case eb: EmptyBox =>
val error = (eb ?~! s"Error when deleting unreferenced policies directory")
logger.error(error.messageChain)
error.rootExceptionCause.foreach(ex =>
logger.debug("Exception was:", ex)
)
error
case Full(deleted) =>
logger.info(s"Deleted ${deleted.length} unreferenced policies folder")
if (logger.isDebugEnabled && deleted.length > 0)
logger.debug(s"Deleted policies folder of Nodes: ${deleted.mkString(",")}")
Full(deleted)
}
}
}

class CleanPoliciesJob(
cleanPoliciesService: CleanPoliciesService
, runInterval : FiniteDuration
) {

val logger = ScheduledJobLogger

if (runInterval < 1.hour) {
logger.info(s"Disable automatic unreferenced policies directory (update interval cannot be less than 1 hour)")
} else {
logger.debug(s"***** starting batch that purge unreferenced policies directory, every ${runInterval.toString()} *****")
scheduler.scheduleWithFixedDelay(1.hour, runInterval) {
cleanPoliciesService.cleanPolicies
}
}


}


Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ class PolicyWriterServiceImpl(

//interpret HookReturnCode as a Box


val readTemplateTime1 = System.currentTimeMillis
for {
configAndPaths <- calculatePathsForNodeConfigurations(interestingNodeConfigs, rootNodeId, allNodeInfos, newPostfix, backupPostfix)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,11 @@ object SystemApi extends ApiModuleProvider[SystemApi] {
val (action, path) = POST / "system" / "regenerate" / "policies"
}

final case object CleanPolicies extends SystemApi with ZeroParam with StartsAtVersion11 with SortIndex { val z = implicitly[Line].value
val description = "Delete policies of deleted nodes or nodes that have been moved to another policy server"
val (action, path) = POST / "system" / "clean" / "policies"
}

// Archive list endpoints

final case object ArchivesGroupsList extends SystemApi with ZeroParam with StartsAtVersion11 with SortIndex { val z = implicitly[Line].value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import com.normation.eventlog.{EventActor, ModificationId}
import com.normation.cfclerk.services.{GitRepositoryProvider, UpdateTechniqueLibrary}
import com.normation.rudder.batch.{AsyncDeploymentAgent, ManualStartDeployment, UpdateDynamicGroups}
import com.normation.rudder.UserService
import com.normation.rudder.batch.CleanPoliciesService
import com.normation.rudder.repository.xml.{GitFindUtils, GitTagDateTimeFormatter}
import com.normation.rudder.repository.{GitArchiveId, GitCommitId, ItemArchiveManager}
import com.normation.rudder.services.{ClearCacheService, DebugInfoScriptResult, DebugInfoService}
Expand Down Expand Up @@ -106,6 +107,7 @@ class SystemApi(
case API.GetDirectivesZipArchive => GetDirectivesZipArchive
case API.GetRulesZipArchive => GetRulesZipArchive
case API.GetAllZipArchive => GetAllZipArchive
case API.CleanPolicies => CleanPolicies
})
}

Expand Down Expand Up @@ -149,6 +151,15 @@ class SystemApi(

}

object CleanPolicies extends LiftApiModule0 {
val schema = API.CleanPolicies
val restExtractor = restExtractorService

def process0(version: ApiVersion, path: ApiPath, req: Req, params: DefaultParams, authzToken: AuthzToken): LiftResponse = {
apiv11service.cleanPolicies(req, params)
}
}

//Reload [techniques / dynamics groups] endpoint implementation

object TechniquesReload extends LiftApiModule0 {
Expand Down Expand Up @@ -428,6 +439,7 @@ class SystemApiService11(
, itemArchiveManager : ItemArchiveManager
, personIdentService : PersonIdentService
, repo : GitRepositoryProvider
, cleanPoliciesService : CleanPoliciesService
) (implicit userService: UserService) extends Loggable {


Expand Down Expand Up @@ -937,4 +949,19 @@ class SystemApiService11(
asyncDeploymentAgent.launchDeployment(dest)
toJsonResponse(None, "policies" -> "Started")
}


def cleanPolicies(req: Req, params: DefaultParams) : LiftResponse = {

implicit val action = "cleanPolicies"
implicit val prettify = params.prettify

cleanPoliciesService.cleanPolicies match {
case emptyBox: EmptyBox =>
val fail = emptyBox ?~! "An error while cleaning policies folder through rest API"
toJsonError(None, fail.messageChain)
case Full(deleted) =>
toJsonResponse(None, ("length" -> deleted.length) ~ ("nodes" -> deleted))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import com.normation.cfclerk.services.{TechniquesLibraryUpdateNotification, Tech
import com.normation.eventlog.{EventActor, EventLog, ModificationId}
import net.liftweb.common.Box
import com.normation.cfclerk.domain.TechniqueName
import com.normation.inventory.domain.NodeId
import net.liftweb.http.LiftResponse
import net.liftweb.util.NamedPF
import net.liftweb.http.S
Expand All @@ -59,18 +60,24 @@ import com.normation.rudder.User
import com.normation.rudder.AuthorizationType
import com.normation.rudder.RudderAccount
import com.normation.rudder.api.{ApiAuthorization => ApiAuthz}
import com.normation.rudder.batch.CleanPoliciesService
import com.normation.rudder.batch.{AsyncDeploymentAgent, StartDeploymentMessage, UpdateDynamicGroups}
import com.normation.rudder.domain.nodes.Node
import com.normation.rudder.domain.nodes.NodeInfo
import com.normation.rudder.domain.queries.CriterionComposition
import com.normation.rudder.domain.queries.NodeInfoMatcher
import com.normation.rudder.repository._
import com.normation.rudder.rest.v1.RestTechniqueReload
import com.normation.rudder.rest.v1.RestStatus
import com.normation.rudder.rest.lift.{LiftApiProcessingLogger, LiftHandler, SystemApiService11}
import com.normation.rudder.services.nodes.LDAPNodeInfo
import com.normation.rudder.services.nodes.NodeInfoService
import com.normation.rudder.services.{ClearCacheService, DebugInfoScriptResult, DebugInfoService}
import com.normation.rudder.services.policies.TestNodeConfiguration
import com.normation.rudder.services.user.PersonIdentService
import org.eclipse.jgit.lib.PersonIdent
import org.joda.time.DateTime


/*
* This file provides all the necessary plumbing to allow test REST API.
*
Expand Down Expand Up @@ -201,11 +208,22 @@ object RestTestSetUp {



val testNodeConfiguration = new TestNodeConfiguration()
val fakeRepo = testNodeConfiguration.repo
val fakeScriptLauncher = new DebugInfoService {
override def launch(): Box[DebugInfoScriptResult] = Full(DebugInfoScriptResult("test", new Array[Byte](42)))
}
val testNodeConfiguration = new TestNodeConfiguration()
val fakeRepo = testNodeConfiguration.repo
val fakeScriptLauncher = new DebugInfoService {
override def launch(): Box[DebugInfoScriptResult] = Full(DebugInfoScriptResult("test", new Array[Byte](42)))
}
val fakeNodeInfoService = new NodeInfoService {
def getLDAPNodeInfo(nodeIds: Set[NodeId], predicates: Seq[NodeInfoMatcher], composition: CriterionComposition): Box[Set[LDAPNodeInfo]] = Full(Set())
def getNodeInfo(nodeId: NodeId): Box[Option[NodeInfo]] = Full(None)
def getAll(): Box[Map[NodeId, NodeInfo]] = Full(Map())
def getAllNodes(): Box[Map[NodeId, Node]] = Full(Map())
def getAllSystemNodeIds(): Box[Seq[NodeId]] = Full(Seq())
def getPendingNodeInfos(): Box[Map[NodeId, NodeInfo]] = Full(Map())
def getPendingNodeInfo(nodeId: NodeId): Box[Option[NodeInfo]] = Full(None)
def getDeletedNodeInfos(): Box[Map[NodeId, NodeInfo]] = Full(Map())
def getDeletedNodeInfo(nodeId: NodeId): Box[Option[NodeInfo]] = Full(None)
}

val apiService11 = new SystemApiService11(
fakeUpdatePTLibService
Expand All @@ -217,6 +235,7 @@ object RestTestSetUp {
, fakeItemArchiveManager
, fakePersonIndentService
, fakeRepo
, new CleanPoliciesService(fakeNodeInfoService)
)

val systemApi = new com.normation.rudder.rest.lift.SystemApi(restExtractorService, apiService11, "5.0", "5.0.0", "some time")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,22 @@ rudder.batch.purge.inventories.delete.TTL=7
# Interval in hours
rudder.batch.purge.inventories.delete.interval=24

###########################
# Policies folder cleaning ###########################################################
###########################

#
# Policies folder cleaning
# This allows you to schedule the purge of unused policies folders (Deleted Nodes, or Nodes that has moved to another relay/policy server)
#
#
# Defaults: garbage collection runs every 24 hours.
#

# Interval in hours
rudder.batch.clean.policies.interval=24


#####################
# Webdav properties #################################################################
#####################
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,16 @@ object RudderConfig extends Loggable {
}
}

val RUDDER_BATCH_CLEAN_POLICIES_INTERVAL = {
try {
config.getInt("rudder.batch.clean.policies.interval")
} catch {
case ex: ConfigException =>
ApplicationLogger.info("Property 'rudder.batch.clean.policies.interval' is missing or empty in rudder.configFile. Default to 24 hours.")
24
}
}

// Roles definitions
val RUDDER_SERVER_ROLES = Seq(
//each time, it's (role name, key in the config file)
Expand Down Expand Up @@ -462,6 +472,8 @@ object RudderConfig extends Loggable {
val checkInventoryUpdate = new CheckInventoryUpdate(nodeInfoServiceImpl, asyncDeploymentAgent, stringUuidGenerator, 15.seconds)
val purgeDeletedInventories = new PurgeDeletedInventories(removeNodeServiceImpl, RUDDER_BATCH_PURGE_DELETED_INVENTORIES_INTERVAL.hours, RUDDER_BATCH_PURGE_DELETED_INVENTORIES)
val purgeUnreferencedSoftwares = new PurgeUnreferencedSoftwares(softwareService, RUDDER_BATCH_DELETE_SOFTWARE_INTERVAL.hours)
val cleanPoliciesService = new CleanPoliciesService(nodeInfoServiceImpl)
val cleanPoliciesFolder = new CleanPoliciesJob(cleanPoliciesService, RUDDER_BATCH_CLEAN_POLICIES_INTERVAL.hours)
val databaseManager: DatabaseManager = databaseManagerImpl
val automaticReportsCleaning: AutomaticReportsCleaning = dbCleaner
val checkTechniqueLibrary: CheckTechniqueLibrary = techniqueLibraryUpdater
Expand Down Expand Up @@ -769,6 +781,7 @@ object RudderConfig extends Loggable {
, itemArchiveManager
, personIdentService
, gitRepo
, cleanPoliciesService
)

private[this] val complianceAPIService = new ComplianceAPIService(
Expand Down