mirror of
				https://github.com/GayPizzaSpecifications/foundation.git
				synced 2025-11-04 03:39:37 +00:00 
			
		
		
		
	Initial work on scheduled backups.
This commit is contained in:
		@ -2,4 +2,5 @@ dependencies {
 | 
			
		||||
  // TODO: might be able to ship all dependencies in core? are we duplicating classes in JARs?
 | 
			
		||||
 | 
			
		||||
  implementation("software.amazon.awssdk:s3:2.17.102")
 | 
			
		||||
  implementation("org.quartz-scheduler:quartz:2.3.2")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@ import cloud.kubelet.foundation.core.features.stats.StatsFeature
 | 
			
		||||
import cloud.kubelet.foundation.core.features.update.UpdateFeature
 | 
			
		||||
import cloud.kubelet.foundation.core.features.world.WorldFeature
 | 
			
		||||
import cloud.kubelet.foundation.core.features.persist.PersistenceFeature
 | 
			
		||||
import cloud.kubelet.foundation.core.features.scheduler.SchedulerFeature
 | 
			
		||||
import org.koin.dsl.module
 | 
			
		||||
import java.nio.file.Path
 | 
			
		||||
 | 
			
		||||
@ -38,6 +39,7 @@ class FoundationCorePlugin : FoundationPlugin() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun createFeatures() = listOf(
 | 
			
		||||
    SchedulerFeature(),
 | 
			
		||||
    PersistenceFeature(),
 | 
			
		||||
    BackupFeature(),
 | 
			
		||||
    DevFeature(),
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,7 @@
 | 
			
		||||
package cloud.kubelet.foundation.core.abstraction
 | 
			
		||||
 | 
			
		||||
interface CoreFeature {
 | 
			
		||||
  fun enable()
 | 
			
		||||
  fun disable()
 | 
			
		||||
  fun module() = org.koin.dsl.module {}
 | 
			
		||||
}
 | 
			
		||||
@ -7,13 +7,15 @@ import org.bukkit.event.Listener
 | 
			
		||||
import org.koin.core.component.KoinComponent
 | 
			
		||||
import org.koin.core.component.inject
 | 
			
		||||
import org.koin.dsl.module
 | 
			
		||||
import org.quartz.Scheduler
 | 
			
		||||
 | 
			
		||||
abstract class Feature : KoinComponent, Listener {
 | 
			
		||||
abstract class Feature : CoreFeature, KoinComponent, Listener {
 | 
			
		||||
  private val plugin by inject<FoundationCorePlugin>()
 | 
			
		||||
  protected val scheduler by inject<Scheduler>()
 | 
			
		||||
 | 
			
		||||
  open fun enable() {}
 | 
			
		||||
  open fun disable() {}
 | 
			
		||||
  open fun module() = module {}
 | 
			
		||||
  override fun enable() {}
 | 
			
		||||
  override fun disable() {}
 | 
			
		||||
  override fun module() = module {}
 | 
			
		||||
 | 
			
		||||
  protected fun registerCommandExecutor(name: String, executor: CommandExecutor) {
 | 
			
		||||
    registerCommandExecutor(listOf(name), executor)
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@ import org.koin.dsl.module
 | 
			
		||||
abstract class FoundationPlugin : JavaPlugin() {
 | 
			
		||||
  private lateinit var pluginModule: Module
 | 
			
		||||
  private lateinit var pluginApplication: KoinApplication
 | 
			
		||||
  private lateinit var features: List<Feature>
 | 
			
		||||
  private lateinit var features: List<CoreFeature>
 | 
			
		||||
  private lateinit var module: Module
 | 
			
		||||
 | 
			
		||||
  override fun onEnable() {
 | 
			
		||||
@ -45,7 +45,10 @@ abstract class FoundationPlugin : JavaPlugin() {
 | 
			
		||||
      try {
 | 
			
		||||
        slF4JLogger.info("Enabling feature: ${it.javaClass.simpleName}")
 | 
			
		||||
        it.enable()
 | 
			
		||||
        server.pluginManager.registerEvents(it, this)
 | 
			
		||||
        // TODO: May replace this check with a method in the interface, CoreFeature would no-op.
 | 
			
		||||
        if (it is Feature) {
 | 
			
		||||
          server.pluginManager.registerEvents(it, this)
 | 
			
		||||
        }
 | 
			
		||||
      } catch (e: Exception) {
 | 
			
		||||
        slF4JLogger.error("Failed to enable feature: ${it.javaClass.simpleName}", e)
 | 
			
		||||
      }
 | 
			
		||||
@ -61,5 +64,5 @@ abstract class FoundationPlugin : JavaPlugin() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected open fun createModule() = module {}
 | 
			
		||||
  protected abstract fun createFeatures(): List<Feature>
 | 
			
		||||
  protected abstract fun createFeatures(): List<CoreFeature>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,9 +4,15 @@ import kotlinx.serialization.Serializable
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class BackupConfig(
 | 
			
		||||
  val schedule: ScheduleConfig = ScheduleConfig(),
 | 
			
		||||
  val s3: S3Config = S3Config(),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class ScheduleConfig(
 | 
			
		||||
  val cron: String = "",
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class S3Config(
 | 
			
		||||
  val accessKeyId: String = "",
 | 
			
		||||
 | 
			
		||||
@ -3,8 +3,9 @@ package cloud.kubelet.foundation.core.features.backup
 | 
			
		||||
import cloud.kubelet.foundation.core.FoundationCorePlugin
 | 
			
		||||
import cloud.kubelet.foundation.core.Util
 | 
			
		||||
import cloud.kubelet.foundation.core.abstraction.Feature
 | 
			
		||||
import cloud.kubelet.foundation.core.features.scheduler.cancel
 | 
			
		||||
import cloud.kubelet.foundation.core.features.scheduler.cron
 | 
			
		||||
import com.charleskorn.kaml.Yaml
 | 
			
		||||
import org.koin.core.KoinApplication
 | 
			
		||||
import org.koin.core.component.inject
 | 
			
		||||
import org.koin.dsl.module
 | 
			
		||||
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials
 | 
			
		||||
@ -18,6 +19,7 @@ class BackupFeature : Feature() {
 | 
			
		||||
  private val plugin by inject<FoundationCorePlugin>()
 | 
			
		||||
  private val s3Client by inject<S3Client>()
 | 
			
		||||
  private val config by inject<BackupConfig>()
 | 
			
		||||
  private lateinit var scheduleId: String
 | 
			
		||||
 | 
			
		||||
  override fun enable() {
 | 
			
		||||
    // Create backup directory.
 | 
			
		||||
@ -25,6 +27,20 @@ class BackupFeature : Feature() {
 | 
			
		||||
    backupPath.toFile().mkdir()
 | 
			
		||||
 | 
			
		||||
    registerCommandExecutor("fbackup", BackupCommand(plugin, backupPath, config, s3Client))
 | 
			
		||||
 | 
			
		||||
    if (config.schedule.cron.isNotEmpty()) {
 | 
			
		||||
      scheduleId = scheduler.cron("${config.schedule.cron} ?") {
 | 
			
		||||
        plugin.server.scheduler.runTask(plugin) { ->
 | 
			
		||||
          plugin.server.dispatchCommand(plugin.server.consoleSender, "fbackup")
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun disable() {
 | 
			
		||||
    if (::scheduleId.isInitialized) {
 | 
			
		||||
      scheduler.cancel(scheduleId)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun module() = module {
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,30 @@
 | 
			
		||||
package cloud.kubelet.foundation.core.features.scheduler
 | 
			
		||||
 | 
			
		||||
import org.quartz.CronScheduleBuilder.cronSchedule
 | 
			
		||||
import org.quartz.JobBuilder.newJob
 | 
			
		||||
import org.quartz.JobDataMap
 | 
			
		||||
import org.quartz.Scheduler
 | 
			
		||||
import org.quartz.TriggerBuilder.newTrigger
 | 
			
		||||
import org.quartz.TriggerKey.triggerKey
 | 
			
		||||
import java.util.UUID
 | 
			
		||||
 | 
			
		||||
fun Scheduler.cron(cronExpression: String, f: () -> Unit): String {
 | 
			
		||||
  val id = UUID.randomUUID().toString()
 | 
			
		||||
  val job = newJob(SchedulerRunner::class.java).apply {
 | 
			
		||||
    setJobData(JobDataMap().apply {
 | 
			
		||||
      set("function", f)
 | 
			
		||||
    })
 | 
			
		||||
  }.build()
 | 
			
		||||
 | 
			
		||||
  val trigger = newTrigger()
 | 
			
		||||
    .withIdentity(triggerKey(id))
 | 
			
		||||
    .withSchedule(cronSchedule(cronExpression))
 | 
			
		||||
    .build()
 | 
			
		||||
 | 
			
		||||
  scheduleJob(job, trigger)
 | 
			
		||||
  return id
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun Scheduler.cancel(id: String) {
 | 
			
		||||
  unscheduleJob(triggerKey(id))
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,22 @@
 | 
			
		||||
package cloud.kubelet.foundation.core.features.scheduler
 | 
			
		||||
 | 
			
		||||
import cloud.kubelet.foundation.core.abstraction.CoreFeature
 | 
			
		||||
import org.koin.dsl.module
 | 
			
		||||
import org.quartz.Scheduler
 | 
			
		||||
import org.quartz.impl.StdSchedulerFactory
 | 
			
		||||
 | 
			
		||||
class SchedulerFeature : CoreFeature {
 | 
			
		||||
  private val scheduler: Scheduler = StdSchedulerFactory.getDefaultScheduler()
 | 
			
		||||
 | 
			
		||||
  override fun enable() {
 | 
			
		||||
    scheduler.start()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun disable() {
 | 
			
		||||
    scheduler.shutdown(true)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun module() = module {
 | 
			
		||||
    single { scheduler }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,11 @@
 | 
			
		||||
package cloud.kubelet.foundation.core.features.scheduler
 | 
			
		||||
 | 
			
		||||
import org.quartz.Job
 | 
			
		||||
import org.quartz.JobExecutionContext
 | 
			
		||||
 | 
			
		||||
class SchedulerRunner : Job {
 | 
			
		||||
  override fun execute(context: JobExecutionContext) {
 | 
			
		||||
    val f = context.jobDetail.jobDataMap["function"] as () -> Unit
 | 
			
		||||
    f()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -1,3 +1,11 @@
 | 
			
		||||
# Configuration of backup scheduling, expressed by cron expressions.
 | 
			
		||||
schedule:
 | 
			
		||||
  # Cron expression to use for the backup schedule.
 | 
			
		||||
  # Examples:
 | 
			
		||||
  # "0 3 * * *"   -> every day at 3 AM
 | 
			
		||||
  # "0 3 * * SUN" -> every Sunday at 3 AM
 | 
			
		||||
  cron: ""
 | 
			
		||||
 | 
			
		||||
# Configuration of S3 service to upload back-ups to.
 | 
			
		||||
s3:
 | 
			
		||||
  # The access key ID from your S3-compliant storage provider.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								foundation-core/src/main/resources/quartz.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								foundation-core/src/main/resources/quartz.properties
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
org.quartz.scheduler.instanceName = Foundation
 | 
			
		||||
org.quartz.threadPool.threadCount = 2
 | 
			
		||||
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
 | 
			
		||||
		Reference in New Issue
	
	Block a user