Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,12 @@ package com.normation.rudder.campaigns
import cats.implicits.*
import com.normation.errors.*
import com.normation.utils.DateFormaterService
import com.normation.utils.DateFormaterService.JavaTimeToJoda
import com.normation.utils.DateFormaterService.toOffsetDateTime
import java.time.OffsetDateTime
import java.time.ZonedDateTime
import java.time.ZoneId
import java.time.temporal.ChronoUnit
import java.time.temporal.TemporalAdjusters
import java.time.temporal.TemporalAdjusters.dayOfWeekInMonth
import org.joda.time.DateTime
import scala.math.Ordered.orderingToOrdered

/*
Expand All @@ -59,7 +56,7 @@ object CampaignDateScheduler {

extension (date: OffsetDateTime) {
def adjustScheduleTimeZone(optTimeZone: Option[ScheduleTimeZone])(implicit defaultTz: ZoneId): ZonedDateTime = {
optTimeZone.flatMap(_.toZoneId) match {
optTimeZone.map(_.id) match {
// no schedule time zone (or invalid one) means the default one should be used
case None => date.atZoneSameInstant(defaultTz)
case Some(scheduleTz) => date.atZoneSameInstant(scheduleTz)
Expand Down Expand Up @@ -95,8 +92,8 @@ object CampaignDateScheduler {

def nextCampaignDate(
schedule: CampaignSchedule,
date: DateTime
): PureResult[Option[(DateTime, DateTime)]] = {
date: OffsetDateTime
): PureResult[Option[(OffsetDateTime, OffsetDateTime)]] = {
// Schedule needs to be adjusted to current server timezone, not to the one from the base schedule date
given currentTz: ZoneId = ZoneId.systemDefault()
schedule match {
Expand All @@ -113,7 +110,7 @@ object CampaignDateScheduler {
}

case Daily(start, end, tz) =>
val scheduleInitialDate = date.toOffsetDateTime.adjustScheduleTimeZone(tz)
val scheduleInitialDate = date.adjustScheduleTimeZone(tz)
val startDate = {
if (
scheduleInitialDate.getHour > start.realHour ||
Expand All @@ -133,17 +130,17 @@ object CampaignDateScheduler {
}
}

Some((startDate.toJoda, endDate.toJoda)).asRight
Some((startDate.toOffsetDateTime, endDate.toOffsetDateTime)).asRight

case WeeklySchedule(start, end, tz) =>
val scheduleInitialDate = date.toOffsetDateTime.adjustScheduleTimeZone(tz)
val scheduleInitialDate = date.adjustScheduleTimeZone(tz)
val startDate = nextDateFromDayTime(scheduleInitialDate, start)
val endDate = nextDateFromDayTime(startDate, end)

Some((startDate.toJoda, endDate.toJoda)).asRight
Some((startDate.toOffsetDateTime, endDate.toOffsetDateTime)).asRight

case MonthlySchedule(position, start, end, tz) =>
val scheduleInitialDate = date.toOffsetDateTime.adjustScheduleTimeZone(tz)
val scheduleInitialDate = date.adjustScheduleTimeZone(tz)

val ordinalForDayOfWeekInMonth = position match {
case First => 1
Expand All @@ -164,7 +161,7 @@ object CampaignDateScheduler {
currentMonthStart
}
val endDate = nextDateFromDayTime(startDate, end)
Some((startDate.toJoda, endDate.toJoda)).asRight
Some((startDate.toOffsetDateTime, endDate.toOffsetDateTime)).asRight
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,13 @@ import com.normation.errors.*
import com.normation.rudder.campaigns.CampaignEventStateType.ScheduledType
import com.normation.rudder.db.Doobie
import com.normation.rudder.db.json.implicits.*
import com.normation.utils.DateFormaterService.toJavaInstant
import doobie.*
import doobie.implicits.*
import doobie.postgres.implicits.*
import io.scalaland.chimney.*
import io.scalaland.chimney.syntax.*
import java.time.Instant
import org.joda.time.DateTime
import java.time.OffsetDateTime
import zio.*
import zio.interop.catz.*
import zio.json.*
Expand Down Expand Up @@ -78,8 +77,8 @@ trait CampaignEventRepository {
states: List[CampaignEventStateType] = Nil,
campaignType: Option[CampaignType] = None,
campaignId: Option[CampaignId] = None,
afterDate: Option[DateTime] = None,
beforeDate: Option[DateTime] = None
afterDate: Option[OffsetDateTime] = None,
beforeDate: Option[OffsetDateTime] = None
): IOResult[Unit]

/*
Expand All @@ -93,16 +92,15 @@ trait CampaignEventRepository {
campaignId: Option[CampaignId] = None,
limit: Option[Int] = None,
offset: Option[Int] = None,
afterDate: Option[DateTime] = None,
beforeDate: Option[DateTime] = None,
afterDate: Option[OffsetDateTime] = None,
beforeDate: Option[OffsetDateTime] = None,
order: Option[CampaignSortOrder] = None,
asc: Option[CampaignSortDirection] = None
): IOResult[List[CampaignEvent]]
}

class CampaignEventRepositoryImpl(doobie: Doobie, campaignSerializer: CampaignSerializer) extends CampaignEventRepository {

import com.normation.rudder.db.Doobie.DateTimeMeta
import doobie.*

implicit val campaignEventStateMeta: Meta[CampaignEventStateType] =
Expand Down Expand Up @@ -179,8 +177,8 @@ class CampaignEventRepositoryImpl(doobie: Doobie, campaignSerializer: CampaignSe
campaignId: CampaignId,
name: String,
state: CampaignEventStateType,
start: DateTime,
end: DateTime,
start: OffsetDateTime,
end: OffsetDateTime,
campaignType: CampaignType
)

Expand All @@ -201,8 +199,8 @@ class CampaignEventRepositoryImpl(doobie: Doobie, campaignSerializer: CampaignSe
campaignId: CampaignId,
name: String,
state: CampaignEventStateType,
start: DateTime,
end: DateTime,
start: OffsetDateTime,
end: OffsetDateTime,
campaignType: CampaignType,
data: Option[DATA]
) {
Expand Down Expand Up @@ -236,8 +234,8 @@ class CampaignEventRepositoryImpl(doobie: Doobie, campaignSerializer: CampaignSe
import com.normation.rudder.campaigns.CampaignEventState.*
Transformer
.define[CampaignEvent, CampaignEventHistoryInsert]
.withFieldComputed(_.start, x => x.start.toJavaInstant)
.withFieldComputed(_.end, x => Some(x.end.toJavaInstant))
.withFieldComputed(_.start, x => x.start.toInstant)
.withFieldComputed(_.end, x => Some(x.end.toInstant))
.withFieldComputed(_.state, _.state.value)
.withFieldComputed(
_.data,
Expand Down Expand Up @@ -281,8 +279,8 @@ class CampaignEventRepositoryImpl(doobie: Doobie, campaignSerializer: CampaignSe
campaignId: Option[CampaignId] = None,
limit: Option[Int] = None,
offset: Option[Int] = None,
afterDate: Option[DateTime] = None,
beforeDate: Option[DateTime] = None,
afterDate: Option[OffsetDateTime] = None,
beforeDate: Option[OffsetDateTime] = None,
order: Option[CampaignSortOrder],
asc: Option[CampaignSortDirection]
): IOResult[List[CampaignEvent]] = {
Expand All @@ -295,8 +293,8 @@ class CampaignEventRepositoryImpl(doobie: Doobie, campaignSerializer: CampaignSe
val campaignIdQuery = campaignId.map(c => fr"campaignId = ${c.value}")
val campaignTypeQuery = campaignType.toNel.map(c => Fragments.in(fr"campaignType", c))
val stateQuery = states.toNel.map(s => Fragments.in(fr"e.state", s))
val afterQuery = afterDate.map(d => fr"e.endDate >= ${new java.sql.Timestamp(d.getMillis)}")
val beforeQuery = beforeDate.map(d => fr"e.startDate <= ${new java.sql.Timestamp(d.getMillis)}")
val afterQuery = afterDate.map(d => fr"e.endDate >= $d}")
val beforeQuery = beforeDate.map(d => fr"e.startDate <= $d}")
val where = Fragments.whereAndOpt(campaignIdQuery, campaignTypeQuery, stateQuery, afterQuery, beforeQuery)

val limitQuery = limit.map(i => fr" limit $i").getOrElse(fr"")
Expand Down Expand Up @@ -372,17 +370,17 @@ class CampaignEventRepositoryImpl(doobie: Doobie, campaignSerializer: CampaignSe
states: List[CampaignEventStateType],
campaignType: Option[CampaignType],
campaignId: Option[CampaignId],
afterDate: Option[DateTime],
beforeDate: Option[DateTime]
afterDate: Option[OffsetDateTime],
beforeDate: Option[OffsetDateTime]
): ConnectionIO[RuntimeFlags] = {

import _root_.cats.syntax.list.*
val eventIdQuery = id.map(c => fr"eventId = ${c.value}")
val campaignIdQuery = campaignId.map(c => fr"campaignId = ${c.value}")
val campaignTypeQuery = campaignType.map(c => fr"campaignType = ${c.value}")
val stateQuery = states.toNel.map(s => Fragments.in(fr"state", s))
val afterQuery = afterDate.map(d => fr"endDate >= ${new java.sql.Timestamp(d.getMillis)}")
val beforeQuery = beforeDate.map(d => fr"startDate <= ${new java.sql.Timestamp(d.getMillis)}")
val afterQuery = afterDate.map(d => fr"endDate >= $d}")
val beforeQuery = beforeDate.map(d => fr"startDate <= $d}")
val where = Fragments.whereAndOpt(eventIdQuery, campaignIdQuery, campaignTypeQuery, stateQuery, afterQuery, beforeQuery)
val query = sql"""delete from campaignEvents """ ++ where

Expand All @@ -394,8 +392,8 @@ class CampaignEventRepositoryImpl(doobie: Doobie, campaignSerializer: CampaignSe
states: List[CampaignEventStateType] = Nil,
campaignType: Option[CampaignType] = None,
campaignId: Option[CampaignId] = None,
afterDate: Option[DateTime] = None,
beforeDate: Option[DateTime] = None
afterDate: Option[OffsetDateTime] = None,
beforeDate: Option[OffsetDateTime] = None
): IOResult[Unit] = {
if (List[Iterable[Any]](id, states, campaignType, campaignId, afterDate, beforeDate).exists(_.nonEmpty)) {
transactIOResult(s"error when deleting campaign event")(xa =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import com.normation.rudder.hooks.HookReturnCode
import com.normation.utils.DateFormaterService
import com.normation.utils.StringUuidGenerator
import com.softwaremill.quicklens.*
import org.joda.time.{Duration as JTDuration, *}
import java.time.OffsetDateTime
import zio.*
import zio.syntax.*

Expand Down Expand Up @@ -120,7 +120,7 @@ class CampaignOrchestrationLogic(effects: CampaignOrchestrationEffects) {
* error (fiber or whatever), it needs to be taken care by the workflow engine runtime based on what makes sens for
* it. (in the queue case, just let it crash)
*/
def handle(eventId: CampaignEventId, now: DateTime, startDelay: JTDuration, endDelay: JTDuration): UIO[Unit] = {
def handle(eventId: CampaignEventId, now: OffsetDateTime, startDelay: Duration, endDelay: Duration): UIO[Unit] = {

effects
.getEventInfo(eventId)
Expand Down Expand Up @@ -175,9 +175,9 @@ class CampaignOrchestrationLogic(effects: CampaignOrchestrationEffects) {
def orchestrateEventType(
campaign: Campaign,
event: CampaignEvent,
now: DateTime,
startDelay: JTDuration,
endDelay: JTDuration
now: OffsetDateTime,
startDelay: Duration,
endDelay: Duration
): IOResult[EventOrchestration] = {
event.state.value match {

Expand Down Expand Up @@ -211,10 +211,10 @@ class CampaignOrchestrationLogic(effects: CampaignOrchestrationEffects) {
_ <-
CampaignLogger.debug(
s"Scheduled Campaign event ${event.id.value} put to sleep until it should start, on ${DateFormaterService
.serialize(effectiveStart)}, ${startDelay.getStandardHours} hour${if (startDelay.getStandardHours > 1) "s"
.serializeOffsetDateTime(effectiveStart)}, ${startDelay.toHours} hour${if (startDelay.toHours > 1) "s"
else ""} before official start date, to ensure policies are correctly dispatched, nothing will be applied on the node"
)
_ <- ZIO.sleep(Duration.fromMillis(effectiveStart.getMillis - now.getMillis))
_ <- ZIO.sleep(Duration.fromInterval(now, effectiveStart))
// re-post processing because anything can happen during the sleep, it will need to
// be managed as a new event handling
} yield EventOrchestration.Queue(event.id)
Expand Down Expand Up @@ -250,7 +250,7 @@ class CampaignOrchestrationLogic(effects: CampaignOrchestrationEffects) {
_ <-
CampaignLogger.warn(
s"Campaign event ${event.id.value} was considered Running but we are before its start date, setting state to Schedule and wait for event to start, on ${DateFormaterService
.serialize(effectiveStart)}, ${startDelay.getStandardHours} hour${if (startDelay.getStandardHours > 1) "s"
.serializeOffsetDateTime(effectiveStart)}, ${startDelay.toHours} hour${if (startDelay.toHours > 1) "s"
else ""} before official start date, to ensure policies are correctly dispatched, nothing will be applied on the node"
)
} yield EventOrchestration.SaveAndQueue(event.copy(state = Scheduled))
Expand All @@ -259,9 +259,9 @@ class CampaignOrchestrationLogic(effects: CampaignOrchestrationEffects) {
_ <-
CampaignLogger.debug(
s"Running Campaign event ${event.id.value} put to sleep until it should end, on ${DateFormaterService
.serialize(effectiveEnd)}, ${endDelay.getStandardHours} hour${if (endDelay.getStandardHours > 1) "s" else ""} after official end date, so that we can gather results"
.serializeOffsetDateTime(effectiveEnd)}, ${endDelay.toHours} hour${if (endDelay.toHours > 1) "s" else ""} after official end date, so that we can gather results"
)
_ <- ZIO.sleep(Duration.fromMillis(effectiveEnd.getMillis - now.getMillis))
_ <- ZIO.sleep(Duration.fromInterval(now, effectiveEnd))
// reprocess event as a new things, anything can happen during sleep
} yield EventOrchestration.Queue(event.id)
} else {
Expand Down Expand Up @@ -358,8 +358,8 @@ object CampaignOrchestrationLogic {
def bootstrapEvent(
campaign: Campaign,
uuid: String,
start: DateTime,
end: DateTime,
start: OffsetDateTime,
end: OffsetDateTime,
index: Int
): EventOrchestration.SaveAndQueue = {
EventOrchestration.SaveAndQueue(
Expand Down Expand Up @@ -391,7 +391,7 @@ trait CampaignOrchestrationEffects {
* clean existing things from the previous scheduled if any exists.
* Return the number of scheduled events
*/
def createNextScheduledCampaignEvent(campaign: Campaign, date: DateTime): IOResult[EventOrchestration]
def createNextScheduledCampaignEvent(campaign: Campaign, date: OffsetDateTime): IOResult[EventOrchestration]

/*
* Retrieve information about an event and its campaign
Expand Down Expand Up @@ -460,7 +460,7 @@ class DefaultCampaignOrchestrationEffects(
* Return the number of scheduled events.
* It looks like it should belong to campaign repository for main parts
*/
override def createNextScheduledCampaignEvent(campaign: Campaign, date: DateTime): IOResult[EventOrchestration] = {
override def createNextScheduledCampaignEvent(campaign: Campaign, date: OffsetDateTime): IOResult[EventOrchestration] = {

campaign.info.status match {
case Enabled =>
Expand Down
Loading
Loading