mirror of
https://github.com/VinylDNS/vinyldns
synced 2025-08-29 13:27:43 +00:00
Add zone sync scheduler
This commit is contained in:
parent
94277086a6
commit
1a9bf5cd89
@ -46,10 +46,15 @@ import scala.concurrent.{ExecutionContext, Future}
|
|||||||
import scala.io.{Codec, Source}
|
import scala.io.{Codec, Source}
|
||||||
import vinyldns.core.notifier.NotifierLoader
|
import vinyldns.core.notifier.NotifierLoader
|
||||||
import vinyldns.core.repository.DataStoreLoader
|
import vinyldns.core.repository.DataStoreLoader
|
||||||
|
import java.util.concurrent.{Executors, ScheduledExecutorService, TimeUnit}
|
||||||
|
|
||||||
object Boot extends App {
|
object Boot extends App {
|
||||||
|
|
||||||
private val logger = LoggerFactory.getLogger("Boot")
|
private val logger = LoggerFactory.getLogger("Boot")
|
||||||
|
|
||||||
|
// Create a ScheduledExecutorService with a single thread
|
||||||
|
private val executor: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor()
|
||||||
|
|
||||||
private implicit val ec: ExecutionContext = scala.concurrent.ExecutionContext.global
|
private implicit val ec: ExecutionContext = scala.concurrent.ExecutionContext.global
|
||||||
private implicit val cs: ContextShift[IO] = IO.contextShift(ec)
|
private implicit val cs: ContextShift[IO] = IO.contextShift(ec)
|
||||||
private implicit val timer: Timer[IO] = IO.timer(ec)
|
private implicit val timer: Timer[IO] = IO.timer(ec)
|
||||||
@ -93,6 +98,11 @@ object Boot extends App {
|
|||||||
repositories.userRepository
|
repositories.userRepository
|
||||||
)
|
)
|
||||||
_ <- APIMetrics.initialize(vinyldnsConfig.apiMetricSettings)
|
_ <- APIMetrics.initialize(vinyldnsConfig.apiMetricSettings)
|
||||||
|
// Schedule the task to be executed every 5 seconds
|
||||||
|
_ <- IO(executor.scheduleAtFixedRate(() => {
|
||||||
|
val zoneChanges = zoneSyncScheduleHandler.zoneSyncScheduler(repositories.zoneRepository).unsafeRunSync()
|
||||||
|
zoneChanges.foreach(zone => messageQueue.send(zone).unsafeRunSync())
|
||||||
|
}, 0, 5, TimeUnit.SECONDS))
|
||||||
_ <- CommandHandler.run(
|
_ <- CommandHandler.run(
|
||||||
messageQueue,
|
messageQueue,
|
||||||
msgsPerPoll,
|
msgsPerPoll,
|
||||||
|
@ -61,6 +61,14 @@ object ZoneChangeGenerator {
|
|||||||
ZoneChangeStatus.Pending
|
ZoneChangeStatus.Pending
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def forSyncs(zone: Zone): ZoneChange =
|
||||||
|
ZoneChange(
|
||||||
|
zone.copy(updated = Some(Instant.now.truncatedTo(ChronoUnit.MILLIS)), status = ZoneStatus.Syncing),
|
||||||
|
zone.scheduleRequestor.get,
|
||||||
|
ZoneChangeType.Sync,
|
||||||
|
ZoneChangeStatus.Pending
|
||||||
|
)
|
||||||
|
|
||||||
def forDelete(zone: Zone, authPrincipal: AuthPrincipal): ZoneChange =
|
def forDelete(zone: Zone, authPrincipal: AuthPrincipal): ZoneChange =
|
||||||
ZoneChange(
|
ZoneChange(
|
||||||
zone.copy(updated = Some(Instant.now.truncatedTo(ChronoUnit.MILLIS)), status = ZoneStatus.Deleted),
|
zone.copy(updated = Some(Instant.now.truncatedTo(ChronoUnit.MILLIS)), status = ZoneStatus.Deleted),
|
||||||
|
@ -44,6 +44,7 @@ case class ZoneInfo(
|
|||||||
adminGroupName: String,
|
adminGroupName: String,
|
||||||
latestSync: Option[Instant],
|
latestSync: Option[Instant],
|
||||||
backendId: Option[String],
|
backendId: Option[String],
|
||||||
|
recurrenceSchedule: Option[String],
|
||||||
accessLevel: AccessLevel
|
accessLevel: AccessLevel
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -70,6 +71,7 @@ object ZoneInfo {
|
|||||||
adminGroupName = groupName,
|
adminGroupName = groupName,
|
||||||
latestSync = zone.latestSync,
|
latestSync = zone.latestSync,
|
||||||
backendId = zone.backendId,
|
backendId = zone.backendId,
|
||||||
|
recurrenceSchedule = zone.recurrenceSchedule,
|
||||||
accessLevel = accessLevel
|
accessLevel = accessLevel
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -90,6 +92,7 @@ case class ZoneSummaryInfo(
|
|||||||
adminGroupName: String,
|
adminGroupName: String,
|
||||||
latestSync: Option[Instant],
|
latestSync: Option[Instant],
|
||||||
backendId: Option[String],
|
backendId: Option[String],
|
||||||
|
recurrenceSchedule: Option[String],
|
||||||
accessLevel: AccessLevel
|
accessLevel: AccessLevel
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -111,6 +114,7 @@ object ZoneSummaryInfo {
|
|||||||
adminGroupName = groupName,
|
adminGroupName = groupName,
|
||||||
latestSync = zone.latestSync,
|
latestSync = zone.latestSync,
|
||||||
zone.backendId,
|
zone.backendId,
|
||||||
|
recurrenceSchedule = zone.recurrenceSchedule,
|
||||||
accessLevel = accessLevel
|
accessLevel = accessLevel
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,12 @@ import vinyldns.core.domain.zone._
|
|||||||
import vinyldns.core.queue.MessageQueue
|
import vinyldns.core.queue.MessageQueue
|
||||||
import vinyldns.core.domain.DomainHelpers.ensureTrailingDot
|
import vinyldns.core.domain.DomainHelpers.ensureTrailingDot
|
||||||
import vinyldns.core.domain.backend.BackendResolver
|
import vinyldns.core.domain.backend.BackendResolver
|
||||||
|
import com.cronutils.model.CronType
|
||||||
|
import com.cronutils.model.definition.{CronDefinition, CronDefinitionBuilder}
|
||||||
|
import com.cronutils.model.time.ExecutionTime
|
||||||
|
import com.cronutils.parser.CronParser
|
||||||
|
import java.time.{Instant, ZoneId}
|
||||||
|
import java.time.temporal.ChronoUnit
|
||||||
|
|
||||||
object ZoneService {
|
object ZoneService {
|
||||||
def apply(
|
def apply(
|
||||||
@ -101,7 +107,8 @@ class ZoneService(
|
|||||||
_ <- adminGroupExists(updateZoneInput.adminGroupId)
|
_ <- adminGroupExists(updateZoneInput.adminGroupId)
|
||||||
// if admin group changes, this confirms user has access to new group
|
// if admin group changes, this confirms user has access to new group
|
||||||
_ <- canChangeZone(auth, updateZoneInput.name, updateZoneInput.adminGroupId).toResult
|
_ <- canChangeZone(auth, updateZoneInput.name, updateZoneInput.adminGroupId).toResult
|
||||||
zoneWithUpdates = Zone(updateZoneInput, existingZone)
|
updatedZoneInput = if(updateZoneInput.recurrenceSchedule.isDefined) updateZoneInput.copy(scheduleRequestor = Some(auth.signedInUser.userName)) else updateZoneInput
|
||||||
|
zoneWithUpdates = Zone(updatedZoneInput, existingZone)
|
||||||
_ <- validateZoneConnectionIfChanged(zoneWithUpdates, existingZone)
|
_ <- validateZoneConnectionIfChanged(zoneWithUpdates, existingZone)
|
||||||
updateZoneChange <- ZoneChangeGenerator
|
updateZoneChange <- ZoneChangeGenerator
|
||||||
.forUpdate(zoneWithUpdates, existingZone, auth, crypto)
|
.forUpdate(zoneWithUpdates, existingZone, auth, crypto)
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Comcast Cable Communications Management, LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package vinyldns.api.domain.zone
|
||||||
|
|
||||||
|
import cats.effect.IO
|
||||||
|
import com.cronutils.model.CronType
|
||||||
|
import com.cronutils.model.definition.{CronDefinition, CronDefinitionBuilder}
|
||||||
|
import com.cronutils.model.time.ExecutionTime
|
||||||
|
import com.cronutils.parser.CronParser
|
||||||
|
import vinyldns.core.domain.zone.{Zone, ZoneChange, ZoneRepository}
|
||||||
|
import java.time.{Instant, ZoneId}
|
||||||
|
import java.time.temporal.ChronoUnit
|
||||||
|
|
||||||
|
object zoneSyncScheduleHandler {
|
||||||
|
|
||||||
|
// Define the function you want to repeat
|
||||||
|
def zoneSyncScheduler(zoneRepository: ZoneRepository): IO[Set[ZoneChange]] = {
|
||||||
|
for {
|
||||||
|
zones <- zoneRepository.getAllZonesWithSyncSchedule
|
||||||
|
zoneScheduleIds = getZoneWithSchedule(zones.toList)
|
||||||
|
zoneChanges <- getZoneChanges(zoneRepository, zoneScheduleIds)
|
||||||
|
} yield zoneChanges
|
||||||
|
}
|
||||||
|
|
||||||
|
def getZoneChanges(zoneRepository: ZoneRepository, zoneScheduleIds: List[String]): IO[Set[ZoneChange]] = {
|
||||||
|
if(zoneScheduleIds.nonEmpty) {
|
||||||
|
for{
|
||||||
|
getZones <- zoneRepository.getZones(zoneScheduleIds.toSet)
|
||||||
|
syncZoneChange = getZones.map(zone => ZoneChangeGenerator.forSyncs(zone))
|
||||||
|
} yield syncZoneChange
|
||||||
|
} else {
|
||||||
|
IO(Set.empty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def getZoneWithSchedule(zone: List[Zone]): List[String] = {
|
||||||
|
var zonesWithSchedule: List[String] = List.empty
|
||||||
|
for(z <- zone) {
|
||||||
|
if (z.recurrenceSchedule.isDefined) {
|
||||||
|
val now = Instant.now()
|
||||||
|
val cronDefinition: CronDefinition = CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ)
|
||||||
|
val parser: CronParser = new CronParser(cronDefinition)
|
||||||
|
val executionTime: ExecutionTime = ExecutionTime.forCron(parser.parse(z.recurrenceSchedule.get))
|
||||||
|
val nextExecution = executionTime.nextExecution(now.atZone(ZoneId.systemDefault())).get()
|
||||||
|
val diff = ChronoUnit.SECONDS.between(now, nextExecution)
|
||||||
|
if (diff <= 5) {
|
||||||
|
zonesWithSchedule = zonesWithSchedule :+ z.id
|
||||||
|
} else {
|
||||||
|
List.empty
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
List.empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zonesWithSchedule
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -111,7 +111,9 @@ trait DnsJsonProtocol extends JsonValidation {
|
|||||||
(js \ "shared").default[Boolean](false),
|
(js \ "shared").default[Boolean](false),
|
||||||
(js \ "acl").default[ZoneACL](ZoneACL()),
|
(js \ "acl").default[ZoneACL](ZoneACL()),
|
||||||
(js \ "adminGroupId").required[String]("Missing Zone.adminGroupId"),
|
(js \ "adminGroupId").required[String]("Missing Zone.adminGroupId"),
|
||||||
(js \ "backendId").optional[String]
|
(js \ "backendId").optional[String],
|
||||||
|
(js \ "recurrenceSchedule").optional[String],
|
||||||
|
(js \ "scheduleRequestor").optional[String],
|
||||||
).mapN(CreateZoneInput.apply)
|
).mapN(CreateZoneInput.apply)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,7 +130,9 @@ trait DnsJsonProtocol extends JsonValidation {
|
|||||||
(js \ "shared").default[Boolean](false),
|
(js \ "shared").default[Boolean](false),
|
||||||
(js \ "acl").default[ZoneACL](ZoneACL()),
|
(js \ "acl").default[ZoneACL](ZoneACL()),
|
||||||
(js \ "adminGroupId").required[String]("Missing Zone.adminGroupId"),
|
(js \ "adminGroupId").required[String]("Missing Zone.adminGroupId"),
|
||||||
(js \ "backendId").optional[String]
|
(js \ "recurrenceSchedule").optional[String],
|
||||||
|
(js \ "scheduleRequestor").optional[String],
|
||||||
|
(js \ "backendId").optional[String],
|
||||||
).mapN(UpdateZoneInput.apply)
|
).mapN(UpdateZoneInput.apply)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +49,8 @@ message Zone {
|
|||||||
optional int64 latestSync = 13;
|
optional int64 latestSync = 13;
|
||||||
optional bool isTest = 14 [default = false];
|
optional bool isTest = 14 [default = false];
|
||||||
optional string backendId = 15;
|
optional string backendId = 15;
|
||||||
|
optional string recurrenceSchedule = 16;
|
||||||
|
optional string scheduleRequestor = 17;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AData {
|
message AData {
|
||||||
|
@ -47,6 +47,8 @@ final case class Zone(
|
|||||||
shared: Boolean = false,
|
shared: Boolean = false,
|
||||||
acl: ZoneACL = ZoneACL(),
|
acl: ZoneACL = ZoneACL(),
|
||||||
adminGroupId: String = "system",
|
adminGroupId: String = "system",
|
||||||
|
recurrenceSchedule: Option[String] = None,
|
||||||
|
scheduleRequestor: Option[String] = None,
|
||||||
latestSync: Option[Instant] = None,
|
latestSync: Option[Instant] = None,
|
||||||
isTest: Boolean = false,
|
isTest: Boolean = false,
|
||||||
backendId: Option[String] = None
|
backendId: Option[String] = None
|
||||||
@ -75,6 +77,8 @@ final case class Zone(
|
|||||||
sb.append("reverse=\"").append(isReverse).append("\"; ")
|
sb.append("reverse=\"").append(isReverse).append("\"; ")
|
||||||
sb.append("isTest=\"").append(isTest).append("\"; ")
|
sb.append("isTest=\"").append(isTest).append("\"; ")
|
||||||
sb.append("created=\"").append(created).append("\"; ")
|
sb.append("created=\"").append(created).append("\"; ")
|
||||||
|
recurrenceSchedule.map(sb.append("recurrenceSchedule=\"").append(_).append("\"; "))
|
||||||
|
scheduleRequestor.map(sb.append("scheduleRequestor=\"").append(_).append("\"; "))
|
||||||
updated.map(sb.append("updated=\"").append(_).append("\"; "))
|
updated.map(sb.append("updated=\"").append(_).append("\"; "))
|
||||||
latestSync.map(sb.append("latestSync=\"").append(_).append("\"; "))
|
latestSync.map(sb.append("latestSync=\"").append(_).append("\"; "))
|
||||||
sb.append("]")
|
sb.append("]")
|
||||||
@ -95,7 +99,9 @@ object Zone {
|
|||||||
acl = acl,
|
acl = acl,
|
||||||
adminGroupId = adminGroupId,
|
adminGroupId = adminGroupId,
|
||||||
backendId = backendId,
|
backendId = backendId,
|
||||||
isTest = isTest
|
isTest = isTest,
|
||||||
|
recurrenceSchedule = recurrenceSchedule,
|
||||||
|
scheduleRequestor = scheduleRequestor
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +116,9 @@ object Zone {
|
|||||||
shared = shared,
|
shared = shared,
|
||||||
acl = acl,
|
acl = acl,
|
||||||
adminGroupId = adminGroupId,
|
adminGroupId = adminGroupId,
|
||||||
backendId = backendId
|
backendId = backendId,
|
||||||
|
recurrenceSchedule = recurrenceSchedule,
|
||||||
|
scheduleRequestor = scheduleRequestor
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,7 +131,9 @@ final case class CreateZoneInput(
|
|||||||
shared: Boolean = false,
|
shared: Boolean = false,
|
||||||
acl: ZoneACL = ZoneACL(),
|
acl: ZoneACL = ZoneACL(),
|
||||||
adminGroupId: String,
|
adminGroupId: String,
|
||||||
backendId: Option[String] = None
|
backendId: Option[String] = None,
|
||||||
|
recurrenceSchedule: Option[String] = None,
|
||||||
|
scheduleRequestor: Option[String] = None
|
||||||
)
|
)
|
||||||
|
|
||||||
final case class UpdateZoneInput(
|
final case class UpdateZoneInput(
|
||||||
@ -135,6 +145,8 @@ final case class UpdateZoneInput(
|
|||||||
shared: Boolean = false,
|
shared: Boolean = false,
|
||||||
acl: ZoneACL = ZoneACL(),
|
acl: ZoneACL = ZoneACL(),
|
||||||
adminGroupId: String,
|
adminGroupId: String,
|
||||||
|
recurrenceSchedule: Option[String] = None,
|
||||||
|
scheduleRequestor: Option[String] = None,
|
||||||
backendId: Option[String] = None
|
backendId: Option[String] = None
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -29,6 +29,8 @@ trait ZoneRepository extends Repository {
|
|||||||
|
|
||||||
def getZones(zoneId: Set[String]): IO[Set[Zone]]
|
def getZones(zoneId: Set[String]): IO[Set[Zone]]
|
||||||
|
|
||||||
|
def getAllZonesWithSyncSchedule: IO[Set[Zone]]
|
||||||
|
|
||||||
def getZoneByName(zoneName: String): IO[Option[Zone]]
|
def getZoneByName(zoneName: String): IO[Option[Zone]]
|
||||||
|
|
||||||
def getZonesByNames(zoneNames: Set[String]): IO[Set[Zone]]
|
def getZonesByNames(zoneNames: Set[String]): IO[Set[Zone]]
|
||||||
|
@ -125,7 +125,9 @@ trait ProtobufConversions {
|
|||||||
adminGroupId = zn.getAdminGroupId,
|
adminGroupId = zn.getAdminGroupId,
|
||||||
latestSync = if (zn.hasLatestSync) Some(Instant.ofEpochMilli(zn.getLatestSync)) else None,
|
latestSync = if (zn.hasLatestSync) Some(Instant.ofEpochMilli(zn.getLatestSync)) else None,
|
||||||
isTest = zn.getIsTest,
|
isTest = zn.getIsTest,
|
||||||
backendId = if (zn.hasBackendId) Some(zn.getBackendId) else None
|
backendId = if (zn.hasBackendId) Some(zn.getBackendId) else None,
|
||||||
|
recurrenceSchedule = if (zn.hasRecurrenceSchedule) Some(zn.getRecurrenceSchedule) else None,
|
||||||
|
scheduleRequestor = if (zn.hasScheduleRequestor) Some(zn.getScheduleRequestor) else None
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,6 +403,8 @@ trait ProtobufConversions {
|
|||||||
zone.transferConnection.foreach(cn => builder.setTransferConnection(toPB(cn)))
|
zone.transferConnection.foreach(cn => builder.setTransferConnection(toPB(cn)))
|
||||||
zone.latestSync.foreach(dt => builder.setLatestSync(dt.toEpochMilli))
|
zone.latestSync.foreach(dt => builder.setLatestSync(dt.toEpochMilli))
|
||||||
zone.backendId.foreach(bid => builder.setBackendId(bid))
|
zone.backendId.foreach(bid => builder.setBackendId(bid))
|
||||||
|
zone.recurrenceSchedule.foreach(rs => builder.setRecurrenceSchedule(rs))
|
||||||
|
zone.scheduleRequestor.foreach(rs => builder.setScheduleRequestor(rs))
|
||||||
builder.build()
|
builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
CREATE SCHEMA IF NOT EXISTS ${dbName};
|
||||||
|
|
||||||
|
USE ${dbName};
|
||||||
|
|
||||||
|
ALTER TABLE zone ADD zone_sync_schedule VARCHAR(256) NULL;
|
@ -48,10 +48,11 @@ class MySqlZoneRepository extends ZoneRepository with ProtobufConversions with M
|
|||||||
*/
|
*/
|
||||||
private final val PUT_ZONE =
|
private final val PUT_ZONE =
|
||||||
sql"""
|
sql"""
|
||||||
|INSERT INTO zone(id, name, admin_group_id, data)
|
|INSERT INTO zone(id, name, admin_group_id, zone_sync_schedule, data)
|
||||||
| VALUES ({id}, {name}, {adminGroupId}, {data}) ON DUPLICATE KEY
|
| VALUES ({id}, {name}, {adminGroupId}, {recurrenceSchedule}, {data}) ON DUPLICATE KEY
|
||||||
| UPDATE name=VALUES(name),
|
| UPDATE name=VALUES(name),
|
||||||
| admin_group_id=VALUES(admin_group_id),
|
| admin_group_id=VALUES(admin_group_id),
|
||||||
|
| zone_sync_schedule=VALUES(zone_sync_schedule),
|
||||||
| data=VALUES(data);
|
| data=VALUES(data);
|
||||||
""".stripMargin
|
""".stripMargin
|
||||||
|
|
||||||
@ -116,6 +117,13 @@ class MySqlZoneRepository extends ZoneRepository with ProtobufConversions with M
|
|||||||
| FROM zone
|
| FROM zone
|
||||||
""".stripMargin
|
""".stripMargin
|
||||||
|
|
||||||
|
private final val BASE_GET_ALL_ZONES_SQL =
|
||||||
|
"""
|
||||||
|
|SELECT data
|
||||||
|
| FROM zone
|
||||||
|
| WHERE zone_sync_schedule IS NOT NULL
|
||||||
|
""".stripMargin
|
||||||
|
|
||||||
private final val GET_ZONE_ACCESS_BY_ADMIN_GROUP_ID =
|
private final val GET_ZONE_ACCESS_BY_ADMIN_GROUP_ID =
|
||||||
sql"""
|
sql"""
|
||||||
|SELECT zone_id
|
|SELECT zone_id
|
||||||
@ -207,6 +215,19 @@ class MySqlZoneRepository extends ZoneRepository with ProtobufConversions with M
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def getAllZonesWithSyncSchedule: IO[Set[Zone]] =
|
||||||
|
monitor("repo.ZoneJDBC.getAllZonesWithSyncSchedule") {
|
||||||
|
IO {
|
||||||
|
DB.readOnly { implicit s =>
|
||||||
|
SQL(
|
||||||
|
BASE_GET_ALL_ZONES_SQL
|
||||||
|
).map(extractZone(1))
|
||||||
|
.list()
|
||||||
|
.apply()
|
||||||
|
}.toSet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def getZonesByFilters(zoneNames: Set[String]): IO[Set[Zone]] =
|
def getZonesByFilters(zoneNames: Set[String]): IO[Set[Zone]] =
|
||||||
if (zoneNames.isEmpty) {
|
if (zoneNames.isEmpty) {
|
||||||
IO.pure(Set())
|
IO.pure(Set())
|
||||||
@ -414,6 +435,7 @@ class MySqlZoneRepository extends ZoneRepository with ProtobufConversions with M
|
|||||||
'id -> zone.id,
|
'id -> zone.id,
|
||||||
'name -> zone.name,
|
'name -> zone.name,
|
||||||
'adminGroupId -> zone.adminGroupId,
|
'adminGroupId -> zone.adminGroupId,
|
||||||
|
'recurrenceSchedule -> zone.recurrenceSchedule,
|
||||||
'data -> toPB(zone).toByteArray
|
'data -> toPB(zone).toByteArray
|
||||||
): _*
|
): _*
|
||||||
)
|
)
|
||||||
@ -475,6 +497,7 @@ class MySqlZoneRepository extends ZoneRepository with ProtobufConversions with M
|
|||||||
def saveTx(zone: Zone): IO[Either[DuplicateZoneError, Zone]] =
|
def saveTx(zone: Zone): IO[Either[DuplicateZoneError, Zone]] =
|
||||||
monitor("repo.ZoneJDBC.save") {
|
monitor("repo.ZoneJDBC.save") {
|
||||||
IO {
|
IO {
|
||||||
|
println(zone.recurrenceSchedule)
|
||||||
DB.localTx { implicit s =>
|
DB.localTx { implicit s =>
|
||||||
getZoneByNameInSession(zone.name) match {
|
getZoneByNameInSession(zone.name) match {
|
||||||
case Some(foundZone) if zone.id != foundZone.id => DuplicateZoneError(zone.name).asLeft
|
case Some(foundZone) if zone.id != foundZone.id => DuplicateZoneError(zone.name).asLeft
|
||||||
|
@ -35,10 +35,12 @@ module.exports = function(grunt) {
|
|||||||
{expand: true, flatten: true, src: ['node_modules/jquery/dist/jquery.min.js'], dest: 'public/js'},
|
{expand: true, flatten: true, src: ['node_modules/jquery/dist/jquery.min.js'], dest: 'public/js'},
|
||||||
{expand: true, flatten: true, src: ['node_modules/moment/min/moment.min.js'], dest: 'public/js'},
|
{expand: true, flatten: true, src: ['node_modules/moment/min/moment.min.js'], dest: 'public/js'},
|
||||||
{expand: true, flatten: true, src: ['node_modules/jquery-ui-dist/jquery-ui.js'], dest: 'public/js'},
|
{expand: true, flatten: true, src: ['node_modules/jquery-ui-dist/jquery-ui.js'], dest: 'public/js'},
|
||||||
|
{expand: true, flatten: true, src: ['node_modules/angular-cron-jobs/dist/angular-cron-jobs.min.js'], dest: 'public/js'},
|
||||||
|
|
||||||
{expand: true, flatten: true, src: ['node_modules/bootstrap/dist/css/bootstrap.min.css'], dest: 'public/css'},
|
{expand: true, flatten: true, src: ['node_modules/bootstrap/dist/css/bootstrap.min.css'], dest: 'public/css'},
|
||||||
{expand: true, flatten: true, src: ['node_modules/font-awesome/css/font-awesome.min.css'], dest: 'public/css'},
|
{expand: true, flatten: true, src: ['node_modules/font-awesome/css/font-awesome.min.css'], dest: 'public/css'},
|
||||||
{expand: true, flatten: true, src: ['node_modules/jquery-ui-dist/jquery-ui.css'], dest: 'public/css'},
|
{expand: true, flatten: true, src: ['node_modules/jquery-ui-dist/jquery-ui.css'], dest: 'public/css'},
|
||||||
|
{expand: true, flatten: true, src: ['node_modules/angular-cron-jobs/dist/angular-cron-jobs.min.css'], dest: 'public/css'},
|
||||||
|
|
||||||
// We're picking just the resources we need from the gentelella UI framework and temporarily storing them in mapped/ui/
|
// We're picking just the resources we need from the gentelella UI framework and temporarily storing them in mapped/ui/
|
||||||
{expand: true, flatten: true, cwd: 'node_modules/gentelella', dest: 'mapped/ui', src: '**/jquery.{smartWizard,dataTables.min,mousewheel.min}.js'},
|
{expand: true, flatten: true, cwd: 'node_modules/gentelella', dest: 'mapped/ui', src: '**/jquery.{smartWizard,dataTables.min,mousewheel.min}.js'},
|
||||||
|
@ -71,7 +71,8 @@ class FrontendController @Inject() (
|
|||||||
}
|
}
|
||||||
|
|
||||||
def viewZone(zoneId: String): Action[AnyContent] = userAction.async { implicit request =>
|
def viewZone(zoneId: String): Action[AnyContent] = userAction.async { implicit request =>
|
||||||
Future(Ok(views.html.zones.zoneDetail(request.user.userName, zoneId)))
|
val canReview = request.user.isSuper || request.user.isSupport
|
||||||
|
Future(Ok(views.html.zones.zoneDetail(request.user.userName, canReview, zoneId)))
|
||||||
}
|
}
|
||||||
|
|
||||||
def viewRecordSets(): Action[AnyContent] = userAction.async { implicit request =>
|
def viewRecordSets(): Action[AnyContent] = userAction.async { implicit request =>
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
<link rel="stylesheet" type="text/css" href="/public/css/ui.css" />
|
<link rel="stylesheet" type="text/css" href="/public/css/ui.css" />
|
||||||
<link rel="stylesheet" type="text/css" id="custom" href="/public/css/theme-overrides.css"/>
|
<link rel="stylesheet" type="text/css" id="custom" href="/public/css/theme-overrides.css"/>
|
||||||
<link rel="stylesheet" type="text/css" id="custom" href="/public/css/vinyldns.css"/>
|
<link rel="stylesheet" type="text/css" id="custom" href="/public/css/vinyldns.css"/>
|
||||||
<link rel="stylesheet" type="text/css" href="/public/css/jquery-ui.css">
|
<link rel="stylesheet" type="text/css" href="/public/css/angular-cron-jobs.min.css">
|
||||||
<!-- EOF CSS INCLUDE -->
|
<!-- EOF CSS INCLUDE -->
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@ -158,6 +158,7 @@
|
|||||||
<script src="/public/js/vinyldns.js"></script>
|
<script src="/public/js/vinyldns.js"></script>
|
||||||
<script src="/public/app.js"></script>
|
<script src="/public/app.js"></script>
|
||||||
<script src="/public/js/custom.js"></script>
|
<script src="/public/js/custom.js"></script>
|
||||||
|
<script src="/public/js/angular-cron-jobs.min.js"></script>
|
||||||
|
|
||||||
@pagePlugins
|
@pagePlugins
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@(rootAccountName: String, zoneId: String)(implicit request: play.api.mvc.Request[Any], customLinks: models.CustomLinks, meta: models.Meta)
|
@(rootAccountName: String, rootAccountCanReview: Boolean, zoneId: String)(implicit request: play.api.mvc.Request[Any], customLinks: models.CustomLinks, meta: models.Meta)
|
||||||
@import zoneTabs._
|
@import zoneTabs._
|
||||||
|
|
||||||
@content = {
|
@content = {
|
||||||
@ -50,7 +50,7 @@
|
|||||||
@manageRecords(request, meta)
|
@manageRecords(request, meta)
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane" id="tab2" ng-controller="ManageZonesController">
|
<div class="tab-pane" id="tab2" ng-controller="ManageZonesController">
|
||||||
@manageZone(request, meta)
|
@manageZone(rootAccountCanReview, request, meta)
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane" id="tab3">
|
<div class="tab-pane" id="tab3">
|
||||||
@changeHistory(request)
|
@changeHistory(request)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@(implicit request: play.api.mvc.Request[Any], meta: models.Meta)
|
@(implicit rootAccountCanReview: Boolean, request: play.api.mvc.Request[Any], meta: models.Meta)
|
||||||
|
|
||||||
<div class="alert-wrapper">
|
<div class="alert-wrapper">
|
||||||
<div ng-repeat="alert in alerts">
|
<div ng-repeat="alert in alerts">
|
||||||
@ -233,6 +233,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-15">
|
||||||
|
<div class="block">
|
||||||
|
<div class="col-md-9">
|
||||||
|
@if(rootAccountCanReview) {
|
||||||
|
<h4>Schedule Zone Sync</h4>
|
||||||
|
<cron-selection class="col-md-12" ng-model="updateZoneInfo.recurrenceSchedule" config="myZoneSyncScheduleConfig"></cron-selection>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ module.exports = function(config) {
|
|||||||
'js/angular.min.js',
|
'js/angular.min.js',
|
||||||
'js/moment.min.js',
|
'js/moment.min.js',
|
||||||
'js/ui.js',
|
'js/ui.js',
|
||||||
|
'js/angular-cron-jobs.min.js',
|
||||||
'test_frameworks/*.js',
|
'test_frameworks/*.js',
|
||||||
'js/vinyldns.js',
|
'js/vinyldns.js',
|
||||||
'lib/services/**/*.spec.js',
|
'lib/services/**/*.spec.js',
|
||||||
|
@ -32,7 +32,8 @@
|
|||||||
"karma-phantomjs-launcher": "^1.0.0",
|
"karma-phantomjs-launcher": "^1.0.0",
|
||||||
"karma-spec-reporter": "^0.0.26",
|
"karma-spec-reporter": "^0.0.26",
|
||||||
"malihu-custom-scrollbar-plugin": "^3.1.5",
|
"malihu-custom-scrollbar-plugin": "^3.1.5",
|
||||||
"phantomjs-prebuilt": "^2.1.16"
|
"phantomjs-prebuilt": "^2.1.16",
|
||||||
|
"angular-cron-jobs": "^3.2.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "unit"
|
"test": "unit"
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
angular.module('controller.manageZones', [])
|
angular.module('controller.manageZones', ['angular-cron-jobs'])
|
||||||
.controller('ManageZonesController', function ($scope, $timeout, $log, recordsService, zonesService, groupsService,
|
.controller('ManageZonesController', function ($scope, $timeout, $log, recordsService, zonesService, groupsService,
|
||||||
profileService, utilityService, pagingService) {
|
profileService, utilityService, pagingService) {
|
||||||
|
|
||||||
@ -94,6 +94,15 @@ angular.module('controller.manageZones', [])
|
|||||||
$("#delete_zone_connection_modal").modal("show");
|
$("#delete_zone_connection_modal").modal("show");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.myZoneSyncScheduleConfig = {
|
||||||
|
allowMultiple: true,
|
||||||
|
quartz: true,
|
||||||
|
options: {
|
||||||
|
allowMinute : false,
|
||||||
|
allowHour : false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$scope.submitDeleteZone = function() {
|
$scope.submitDeleteZone = function() {
|
||||||
zonesService.delZone($scope.zoneInfo.id)
|
zonesService.delZone($scope.zoneInfo.id)
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
@ -278,6 +287,8 @@ angular.module('controller.manageZones', [])
|
|||||||
$scope.updateZoneInfo = angular.copy($scope.zoneInfo);
|
$scope.updateZoneInfo = angular.copy($scope.zoneInfo);
|
||||||
$scope.updateZoneInfo.hiddenKey = '';
|
$scope.updateZoneInfo.hiddenKey = '';
|
||||||
$scope.updateZoneInfo.hiddenTransferKey = '';
|
$scope.updateZoneInfo.hiddenTransferKey = '';
|
||||||
|
$log.log('recordsService::getZone-success schedule: ', $scope.updateZoneInfo.recurrenceSchedule);
|
||||||
|
$log.log('recordsService::getZone-success: ', $scope.zoneInfo);
|
||||||
$scope.currentManageZoneState = $scope.manageZoneState.UPDATE;
|
$scope.currentManageZoneState = $scope.manageZoneState.UPDATE;
|
||||||
$scope.refreshAclRuleDisplay();
|
$scope.refreshAclRuleDisplay();
|
||||||
$scope.refreshZoneChange();
|
$scope.refreshZoneChange();
|
||||||
|
@ -52,7 +52,8 @@ object Dependencies {
|
|||||||
"com.sun.mail" % "javax.mail" % "1.6.2",
|
"com.sun.mail" % "javax.mail" % "1.6.2",
|
||||||
"javax.mail" % "javax.mail-api" % "1.6.2",
|
"javax.mail" % "javax.mail-api" % "1.6.2",
|
||||||
"com.amazonaws" % "aws-java-sdk-sns" % awsV withSources(),
|
"com.amazonaws" % "aws-java-sdk-sns" % awsV withSources(),
|
||||||
"co.elastic.logging" % "logback-ecs-encoder" % "1.3.2"
|
"co.elastic.logging" % "logback-ecs-encoder" % "1.3.2",
|
||||||
|
"com.cronutils" % "cron-utils" % "9.1.6"
|
||||||
)
|
)
|
||||||
|
|
||||||
lazy val coreDependencies = Seq(
|
lazy val coreDependencies = Seq(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user