Skip to content

Commit

Permalink
Merge pull request #738 from NationalSecurityAgency/ticket#736/projec…
Browse files Browse the repository at this point in the history
…t_expiration_locking

#736 add project expiration locking, meta data storage documenting ru…
  • Loading branch information
sudo-may authored Jul 12, 2021
2 parents 57a7af7 + 4a9e68d commit 120d6d6
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 10 deletions.
3 changes: 3 additions & 0 deletions service/src/main/java/skills/services/LockingService.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,8 @@ class LockingService {
return skillsDBLockRepo.findUserAttrsByUserId(userId?.toLowerCase())
}

SkillsDBLock lockForProjectExpiration() {
return skillsDBLockRepo.findByLock("project_expiration_lock")
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Transactional
import skills.controller.exceptions.ErrorCode
import skills.controller.exceptions.SkillException
import skills.controller.request.model.GlobalSettingsRequest
import skills.controller.request.model.ProjectSettingsRequest
import skills.controller.result.model.SettingsResult
import skills.notify.EmailNotifier
Expand All @@ -35,6 +36,7 @@ import skills.storage.model.auth.RoleName
import skills.storage.repos.ProjDefRepo
import skills.storage.repos.UserRoleRepo

import java.time.LocalDate
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit
Expand All @@ -45,6 +47,9 @@ class ProjectExpirationService {

private static final String SETTING_GROUP = "expiration"

public static final String SCHEDULED_SETTING_GROUP = "scheduled"
public static final String EXPIRATION_LAST_RUN_DATE = "expiration_last_run"

@Value('#{"${skills.config.expireUnusedProjectsOlderThan:180}"}')
int unusedProjectExpirationInDays

Expand All @@ -66,6 +71,9 @@ class ProjectExpirationService {
@Autowired
EmailNotifier notifier

@Autowired
LockingService lockingService

@Transactional
public void flagOldProjects(Date expireOlderThan) {
List<ProjectLastTouched> lastTouchedList = projDefRepo.findProjectsNotTouchedSince(expireOlderThan)
Expand Down Expand Up @@ -151,4 +159,36 @@ class ProjectExpirationService {
}
}
}

@Transactional
public void flagDeleteAndNotify() {
lockingService.lockForProjectExpiration()

final LocalDate todayLd = LocalDate.now()
final String today = todayLd.format(DateTimeFormatter.BASIC_ISO_DATE)

SettingsResult expirationLastRan = settingService.getGlobalSetting(EXPIRATION_LAST_RUN_DATE, SCHEDULED_SETTING_GROUP)

if (expirationLastRan && !LocalDate.parse(expirationLastRan.getValue(), DateTimeFormatter.BASIC_ISO_DATE).isBefore(todayLd)) {
log.info("project expiration was already run today (potentially by another node), will not run again today")
return
}

try {
log.info("identifying projects that haven't been used in [${unusedProjectExpirationInDays}] days")
Date expireOlderThan = new Date().minus(unusedProjectExpirationInDays)
flagOldProjects(expireOlderThan)
log.info("deleting projects whose grace period has expired")
Date cutoff = new Date().minus(unusedProjectExpirationGracePeriodInDays)
deleteUnusedProjects(cutoff)
log.info("sending pending deletion notifications to Project Administrators")
notifyGracePeriodProjectAdmins(cutoff)
} finally {
GlobalSettingsRequest lastRunSettingRequest = new GlobalSettingsRequest()
lastRunSettingRequest.value = today
lastRunSettingRequest.setting = EXPIRATION_LAST_RUN_DATE
lastRunSettingRequest.settingGroup = SCHEDULED_SETTING_GROUP
settingService.saveSetting(lastRunSettingRequest)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class ScheduledProjectExpiration {
@Value('#{"${skills.config.expirationGracePeriod:7}"}')
int unusedProjectExpirationGracePeriodInDays

@Scheduled(cron='#{"${skills.config.projectExpirationSchedule:* 4 0 * * *}"}')
@Scheduled(cron='#{"${skills.config.projectExpirationSchedule:0 4 0 * * *}"}')
public void flagUnusedProjectsForDeletion(){
if (!unusedProjectDeletionEnabled) {
log.debug("skills.config.unusedProjectDeletionEnabled is set to false, unused project deletion will not occur")
Expand All @@ -50,14 +50,8 @@ class ScheduledProjectExpiration {
log.debug("Email Settings have not configured for this instance, unused project deletion will not occur")
return
}
log.info("identifying projects that haven't been used in [${unusedProjectExpirationInDays}] days")
Date expireOlderThan = new Date().minus(unusedProjectExpirationInDays)
projectExpirationService.flagOldProjects(expireOlderThan)
log.info("deleting projects whose grace period has expired")
Date cutoff = new Date().minus(unusedProjectExpirationGracePeriodInDays)
projectExpirationService.deleteUnusedProjects(cutoff)
log.info("sending pending deletion notifications to Project Administrators")
projectExpirationService.notifyGracePeriodProjectAdmins(cutoff)

projectExpirationService.flagDeleteAndNotify()
}

}
2 changes: 1 addition & 1 deletion service/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ skills:
unusedProjectDeletionEnabled: true
expireUnusedProjectsOlderThan: 180
expirationGracePeriod: 7
projectExpirationSchedule: "* 4 0 * * *"
projectExpirationSchedule: "0 4 0 * * *"
ui:
dashboardVersion: @pom.version@
buildTimestamp: @maven.build.timestamp@
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1306,4 +1306,10 @@
</sql>
</changeSet>

<changeSet id="10" author="skills team" context="migration10">
<insert tableName="skills_db_locks">
<column name="lock" type="varchar(255)" value="project_expiration_lock" />
</insert>
</changeSet>

</databaseChangeLog>
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@
*/
package skills.intTests

import org.junit.Rule
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.system.OutputCaptureRule
import org.springframework.transaction.PlatformTransactionManager
import org.springframework.transaction.TransactionStatus
import org.springframework.transaction.support.TransactionCallback
import org.springframework.transaction.support.TransactionTemplate
import skills.controller.request.model.GlobalSettingsRequest
import skills.controller.result.model.SettingsResult
import skills.intTests.utils.DefaultIntSpec
import skills.intTests.utils.EmailUtils
import skills.intTests.utils.SkillsFactory
Expand All @@ -29,9 +33,15 @@ import skills.services.UserEventService
import skills.services.settings.SettingsService
import skills.storage.model.UserAttrs
import skills.storage.repos.SkillDefRepo
import skills.utils.LoggerHelper
import skills.utils.WaitFor
import spock.lang.IgnoreRest

import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.util.concurrent.Callable
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.regex.Pattern

Expand All @@ -55,6 +65,7 @@ class UnusedProjectExpirationSpecs extends DefaultIntSpec{
@Autowired
ProjectErrorService errorService


def setup() {
startEmailServer()
}
Expand Down Expand Up @@ -218,4 +229,22 @@ class UnusedProjectExpirationSpecs extends DefaultIntSpec{
emails.find { p3.matcher(it.html).find() }
}

def "only one run per day" () {
LoggerHelper loggerHelper = new LoggerHelper(ProjectExpirationService)

when:

expirationService.flagDeleteAndNotify()

expirationService.flagDeleteAndNotify()
expirationService.flagDeleteAndNotify()
expirationService.flagDeleteAndNotify()

then:
loggerHelper.logEvents.each { println it.message }
loggerHelper.logEvents.findAll { it.message.contains("identifying projects that haven't been used in") }.size() == 1
loggerHelper.logEvents.findAll { it.message.contains("project expiration was already run today (potentially by another node), will not run again today")}.size() == 3

}

}
10 changes: 10 additions & 0 deletions service/src/test/java/skills/services/LockingServiceSpec.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,14 @@ class LockingServiceSpec extends DefaultIntSpec {
lock
}

@Transactional
def "lock project expiration"() {
when:
SkillsDBLock lock = lockingService.lockForProjectExpiration()

then:
lock
}


}

0 comments on commit 120d6d6

Please sign in to comment.