Initial commit
This commit is contained in:
commit
63552dfc7b
109 changed files with 2699 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
build
|
||||
.gradle
|
59
build.gradle
Normal file
59
build.gradle
Normal file
|
@ -0,0 +1,59 @@
|
|||
plugins {
|
||||
id 'java'
|
||||
id 'idea'
|
||||
id 'application'
|
||||
id 'com.github.johnrengelman.shadow' version '5.1.0'
|
||||
id 'org.beryx.runtime' version '1.2.0'
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.3.40'
|
||||
}
|
||||
|
||||
group 'me.eater.hefbrug'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
|
||||
// Scripting dependencies
|
||||
implementation 'org.jetbrains.kotlin:kotlin-compiler-embeddable'
|
||||
implementation 'org.jetbrains.kotlin:kotlin-scripting-common'
|
||||
implementation 'org.jetbrains.kotlin:kotlin-scripting-jvm'
|
||||
implementation 'org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable'
|
||||
implementation 'org.jetbrains.kotlin:kotlin-scripting-jvm-host-embeddable'
|
||||
|
||||
// Coroutines
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0-M2'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core-common:1.3.0-M2'
|
||||
|
||||
// Logging
|
||||
implementation "org.apache.logging.log4j:log4j-api-kotlin:1.0.0"
|
||||
implementation "org.apache.logging.log4j:log4j-api:2.11.1"
|
||||
implementation "org.apache.logging.log4j:log4j-core:2.11.1"
|
||||
// Allows ANSI in logs
|
||||
implementation 'org.fusesource.jansi:jansi:1.18'
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
compileTestKotlin {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
baseName = 'hefbrug'
|
||||
classifier = null
|
||||
version = null
|
||||
}
|
||||
|
||||
runtime {
|
||||
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
|
||||
}
|
||||
|
||||
mainClassName = "me.eater.hefbrug.Main"
|
30
examples/build.gradle
Normal file
30
examples/build.gradle
Normal file
|
@ -0,0 +1,30 @@
|
|||
plugins {
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.3.40'
|
||||
}
|
||||
|
||||
group 'me.eater.hefbrug.examples'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
assemble {
|
||||
dependsOn gradle.includedBuild('hefbrug').task(':jar')
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Kotlin
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
|
||||
// Hefbrug
|
||||
implementation 'me.eater.hefbrug:hefbrug:1.0-SNAPSHOT'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
kotlin {
|
||||
srcDir "src/"
|
||||
}
|
||||
}
|
||||
}
|
1
examples/gradle
Symbolic link
1
examples/gradle
Symbolic link
|
@ -0,0 +1 @@
|
|||
../gradle
|
1
examples/gradlew
vendored
Symbolic link
1
examples/gradlew
vendored
Symbolic link
|
@ -0,0 +1 @@
|
|||
../gradlew
|
2
examples/settings.gradle
Normal file
2
examples/settings.gradle
Normal file
|
@ -0,0 +1,2 @@
|
|||
rootProject.name = "hefbrug-examples"
|
||||
includeBuild("..")
|
6
examples/src/entry.hb.kts
Normal file
6
examples/src/entry.hb.kts
Normal file
|
@ -0,0 +1,6 @@
|
|||
include(
|
||||
"./selectors.hb.kts",
|
||||
"./packages.hb.kts"
|
||||
)
|
||||
|
||||
|
17
examples/src/packages.hb.kts
Normal file
17
examples/src/packages.hb.kts
Normal file
|
@ -0,0 +1,17 @@
|
|||
group("dev") {
|
||||
pkg(
|
||||
"openssh",
|
||||
"neovim",
|
||||
"git",
|
||||
"curl",
|
||||
"youtube-dl",
|
||||
"PackageKit"
|
||||
) {
|
||||
// installed
|
||||
upgraded
|
||||
|
||||
if (id == "curl") {
|
||||
require += pkg["xbps"]
|
||||
}
|
||||
}
|
||||
}
|
5
examples/src/selectors.hb.kts
Normal file
5
examples/src/selectors.hb.kts
Normal file
|
@ -0,0 +1,5 @@
|
|||
assign {
|
||||
node("momo") {
|
||||
groups += "dev"
|
||||
}
|
||||
}
|
1
gradle.properties
Normal file
1
gradle.properties
Normal file
|
@ -0,0 +1 @@
|
|||
kotlin.code.style=official
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.5-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
172
gradlew
vendored
Executable file
172
gradlew
vendored
Executable file
|
@ -0,0 +1,172 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
84
gradlew.bat
vendored
Normal file
84
gradlew.bat
vendored
Normal file
|
@ -0,0 +1,84 @@
|
|||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
1
settings.gradle
Normal file
1
settings.gradle
Normal file
|
@ -0,0 +1 @@
|
|||
rootProject.name = 'hefbrug'
|
38
src/main/kotlin/me/eater/hefbrug/Main.kt
Normal file
38
src/main/kotlin/me/eater/hefbrug/Main.kt
Normal file
|
@ -0,0 +1,38 @@
|
|||
package me.eater.hefbrug
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import me.eater.hefbrug.executor.ExecutionInstance
|
||||
import me.eater.hefbrug.executor.Executor
|
||||
import me.eater.hefbrug.logging.LoggerConfig
|
||||
import me.eater.hefbrug.node.Node
|
||||
import org.apache.logging.log4j.kotlin.loggerOf
|
||||
import java.nio.file.Paths
|
||||
import java.time.Instant
|
||||
|
||||
object Main {
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
System.setProperty("log4j2.configurationFactory", LoggerConfig::class.qualifiedName!!)
|
||||
System.setProperty("log4j.skipJansi", "false")
|
||||
val logger = loggerOf(this.javaClass)
|
||||
logger.info("Booting hefbrug")
|
||||
logger.info("Compiling configuration")
|
||||
val start = Instant.now()
|
||||
val scope = Executor().apply {
|
||||
run(Paths.get(args[0]).toAbsolutePath().toString())
|
||||
}
|
||||
.getScope()
|
||||
|
||||
val end = Instant.now()
|
||||
logger.info("Compiled configuration in ${end.epochSecond - start.epochSecond}s")
|
||||
|
||||
runBlocking {
|
||||
val executionInstance = ExecutionInstance.forNode(
|
||||
Node(Runtime.getRuntime().exec("hostname").inputStream.bufferedReader().readText().trim()),
|
||||
scope
|
||||
)
|
||||
|
||||
executionInstance.apply(false)
|
||||
}
|
||||
}
|
||||
}
|
15
src/main/kotlin/me/eater/hefbrug/access/AccessSkeleton.kt
Normal file
15
src/main/kotlin/me/eater/hefbrug/access/AccessSkeleton.kt
Normal file
|
@ -0,0 +1,15 @@
|
|||
package me.eater.hefbrug.access
|
||||
|
||||
interface AccessSkeleton {
|
||||
fun id(): String
|
||||
suspend fun execute(
|
||||
vararg command: String,
|
||||
environment: Map<String, String> = mapOf(),
|
||||
workingDirectory: String? = null
|
||||
): ExecutionOutput
|
||||
|
||||
suspend fun exists(fileName: String, type: FileType = FileType.Anything): Boolean {
|
||||
val res = execute("test", type.switch, fileName)
|
||||
return res.exitCode == 0
|
||||
}
|
||||
}
|
27
src/main/kotlin/me/eater/hefbrug/access/ExecutionCommand.kt
Normal file
27
src/main/kotlin/me/eater/hefbrug/access/ExecutionCommand.kt
Normal file
|
@ -0,0 +1,27 @@
|
|||
package me.eater.hefbrug.access
|
||||
|
||||
data class ExecutionCommand(
|
||||
val command: Array<out String>,
|
||||
val environment: Map<String, String>,
|
||||
val workingDirectory: String?
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as ExecutionCommand
|
||||
|
||||
if (!command.contentEquals(other.command)) return false
|
||||
if (environment != other.environment) return false
|
||||
if (workingDirectory != other.workingDirectory) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = command.contentHashCode()
|
||||
result = 31 * result + environment.hashCode()
|
||||
result = 31 * result + (workingDirectory?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
16
src/main/kotlin/me/eater/hefbrug/access/ExecutionOutput.kt
Normal file
16
src/main/kotlin/me/eater/hefbrug/access/ExecutionOutput.kt
Normal file
|
@ -0,0 +1,16 @@
|
|||
package me.eater.hefbrug.access
|
||||
|
||||
import me.eater.hefbrug.utils.escape
|
||||
|
||||
data class ExecutionOutput(val command: ExecutionCommand, val exitCode: Int, val stdout: String, val stderr: String) {
|
||||
fun orThrow() {
|
||||
if (exitCode != 0) {
|
||||
throw ExecutionException(
|
||||
"Execution of [${command.command.joinToString(" ") { it.escape() }}] failed with exit code $exitCode",
|
||||
this
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class ExecutionException(message: String?, val output: ExecutionOutput) : RuntimeException(message)
|
||||
}
|
7
src/main/kotlin/me/eater/hefbrug/access/FileType.kt
Normal file
7
src/main/kotlin/me/eater/hefbrug/access/FileType.kt
Normal file
|
@ -0,0 +1,7 @@
|
|||
package me.eater.hefbrug.access
|
||||
|
||||
enum class FileType(val switch: String) {
|
||||
Anything("-e"),
|
||||
Directory("-d"),
|
||||
File("-f");
|
||||
}
|
41
src/main/kotlin/me/eater/hefbrug/access/Local.kt
Normal file
41
src/main/kotlin/me/eater/hefbrug/access/Local.kt
Normal file
|
@ -0,0 +1,41 @@
|
|||
package me.eater.hefbrug.access
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.async
|
||||
import org.apache.logging.log4j.kotlin.Logging
|
||||
import java.io.File
|
||||
import java.nio.charset.Charset
|
||||
|
||||
class Local : AccessSkeleton, Logging {
|
||||
override fun id() = "local"
|
||||
|
||||
override suspend fun execute(
|
||||
vararg command: String,
|
||||
environment: Map<String, String>,
|
||||
workingDirectory: String?
|
||||
): ExecutionOutput {
|
||||
val process = GlobalScope.async(Dispatchers.Unconfined) {
|
||||
ProcessBuilder()
|
||||
.apply {
|
||||
command(*command)
|
||||
|
||||
if (workingDirectory != null)
|
||||
directory(File(workingDirectory))
|
||||
|
||||
environment().putAll(environment)
|
||||
}
|
||||
.start()
|
||||
.apply {
|
||||
waitFor()
|
||||
}
|
||||
}.await()
|
||||
|
||||
return ExecutionOutput(
|
||||
ExecutionCommand(command, environment, workingDirectory),
|
||||
process.exitValue(),
|
||||
process.inputStream.bufferedReader(Charset.forName("UTF-8")).readText(),
|
||||
process.errorStream.bufferedReader(Charset.forName("UTF-8")).readText()
|
||||
)
|
||||
}
|
||||
}
|
15
src/main/kotlin/me/eater/hefbrug/access/NoopAccess.kt
Normal file
15
src/main/kotlin/me/eater/hefbrug/access/NoopAccess.kt
Normal file
|
@ -0,0 +1,15 @@
|
|||
package me.eater.hefbrug.access
|
||||
|
||||
import me.eater.hefbrug.logging.Logging
|
||||
|
||||
class NoopAccess : AccessSkeleton, Logging {
|
||||
override fun id() = "noop"
|
||||
|
||||
override suspend fun execute(
|
||||
vararg command: String,
|
||||
environment: Map<String, String>,
|
||||
workingDirectory: String?
|
||||
): ExecutionOutput {
|
||||
return ExecutionOutput(ExecutionCommand(command, environment, workingDirectory), 0, "", "")
|
||||
}
|
||||
}
|
22
src/main/kotlin/me/eater/hefbrug/access/Wrapper.kt
Normal file
22
src/main/kotlin/me/eater/hefbrug/access/Wrapper.kt
Normal file
|
@ -0,0 +1,22 @@
|
|||
package me.eater.hefbrug.access
|
||||
|
||||
import me.eater.hefbrug.logging.Logging
|
||||
import me.eater.hefbrug.utils.escape
|
||||
import org.apache.logging.log4j.Level
|
||||
|
||||
class Wrapper(private val parent: AccessSkeleton, val rw: Boolean = true) : AccessSkeleton by parent, Logging {
|
||||
|
||||
|
||||
override suspend fun execute(
|
||||
vararg command: String,
|
||||
environment: Map<String, String>,
|
||||
workingDirectory: String?
|
||||
): ExecutionOutput {
|
||||
log(if (rw) Level.INFO else Level.DEBUG, "Executing [${id()}][${if (rw) "rw" else "ro"}] [${command.joinToString(" ") { it.escape() }}]")
|
||||
return parent.execute(*command, environment = environment, workingDirectory = workingDirectory)
|
||||
}
|
||||
|
||||
override suspend fun exists(fileName: String, type: FileType): Boolean {
|
||||
return super.exists(fileName, type)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package me.eater.hefbrug.collector
|
||||
|
||||
import me.eater.hefbrug.definition.DefinitionWildcard
|
||||
import me.eater.hefbrug.executor.ExecutionContext
|
||||
import me.eater.hefbrug.state.AbstractState
|
||||
|
||||
abstract class AbstractCollector<S : AbstractState, D : DefinitionWildcard<S>>(val context: ExecutionContext) {
|
||||
abstract suspend fun collect(definition: D): S
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package me.eater.hefbrug.collector.impl
|
||||
|
||||
import me.eater.hefbrug.collector.AbstractCollector
|
||||
import me.eater.hefbrug.executor.ExecutionContext
|
||||
import me.eater.hefbrug.definition.impl.PackageDefinition
|
||||
import me.eater.hefbrug.state.ExistenceStatus
|
||||
import me.eater.hefbrug.state.impl.PackageState
|
||||
|
||||
class PackageCollector(context: ExecutionContext) : AbstractCollector<PackageState, PackageDefinition>(context) {
|
||||
override suspend fun collect(definition: PackageDefinition): PackageState {
|
||||
val pm = context.getPackageManager()
|
||||
|
||||
return PackageState(definition.id).apply {
|
||||
name = definition.state.name
|
||||
status = if (pm.isInstalled(definition.state.name))
|
||||
ExistenceStatus.Present
|
||||
else
|
||||
ExistenceStatus.Absent
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package me.eater.hefbrug.collector.impl
|
||||
|
||||
import me.eater.hefbrug.collector.AbstractCollector
|
||||
import me.eater.hefbrug.executor.ExecutionContext
|
||||
import me.eater.hefbrug.definition.impl.ServiceDefinition
|
||||
import me.eater.hefbrug.state.impl.ServiceState
|
||||
|
||||
class ServiceCollector(context: ExecutionContext) : AbstractCollector<ServiceState, ServiceDefinition>(context) {
|
||||
override suspend fun collect(definition: ServiceDefinition): ServiceState {
|
||||
val serviceName = definition.state.name
|
||||
|
||||
val sm = context.getServiceManager()
|
||||
|
||||
return ServiceState(definition.id).apply {
|
||||
name = serviceName
|
||||
enabled = sm.isEnabled(serviceName)
|
||||
running = sm.isRunning(serviceName)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package me.eater.hefbrug.definition
|
||||
|
||||
import me.eater.hefbrug.dsl.context.DefinitionContextSkeleton
|
||||
import me.eater.hefbrug.state.AbstractState
|
||||
|
||||
abstract class AbstractDefinition<S : AbstractState, C : DefinitionContextSkeleton<S>>(
|
||||
override val key: DefinitionKey,
|
||||
override val state: S
|
||||
) : DefinitionSkeleton<S, C> {
|
||||
override val id: String get() = state.id
|
||||
override val require: MutableSet<DefinitionKey> = mutableSetOf()
|
||||
override val before: MutableSet<DefinitionKey> = mutableSetOf()
|
||||
override val after: MutableSet<DefinitionKey> = mutableSetOf()
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package me.eater.hefbrug.definition
|
||||
|
||||
import me.eater.hefbrug.collector.AbstractCollector
|
||||
import me.eater.hefbrug.dsl.context.DefinitionContextSkeleton
|
||||
import me.eater.hefbrug.dsl.context.extension_util.FactoryRegister
|
||||
import me.eater.hefbrug.enforcer.AbstractEnforcer
|
||||
import me.eater.hefbrug.executor.ExecutionContext
|
||||
import me.eater.hefbrug.state.AbstractState
|
||||
|
||||
abstract class DefinitionFactory<S : AbstractState, C : DefinitionContextSkeleton<S>, D : DefinitionSkeleton<S, C>>(
|
||||
val id: String,
|
||||
val builder: (String) -> D
|
||||
) {
|
||||
fun build(id: String): D = builder(id)
|
||||
open fun key(id: String): DefinitionKey = DefinitionKey(this.id, id)
|
||||
|
||||
abstract fun createCollector(context: ExecutionContext): AbstractCollector<S, D>
|
||||
abstract fun createEnforcer(context: ExecutionContext): AbstractEnforcer<S>
|
||||
|
||||
init {
|
||||
FactoryRegister += this
|
||||
}
|
||||
}
|
26
src/main/kotlin/me/eater/hefbrug/definition/DefinitionKey.kt
Normal file
26
src/main/kotlin/me/eater/hefbrug/definition/DefinitionKey.kt
Normal file
|
@ -0,0 +1,26 @@
|
|||
package me.eater.hefbrug.definition
|
||||
|
||||
import me.eater.hefbrug.logging.LogFormat
|
||||
|
||||
open class DefinitionKey(val group: String, val id: String) : LogFormat {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as DefinitionKey
|
||||
|
||||
if (group != other.group) return false
|
||||
if (id != other.id) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = group.hashCode()
|
||||
result = 31 * result + id.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String = "$group.$id"
|
||||
override fun logFormat() = "@|blue $this|@"
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package me.eater.hefbrug.definition
|
||||
|
||||
open class DefinitionKeyAction(name: String, id: String, val action: String) : DefinitionKey(name, id)
|
|
@ -0,0 +1,16 @@
|
|||
package me.eater.hefbrug.definition
|
||||
|
||||
import me.eater.hefbrug.dsl.context.DefinitionContextSkeleton
|
||||
import me.eater.hefbrug.state.AbstractState
|
||||
|
||||
interface DefinitionSkeleton<S : AbstractState, out C : DefinitionContextSkeleton<S>> {
|
||||
val key: DefinitionKey
|
||||
val state: S
|
||||
|
||||
val id: String
|
||||
val require: MutableSet<DefinitionKey>
|
||||
val before: MutableSet<DefinitionKey>
|
||||
val after: MutableSet<DefinitionKey>
|
||||
|
||||
fun getContext(): C
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package me.eater.hefbrug.definition
|
||||
|
||||
import me.eater.hefbrug.dsl.context.DefinitionContextSkeleton
|
||||
|
||||
typealias DefinitionWildcard<S> = DefinitionSkeleton<S, DefinitionContextSkeleton<S>>
|
|
@ -0,0 +1,31 @@
|
|||
package me.eater.hefbrug.definition.impl
|
||||
|
||||
import me.eater.hefbrug.collector.impl.PackageCollector
|
||||
import me.eater.hefbrug.definition.AbstractDefinition
|
||||
import me.eater.hefbrug.definition.DefinitionFactory
|
||||
import me.eater.hefbrug.dsl.context.impl.PackageContext
|
||||
import me.eater.hefbrug.enforcer.impl.PackageEnforcer
|
||||
import me.eater.hefbrug.executor.ExecutionContext
|
||||
import me.eater.hefbrug.state.impl.PackageState
|
||||
|
||||
class PackageDefinition(id: String) :
|
||||
AbstractDefinition<PackageState, PackageContext>(factory.key(id), PackageState(id)) {
|
||||
override fun getContext(): PackageContext = PackageContext(this)
|
||||
|
||||
class Factory : DefinitionFactory<PackageState, PackageContext, PackageDefinition>("package", ::PackageDefinition) {
|
||||
override fun createCollector(context: ExecutionContext): PackageCollector =
|
||||
PackageCollector(context)
|
||||
|
||||
override fun createEnforcer(context: ExecutionContext): PackageEnforcer =
|
||||
PackageEnforcer(context)
|
||||
}
|
||||
|
||||
object KeyHelper {
|
||||
operator fun get(id: String) = factory.key(id)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val factory: Factory =
|
||||
Factory()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package me.eater.hefbrug.definition.impl
|
||||
|
||||
import me.eater.hefbrug.collector.impl.ServiceCollector
|
||||
import me.eater.hefbrug.definition.AbstractDefinition
|
||||
import me.eater.hefbrug.definition.DefinitionFactory
|
||||
import me.eater.hefbrug.definition.DefinitionKeyAction
|
||||
import me.eater.hefbrug.dsl.context.impl.ServiceContext
|
||||
import me.eater.hefbrug.enforcer.impl.ServiceEnforcer
|
||||
import me.eater.hefbrug.executor.ExecutionContext
|
||||
import me.eater.hefbrug.state.impl.ServiceState
|
||||
|
||||
class ServiceDefinition(id: String) :
|
||||
AbstractDefinition<ServiceState, ServiceContext>(factory.key(id), ServiceState(id)) {
|
||||
override fun getContext(): ServiceContext = ServiceContext(this)
|
||||
|
||||
class Factory : DefinitionFactory<ServiceState, ServiceContext, ServiceDefinition>("service", ::ServiceDefinition) {
|
||||
override fun key(id: String) = Key(this.id, id, "default")
|
||||
override fun createCollector(context: ExecutionContext): ServiceCollector =
|
||||
ServiceCollector(context)
|
||||
|
||||
override fun createEnforcer(context: ExecutionContext): ServiceEnforcer =
|
||||
ServiceEnforcer(context)
|
||||
}
|
||||
|
||||
|
||||
class Key(name: String, id: String, action: String) : DefinitionKeyAction(name, id, action) {
|
||||
fun reload() = Key(group, id, "reload")
|
||||
fun restart() = Key(group, id, "restart")
|
||||
}
|
||||
|
||||
object KeyHelper {
|
||||
operator fun get(id: String) = factory.key(id)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val factory: Factory =
|
||||
Factory()
|
||||
}
|
||||
}
|
20
src/main/kotlin/me/eater/hefbrug/dsl/Location.kt
Normal file
20
src/main/kotlin/me/eater/hefbrug/dsl/Location.kt
Normal file
|
@ -0,0 +1,20 @@
|
|||
package me.eater.hefbrug.dsl
|
||||
|
||||
import java.nio.file.Paths
|
||||
|
||||
sealed class Location {
|
||||
data class File(override val path: String) : Location() {
|
||||
override val directory: String
|
||||
get() = java.io.File(path).parent.toString()
|
||||
}
|
||||
|
||||
object Memory : Location() {
|
||||
override val path: String = "-"
|
||||
override val directory = System.getProperty("user.dir") ?: "-"
|
||||
}
|
||||
|
||||
abstract val path: String
|
||||
abstract val directory: String
|
||||
|
||||
fun resolve(file: String) = Paths.get(directory, file).toAbsolutePath().toString()
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package me.eater.hefbrug.dsl.annotation
|
||||
|
||||
@DslMarker
|
||||
annotation class HefbrugDSL
|
|
@ -0,0 +1,17 @@
|
|||
package me.eater.hefbrug.dsl.context
|
||||
|
||||
import me.eater.hefbrug.node.Node
|
||||
import me.eater.hefbrug.selector.SelectorInterface
|
||||
|
||||
class AssignContext(private val node: Node) :
|
||||
SelectionDefinitionContext<AssignContext.() -> Unit> {
|
||||
|
||||
val groups: MutableSet<String>
|
||||
get() = node.groups
|
||||
|
||||
override fun select(selector: SelectorInterface, block: AssignContext.() -> Unit) {
|
||||
if (selector.matches(node)) {
|
||||
block(this)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package me.eater.hefbrug.dsl.context
|
||||
|
||||
import me.eater.hefbrug.dsl.annotation.HefbrugDSL
|
||||
import me.eater.hefbrug.dsl.context.extension_util.RuntimeRegister
|
||||
import me.eater.hefbrug.dsl.scope.AbstractScope
|
||||
import me.eater.hefbrug.dsl.scope.RootScope
|
||||
import me.eater.hefbrug.dsl.scope.SelectorScope
|
||||
import java.util.*
|
||||
|
||||
@HefbrugDSL
|
||||
class CollectionContext(override val runtimeUUID: UUID, private val parentScope: SelectorScope, private val rootScope: RootScope) :
|
||||
TargetedContext() {
|
||||
|
||||
override val contextUUID: UUID
|
||||
get() = parentScope.contextUUID
|
||||
|
||||
init {
|
||||
RuntimeRegister[runtimeUUID].registerScope(this, AbstractScope(rootScope))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package me.eater.hefbrug.dsl.context
|
||||
|
||||
import java.util.*
|
||||
|
||||
|
||||
interface ContextInterface {
|
||||
val contextUUID: UUID
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package me.eater.hefbrug.dsl.context
|
||||
|
||||
import me.eater.hefbrug.definition.DefinitionKey
|
||||
import me.eater.hefbrug.definition.DefinitionSkeleton
|
||||
import me.eater.hefbrug.dsl.annotation.HefbrugDSL
|
||||
import me.eater.hefbrug.state.AbstractState
|
||||
import me.eater.hefbrug.state.property.ProxyDelegate
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KMutableProperty0
|
||||
|
||||
|
||||
@HefbrugDSL
|
||||
open class DefinitionContext<S : AbstractState>(private val definition: DefinitionSkeleton<S, DefinitionContextSkeleton<S>>) :
|
||||
DefinitionContextSkeleton<S> {
|
||||
protected fun <T> proxy(reference: KMutableProperty0<T>): ReadWriteProperty<DefinitionContext<*>, T> =
|
||||
ProxyDelegate(reference)
|
||||
|
||||
protected val state: S
|
||||
get() = definition.state
|
||||
|
||||
override val after: MutableSet<DefinitionKey>
|
||||
get() = definition.after
|
||||
override val before: MutableSet<DefinitionKey>
|
||||
get() = definition.before
|
||||
override val require: MutableSet<DefinitionKey>
|
||||
get() = definition.require
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package me.eater.hefbrug.dsl.context
|
||||
|
||||
import me.eater.hefbrug.definition.DefinitionKey
|
||||
import me.eater.hefbrug.state.AbstractState
|
||||
|
||||
interface DefinitionContextSkeleton<S : AbstractState> {
|
||||
val require: MutableSet<DefinitionKey>
|
||||
val before: MutableSet<DefinitionKey>
|
||||
val after: MutableSet<DefinitionKey>
|
||||
}
|
12
src/main/kotlin/me/eater/hefbrug/dsl/context/Emitter.kt
Normal file
12
src/main/kotlin/me/eater/hefbrug/dsl/context/Emitter.kt
Normal file
|
@ -0,0 +1,12 @@
|
|||
package me.eater.hefbrug.dsl.context
|
||||
|
||||
import me.eater.hefbrug.definition.DefinitionKey
|
||||
import me.eater.hefbrug.definition.DefinitionKeyAction
|
||||
|
||||
interface Emitter {
|
||||
val notify: MutableSet<DefinitionKeyAction>
|
||||
|
||||
operator fun MutableSet<DefinitionKeyAction>.plusAssign(definitionKey: Array<out DefinitionKey>) {
|
||||
this.addAll(definitionKey.map { DefinitionKeyAction(it.group, it.id, "default") })
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package me.eater.hefbrug.dsl.context
|
||||
|
||||
import me.eater.hefbrug.definition.DefinitionKeyAction
|
||||
|
||||
open class EmitterContext : Emitter {
|
||||
override val notify: MutableSet<DefinitionKeyAction> = mutableSetOf()
|
||||
}
|
5
src/main/kotlin/me/eater/hefbrug/dsl/context/Listener.kt
Normal file
5
src/main/kotlin/me/eater/hefbrug/dsl/context/Listener.kt
Normal file
|
@ -0,0 +1,5 @@
|
|||
package me.eater.hefbrug.dsl.context
|
||||
|
||||
interface Listener {
|
||||
val listen: MutableSet<Unit>
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package me.eater.hefbrug.dsl.context
|
||||
|
||||
class ListenerContext : Listener {
|
||||
override val listen: MutableSet<Unit> = mutableSetOf()
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
@file:Suppress("UNUSED")
|
||||
|
||||
package me.eater.hefbrug.dsl.context
|
||||
|
||||
import me.eater.hefbrug.dsl.annotation.HefbrugDSL
|
||||
import me.eater.hefbrug.module.Module
|
||||
import me.eater.hefbrug.utils.ArgumentDelegate
|
||||
import java.util.*
|
||||
|
||||
@HefbrugDSL
|
||||
class ModuleContext(
|
||||
override val runtimeUUID: UUID,
|
||||
override val contextUUID: UUID,
|
||||
private val module: Module,
|
||||
private val arguments: Map<String, Any?>
|
||||
) :
|
||||
TargetedContext() {
|
||||
val id: String
|
||||
get() = module.id
|
||||
|
||||
fun <T> arg(name: String, default: T? = null) =
|
||||
ArgumentDelegate<T>(arguments, name, default)
|
||||
|
||||
fun <T> arg(default: T? = null) =
|
||||
ArgumentDelegate<T>(arguments, null, default)
|
||||
|
||||
|
||||
}
|
32
src/main/kotlin/me/eater/hefbrug/dsl/context/RootContext.kt
Normal file
32
src/main/kotlin/me/eater/hefbrug/dsl/context/RootContext.kt
Normal file
|
@ -0,0 +1,32 @@
|
|||
package me.eater.hefbrug.dsl.context
|
||||
|
||||
|
||||
import me.eater.hefbrug.dsl.annotation.HefbrugDSL
|
||||
import me.eater.hefbrug.dsl.context.extension_util.Register
|
||||
import me.eater.hefbrug.selector.NodeSelector
|
||||
import me.eater.hefbrug.selector.SelectorInterface
|
||||
import java.util.*
|
||||
|
||||
|
||||
@HefbrugDSL
|
||||
open class RootContext : ContextInterface, RootContextSkeleton,
|
||||
SelectionDefinitionContext<suspend TargetedContext.() -> Unit> {
|
||||
override val contextUUID: UUID = UUID.randomUUID()
|
||||
|
||||
override fun select(selector: SelectorInterface, block: suspend TargetedContext.() -> Unit) {
|
||||
Register[contextUUID].selectorScope.addSelector(selector, block)
|
||||
}
|
||||
|
||||
override fun module(id: String, block: suspend ModuleContext.() -> Unit) {
|
||||
Register[contextUUID].selectorScope.getModule(id).addBlock(block)
|
||||
}
|
||||
|
||||
override fun assign(block: AssignContext.() -> Unit) {
|
||||
Register[contextUUID].selectorScope.addAssigner(block)
|
||||
}
|
||||
|
||||
@Suppress("UNUSED")
|
||||
fun node(selector: Regex, block: suspend TargetedContext.() -> Unit = {}) {
|
||||
Register[contextUUID].selectorScope.addSelector(NodeSelector(selector), block)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package me.eater.hefbrug.dsl.context
|
||||
|
||||
import me.eater.hefbrug.dsl.annotation.HefbrugDSL
|
||||
|
||||
@HefbrugDSL
|
||||
interface RootContextSkeleton {
|
||||
fun module(id: String, block: suspend ModuleContext.() -> Unit)
|
||||
fun assign(block: AssignContext.() -> Unit)
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package me.eater.hefbrug.dsl.context
|
||||
|
||||
import me.eater.hefbrug.dsl.annotation.HefbrugDSL
|
||||
import me.eater.hefbrug.selector.*
|
||||
|
||||
@HefbrugDSL
|
||||
interface SelectionDefinitionContext<C> {
|
||||
fun select(selector: SelectorInterface, block: C)
|
||||
fun all() = TrueSelector()
|
||||
fun node(selector: String): SelectorInterface = NodeSelector(selector)
|
||||
fun group(name: String): SelectorInterface = GroupSelector(name)
|
||||
fun group(vararg name: String): SelectorInterface = AllSelector(name.map { GroupSelector(it) })
|
||||
|
||||
|
||||
fun group(name: String, block: C) {
|
||||
select(GroupSelector(name), block)
|
||||
}
|
||||
|
||||
fun group(vararg name: String, block: C) {
|
||||
select(group(*name), block)
|
||||
}
|
||||
|
||||
fun node(selector: String, block: C) {
|
||||
select(node(selector), block)
|
||||
}
|
||||
|
||||
fun all(block: C) {
|
||||
select(all(), block)
|
||||
}
|
||||
|
||||
operator fun SelectorInterface.invoke(block: C) {
|
||||
this@SelectionDefinitionContext.select(this, block)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package me.eater.hefbrug.dsl.context
|
||||
|
||||
import me.eater.hefbrug.dsl.Location
|
||||
import me.eater.hefbrug.dsl.annotation.HefbrugDSL
|
||||
import me.eater.hefbrug.executor.Executor
|
||||
|
||||
@HefbrugDSL
|
||||
@Suppress("UNUSED")
|
||||
open class SourceContext(private val root: RootContext, var location: Location, private val executor: Executor) :
|
||||
RootContextSkeleton by root,
|
||||
SelectionDefinitionContext<suspend TargetedContext.() -> Unit> by root,
|
||||
SourceContextSkeleton {
|
||||
fun include(vararg file: String) {
|
||||
for (f in file) {
|
||||
executor.run(location.resolve(f))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package me.eater.hefbrug.dsl.context
|
||||
|
||||
interface SourceContextSkeleton : RootContextSkeleton {
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
@file:Suppress("UNUSED")
|
||||
|
||||
package me.eater.hefbrug.dsl.context
|
||||
|
||||
import me.eater.hefbrug.definition.impl.PackageDefinition
|
||||
import me.eater.hefbrug.definition.impl.ServiceDefinition
|
||||
import me.eater.hefbrug.dsl.annotation.HefbrugDSL
|
||||
import me.eater.hefbrug.dsl.context.extension_util.DefinitionHelper.runBlock
|
||||
import me.eater.hefbrug.dsl.context.impl.PackageContext
|
||||
import me.eater.hefbrug.dsl.context.impl.ServiceContext
|
||||
import java.util.*
|
||||
|
||||
@HefbrugDSL
|
||||
abstract class TargetedContext : ContextInterface {
|
||||
abstract val runtimeUUID: UUID
|
||||
suspend fun sv(vararg id: String, block: suspend ServiceContext.() -> Unit = {}) =
|
||||
runBlock(this, ServiceDefinition.factory, id, block)
|
||||
|
||||
suspend fun pkg(vararg id: String, block: suspend PackageContext.() -> Unit = {}) =
|
||||
runBlock(this, PackageDefinition.factory, id, block)
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package me.eater.hefbrug.dsl.context.extension_util
|
||||
|
||||
import me.eater.hefbrug.definition.DefinitionFactory
|
||||
import me.eater.hefbrug.definition.DefinitionKey
|
||||
import me.eater.hefbrug.definition.DefinitionSkeleton
|
||||
import me.eater.hefbrug.dsl.context.DefinitionContextSkeleton
|
||||
import me.eater.hefbrug.dsl.context.TargetedContext
|
||||
import me.eater.hefbrug.dsl.scope.TargetedScope
|
||||
import me.eater.hefbrug.state.AbstractState
|
||||
|
||||
|
||||
object DefinitionHelper {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
suspend fun <S : AbstractState, C : DefinitionContextSkeleton<S>, D : DefinitionSkeleton<S, C>, F : DefinitionFactory<S, C, D>> runBlock(
|
||||
context: TargetedContext,
|
||||
factory: F,
|
||||
id: Array<out String>,
|
||||
block: suspend C.() -> Unit
|
||||
) {
|
||||
for (idx in id) {
|
||||
block(
|
||||
RuntimeRegister[context.runtimeUUID]
|
||||
.getScope<TargetedScope>(context)
|
||||
.getDefinition(idx, factory)
|
||||
.getContext()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class KeyHelper(private val factory: DefinitionFactory<*, *, *>) {
|
||||
operator fun get(id: String) = factory.key(id)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package me.eater.hefbrug.dsl.context.extension_util
|
||||
|
||||
import me.eater.hefbrug.definition.DefinitionFactory
|
||||
|
||||
object FactoryRegister {
|
||||
private val map = mutableMapOf<String, DefinitionFactory<*, *, *>>()
|
||||
|
||||
operator fun plusAssign(factory: DefinitionFactory<*, *, *>) {
|
||||
map[factory.id] = factory
|
||||
}
|
||||
|
||||
operator fun get(id: String) = map[id]!!
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package me.eater.hefbrug.dsl.context.extension_util
|
||||
|
||||
import me.eater.hefbrug.dsl.scope.SelectorScope
|
||||
import me.eater.hefbrug.executor.Executor
|
||||
import java.util.*
|
||||
|
||||
class Register(val id: UUID) {
|
||||
val selectorScope: SelectorScope = SelectorScope(id)
|
||||
|
||||
companion object {
|
||||
private val registerMap = mutableMapOf<UUID, Register>()
|
||||
|
||||
operator fun get(id: UUID) = registerMap[id]!!
|
||||
|
||||
fun register(register: Register) {
|
||||
registerMap[register.id] = register
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package me.eater.hefbrug.dsl.context.extension_util
|
||||
|
||||
import me.eater.hefbrug.dsl.context.ContextInterface
|
||||
import me.eater.hefbrug.dsl.scope.ScopeInterface
|
||||
import java.util.*
|
||||
|
||||
class RuntimeRegister {
|
||||
private val scopeMap = mutableMapOf<ContextInterface, ScopeInterface>()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <T : ScopeInterface> getScope(context: ContextInterface): T =
|
||||
scopeMap[context]!! as T
|
||||
|
||||
fun registerScope(context: ContextInterface, scope: ScopeInterface) {
|
||||
scopeMap[context] = scope
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val runtimeRegisterMap = mutableMapOf<UUID, RuntimeRegister>()
|
||||
|
||||
operator fun get(runtimeUUID: UUID) =
|
||||
runtimeRegisterMap.getOrPut(runtimeUUID, { RuntimeRegister() })
|
||||
|
||||
fun remove(runtimeUUID: UUID) {
|
||||
runtimeRegisterMap.remove(runtimeUUID)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package me.eater.hefbrug.dsl.context.impl
|
||||
|
||||
import me.eater.hefbrug.definition.impl.PackageDefinition
|
||||
import me.eater.hefbrug.dsl.context.DefinitionContext
|
||||
import me.eater.hefbrug.dsl.context.Emitter
|
||||
import me.eater.hefbrug.dsl.context.EmitterContext
|
||||
import me.eater.hefbrug.state.ExistenceStatus
|
||||
import me.eater.hefbrug.state.impl.PackageState
|
||||
|
||||
class PackageContext(definition: PackageDefinition) : DefinitionContext<PackageState>(definition),
|
||||
Emitter by EmitterContext() {
|
||||
val id: String
|
||||
get() = state.id
|
||||
|
||||
fun upgrade(upgrade: Boolean) {
|
||||
state.upgrade = upgrade
|
||||
}
|
||||
|
||||
var ensure by proxy(state::status)
|
||||
|
||||
val upgraded: Unit
|
||||
get() {
|
||||
state.upgrade = true
|
||||
}
|
||||
|
||||
val held: Unit
|
||||
get() {
|
||||
state.upgrade = false
|
||||
}
|
||||
|
||||
val installed: Unit
|
||||
get() {
|
||||
state.status = ExistenceStatus.Present
|
||||
}
|
||||
|
||||
val absent: Unit
|
||||
get() {
|
||||
state.status = ExistenceStatus.Absent
|
||||
}
|
||||
|
||||
var name: String by proxy(state::name)
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package me.eater.hefbrug.dsl.context.impl
|
||||
|
||||
import me.eater.hefbrug.definition.impl.ServiceDefinition
|
||||
import me.eater.hefbrug.dsl.context.*
|
||||
import me.eater.hefbrug.state.impl.ServiceState
|
||||
|
||||
class ServiceContext(definition: ServiceDefinition) : DefinitionContext<ServiceState>(definition),
|
||||
Emitter by EmitterContext(), Listener by ListenerContext() {
|
||||
|
||||
var name: String by proxy(state::name)
|
||||
|
||||
val enabled: Unit
|
||||
get() {
|
||||
state.enabled = true
|
||||
}
|
||||
|
||||
val running: Unit
|
||||
get() {
|
||||
state.running = true
|
||||
}
|
||||
|
||||
val stopped: Unit
|
||||
get() {
|
||||
state.running = false
|
||||
}
|
||||
|
||||
val disabled: Unit
|
||||
get() {
|
||||
state.enabled = false
|
||||
}
|
||||
|
||||
val autostart: Unit
|
||||
get() {
|
||||
state.autostart = true
|
||||
}
|
||||
|
||||
val noAutostart: Unit
|
||||
get() {
|
||||
state.autostart = false
|
||||
}
|
||||
}
|
15
src/main/kotlin/me/eater/hefbrug/dsl/scope/AbstractScope.kt
Normal file
15
src/main/kotlin/me/eater/hefbrug/dsl/scope/AbstractScope.kt
Normal file
|
@ -0,0 +1,15 @@
|
|||
package me.eater.hefbrug.dsl.scope
|
||||
|
||||
import me.eater.hefbrug.definition.DefinitionFactory
|
||||
import me.eater.hefbrug.definition.DefinitionSkeleton
|
||||
import me.eater.hefbrug.dsl.context.DefinitionContextSkeleton
|
||||
import me.eater.hefbrug.state.AbstractState
|
||||
|
||||
class AbstractScope(private val parentScope: ScopeInterface) : ScopeInterface, TargetedScope {
|
||||
override fun <S : AbstractState, C : DefinitionContextSkeleton<S>, D : DefinitionSkeleton<S, C>> getDefinition(
|
||||
id: String,
|
||||
factory: DefinitionFactory<S, C, D>
|
||||
): D =
|
||||
parentScope.getDefinition(id, factory)
|
||||
|
||||
}
|
23
src/main/kotlin/me/eater/hefbrug/dsl/scope/RootScope.kt
Normal file
23
src/main/kotlin/me/eater/hefbrug/dsl/scope/RootScope.kt
Normal file
|
@ -0,0 +1,23 @@
|
|||
package me.eater.hefbrug.dsl.scope
|
||||
|
||||
import me.eater.hefbrug.definition.DefinitionFactory
|
||||
import me.eater.hefbrug.definition.DefinitionKey
|
||||
import me.eater.hefbrug.definition.DefinitionSkeleton
|
||||
import me.eater.hefbrug.dsl.context.DefinitionContextSkeleton
|
||||
import me.eater.hefbrug.state.AbstractState
|
||||
|
||||
class RootScope : ScopeInterface {
|
||||
private val definitionsMut =
|
||||
mutableMapOf<DefinitionKey, DefinitionSkeleton<*, *>>()
|
||||
|
||||
val definitions by lazy { definitionsMut }
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <S : AbstractState, C : DefinitionContextSkeleton<S>, D : DefinitionSkeleton<S, C>> getDefinition(
|
||||
id: String,
|
||||
factory: DefinitionFactory<S, C, D>
|
||||
): D =
|
||||
definitionsMut.getOrPut(factory.key(id)) {
|
||||
factory.build(id)
|
||||
} as D
|
||||
}
|
13
src/main/kotlin/me/eater/hefbrug/dsl/scope/ScopeInterface.kt
Normal file
13
src/main/kotlin/me/eater/hefbrug/dsl/scope/ScopeInterface.kt
Normal file
|
@ -0,0 +1,13 @@
|
|||
package me.eater.hefbrug.dsl.scope
|
||||
|
||||
import me.eater.hefbrug.definition.DefinitionFactory
|
||||
import me.eater.hefbrug.definition.DefinitionSkeleton
|
||||
import me.eater.hefbrug.dsl.context.DefinitionContextSkeleton
|
||||
import me.eater.hefbrug.state.AbstractState
|
||||
|
||||
interface ScopeInterface {
|
||||
fun <S : AbstractState, C : DefinitionContextSkeleton<S>, D : DefinitionSkeleton<S, C>> getDefinition(
|
||||
id: String,
|
||||
factory: DefinitionFactory<S, C, D>
|
||||
): D
|
||||
}
|
56
src/main/kotlin/me/eater/hefbrug/dsl/scope/SelectorScope.kt
Normal file
56
src/main/kotlin/me/eater/hefbrug/dsl/scope/SelectorScope.kt
Normal file
|
@ -0,0 +1,56 @@
|
|||
package me.eater.hefbrug.dsl.scope
|
||||
|
||||
import me.eater.hefbrug.definition.DefinitionKey
|
||||
import me.eater.hefbrug.definition.DefinitionSkeleton
|
||||
import me.eater.hefbrug.dsl.context.AssignContext
|
||||
import me.eater.hefbrug.dsl.context.CollectionContext
|
||||
import me.eater.hefbrug.dsl.context.ContextInterface
|
||||
import me.eater.hefbrug.dsl.context.TargetedContext
|
||||
import me.eater.hefbrug.dsl.context.extension_util.RuntimeRegister
|
||||
import me.eater.hefbrug.logging.Logging
|
||||
import me.eater.hefbrug.module.Module
|
||||
import me.eater.hefbrug.node.Node
|
||||
import me.eater.hefbrug.selector.SelectorInterface
|
||||
import java.util.*
|
||||
|
||||
class SelectorScope(
|
||||
override val contextUUID: UUID
|
||||
) : ContextInterface, Logging {
|
||||
private val selectors: MutableSet<Pair<SelectorInterface, suspend TargetedContext.() -> Unit>> = mutableSetOf()
|
||||
private val modules: MutableMap<String, Module> = mutableMapOf()
|
||||
private val assigners: MutableSet<AssignContext.() -> Unit> = mutableSetOf()
|
||||
|
||||
fun getModule(id: String): Module =
|
||||
modules.getOrPut(id, { Module(id, contextUUID) })
|
||||
|
||||
|
||||
fun addSelector(selector: SelectorInterface, block: suspend TargetedContext.() -> Unit) =
|
||||
selectors.add(selector to block)
|
||||
|
||||
fun addAssigner(block: AssignContext.() -> Unit) {
|
||||
assigners.add(block)
|
||||
}
|
||||
|
||||
suspend fun collect(node: Node): Map<DefinitionKey, DefinitionSkeleton<*, *>> {
|
||||
val assign = AssignContext(node)
|
||||
assigners.forEach { it(assign) }
|
||||
info("Collecting definitions for $node")
|
||||
|
||||
val rootScope = RootScope()
|
||||
val collection = CollectionContext(UUID.randomUUID(), this, rootScope)
|
||||
|
||||
|
||||
for ((selector, block) in selectors) {
|
||||
if (!selector.matches(node)) {
|
||||
continue
|
||||
}
|
||||
|
||||
block(collection)
|
||||
}
|
||||
|
||||
RuntimeRegister.remove(collection.runtimeUUID)
|
||||
|
||||
info("Collected ${rootScope.definitions.size} for $node")
|
||||
return rootScope.definitions
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package me.eater.hefbrug.dsl.scope
|
||||
|
||||
interface TargetedScope : ScopeInterface
|
|
@ -0,0 +1,13 @@
|
|||
package me.eater.hefbrug.enforcer
|
||||
|
||||
import me.eater.hefbrug.executor.ExecutionContext
|
||||
import me.eater.hefbrug.state.AbstractState
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
abstract class AbstractEnforcer<S: AbstractState>(val context: ExecutionContext) {
|
||||
operator fun Set<String>.contains(property: KProperty<*>): Boolean {
|
||||
return property.name in this
|
||||
}
|
||||
|
||||
abstract suspend fun enforce(currentState: S, desiredState: S, changeSet: Set<String>)
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package me.eater.hefbrug.enforcer.impl
|
||||
|
||||
import me.eater.hefbrug.enforcer.AbstractEnforcer
|
||||
import me.eater.hefbrug.executor.ExecutionContext
|
||||
import me.eater.hefbrug.state.ExistenceStatus
|
||||
import me.eater.hefbrug.state.impl.PackageState
|
||||
|
||||
class PackageEnforcer(context: ExecutionContext) : AbstractEnforcer<PackageState>(context) {
|
||||
override suspend fun enforce(currentState: PackageState, desiredState: PackageState, changeSet: Set<String>) {
|
||||
val pm = context.getPackageManager()
|
||||
pm.sync()
|
||||
|
||||
if (PackageState::status in changeSet) {
|
||||
when (desiredState.status) {
|
||||
ExistenceStatus.Absent -> pm.remove(desiredState.name)
|
||||
ExistenceStatus.Present -> pm.install(desiredState.name)
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
if (currentState.status == ExistenceStatus.Present && (desiredState.upgrade && !(PackageState::status in changeSet || desiredState.status == ExistenceStatus.Absent))) {
|
||||
pm.upgrade(desiredState.name)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package me.eater.hefbrug.enforcer.impl
|
||||
|
||||
import me.eater.hefbrug.enforcer.AbstractEnforcer
|
||||
import me.eater.hefbrug.executor.ExecutionContext
|
||||
import me.eater.hefbrug.state.impl.ServiceState
|
||||
|
||||
class ServiceEnforcer(context: ExecutionContext) : AbstractEnforcer<ServiceState>(context) {
|
||||
override suspend fun enforce(currentState: ServiceState, desiredState: ServiceState, changeSet: Set<String>) {
|
||||
val sm = context.getServiceManager()
|
||||
|
||||
sm.setState(
|
||||
desiredState.name,
|
||||
enabled = desiredState.enabled,
|
||||
running = desiredState.running,
|
||||
autostart = desiredState.autostart
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package me.eater.hefbrug.executor
|
||||
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import me.eater.hefbrug.access.AccessSkeleton
|
||||
import me.eater.hefbrug.access.NoopAccess
|
||||
import me.eater.hefbrug.access.Wrapper
|
||||
import me.eater.hefbrug.node.Node
|
||||
import me.eater.hefbrug.platform_utils.`package`.PackageManager
|
||||
import me.eater.hefbrug.platform_utils.service.ServiceManager
|
||||
|
||||
class ExecutionContext(access: AccessSkeleton, val node: Node, val noop: Boolean = true) {
|
||||
private var packageManager: PackageManager? = null
|
||||
private var packageManagerLock = Mutex()
|
||||
private var serviceManager: ServiceManager? = null
|
||||
private var serviceManagerLock = Mutex()
|
||||
|
||||
val roAccess: AccessSkeleton = Wrapper(access, false)
|
||||
val rwAccess: AccessSkeleton = Wrapper(if (noop) NoopAccess() else access)
|
||||
|
||||
suspend fun getPackageManager(): PackageManager {
|
||||
if (packageManager == null) {
|
||||
packageManagerLock.withLock {
|
||||
if (packageManager == null) {
|
||||
packageManager = PackageManager.getManager(this@ExecutionContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return packageManager!!
|
||||
}
|
||||
|
||||
suspend fun getServiceManager(): ServiceManager {
|
||||
if (serviceManager == null) {
|
||||
serviceManagerLock.withLock {
|
||||
if (serviceManager == null) {
|
||||
serviceManager = ServiceManager.getManager(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return serviceManager!!
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package me.eater.hefbrug.executor
|
||||
|
||||
import me.eater.hefbrug.access.ExecutionOutput
|
||||
import me.eater.hefbrug.access.Local
|
||||
import me.eater.hefbrug.definition.DefinitionKey
|
||||
import me.eater.hefbrug.definition.DefinitionSkeleton
|
||||
import me.eater.hefbrug.dsl.context.DefinitionContextSkeleton
|
||||
import me.eater.hefbrug.dsl.scope.SelectorScope
|
||||
import me.eater.hefbrug.logging.Logging
|
||||
import me.eater.hefbrug.logging.message.Messages
|
||||
import me.eater.hefbrug.node.Node
|
||||
import me.eater.hefbrug.state.AbstractState
|
||||
import me.eater.hefbrug.utils.parallel
|
||||
import org.apache.logging.log4j.MarkerManager
|
||||
import org.jetbrains.kotlin.utils.mapToIndex
|
||||
|
||||
class ExecutionInstance(private val node: Node, private val definitions: Map<DefinitionKey, DefinitionSkeleton<*, *>>) :
|
||||
Logging {
|
||||
|
||||
suspend fun apply(noop: Boolean = true) {
|
||||
val layers = GraphBuilder.makeGraph(definitions)
|
||||
debug("Graph created ${layers.size} layers of definitions to apply")
|
||||
val context = ExecutionContext(Local(), node, noop)
|
||||
val collector = StateCollector(context)
|
||||
val enforcer = StateEnforcer(context)
|
||||
for ((layer, i) in layers.mapToIndex()) {
|
||||
debug("Start applying layer #$i for @|green $node|@")
|
||||
parallel(layer) { item ->
|
||||
debug(Messages.applying(item, node))
|
||||
val def = definitions[item] ?: run {
|
||||
error("Missing definition for @|blue $item|@")
|
||||
return@parallel
|
||||
}
|
||||
|
||||
try {
|
||||
trace(Messages.collectingCurrentState(item, node))
|
||||
@Suppress("UNCHECKED_CAST") val state =
|
||||
collector.collect(def as DefinitionSkeleton<AbstractState, DefinitionContextSkeleton<AbstractState>>)
|
||||
trace(Messages.startEnforcing(item, node))
|
||||
enforcer.enforce(def, state)
|
||||
} catch (e: ExecutionOutput.ExecutionException) {
|
||||
error(Messages.failedDefinition(def.key, node, e.output))
|
||||
} catch (t: Throwable) {
|
||||
error(
|
||||
MarkerManager.getMarker("DefinitionError"),
|
||||
"Failed collecting and enforcing state for @|blue $item|@ for @|green $node|@",
|
||||
t
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
suspend fun forNode(node: Node, scope: SelectorScope) = ExecutionInstance(node, scope.collect(node))
|
||||
}
|
||||
}
|
64
src/main/kotlin/me/eater/hefbrug/executor/Executor.kt
Normal file
64
src/main/kotlin/me/eater/hefbrug/executor/Executor.kt
Normal file
|
@ -0,0 +1,64 @@
|
|||
package me.eater.hefbrug.executor
|
||||
|
||||
import me.eater.hefbrug.dsl.Location
|
||||
import me.eater.hefbrug.dsl.context.RootContext
|
||||
import me.eater.hefbrug.dsl.context.SourceContext
|
||||
import me.eater.hefbrug.dsl.context.extension_util.Register
|
||||
import me.eater.hefbrug.dsl.scope.SelectorScope
|
||||
import me.eater.hefbrug.logging.Logging
|
||||
import org.apache.logging.log4j.Level
|
||||
import org.apache.logging.log4j.MarkerManager
|
||||
import java.io.File
|
||||
import java.nio.file.Paths
|
||||
import kotlin.script.experimental.api.ScriptDiagnostic
|
||||
import kotlin.script.experimental.api.constructorArgs
|
||||
import kotlin.script.experimental.api.implicitReceivers
|
||||
import kotlin.script.experimental.host.toScriptSource
|
||||
import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost
|
||||
import kotlin.script.experimental.jvmhost.createJvmCompilationConfigurationFromTemplate
|
||||
import kotlin.script.experimental.jvmhost.createJvmEvaluationConfigurationFromTemplate
|
||||
|
||||
class Executor(val context: RootContext = RootContext()) : Logging {
|
||||
private val compilationConfiguration = createJvmCompilationConfigurationFromTemplate<HefbrugScript>()
|
||||
|
||||
fun run(name: String) {
|
||||
val location = Location.File(Paths.get(name).toRealPath().toString())
|
||||
val evaluationConfiguration = createJvmEvaluationConfigurationFromTemplate<HefbrugScript> {
|
||||
constructorArgs(context, location)
|
||||
implicitReceivers(SourceContext(context, location, this@Executor))
|
||||
}
|
||||
|
||||
val diags =
|
||||
BasicJvmScriptingHost().eval(
|
||||
File(location.path).toScriptSource(),
|
||||
compilationConfiguration,
|
||||
evaluationConfiguration
|
||||
)
|
||||
|
||||
for (d in diags.reports) {
|
||||
|
||||
log(
|
||||
when (d.severity) {
|
||||
ScriptDiagnostic.Severity.DEBUG -> Level.forName("SCRIPT", 700)
|
||||
ScriptDiagnostic.Severity.INFO -> Level.DEBUG
|
||||
ScriptDiagnostic.Severity.WARNING -> Level.INFO
|
||||
ScriptDiagnostic.Severity.ERROR -> Level.ERROR
|
||||
ScriptDiagnostic.Severity.FATAL -> Level.FATAL
|
||||
},
|
||||
MarkerManager.getMarker("script"),
|
||||
"[@|blue ${location.path}|@] ${d.message} ${d.location ?: ""}"
|
||||
)
|
||||
|
||||
if (d.exception != null) {
|
||||
d.exception!!.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getScope(): SelectorScope =
|
||||
Register[context.contextUUID].selectorScope
|
||||
|
||||
init {
|
||||
Register.register(Register(context.contextUUID))
|
||||
}
|
||||
}
|
42
src/main/kotlin/me/eater/hefbrug/executor/GraphBuilder.kt
Normal file
42
src/main/kotlin/me/eater/hefbrug/executor/GraphBuilder.kt
Normal file
|
@ -0,0 +1,42 @@
|
|||
package me.eater.hefbrug.executor
|
||||
|
||||
import me.eater.hefbrug.definition.AbstractDefinition
|
||||
import me.eater.hefbrug.definition.DefinitionKey
|
||||
import me.eater.hefbrug.definition.DefinitionSkeleton
|
||||
|
||||
object GraphBuilder {
|
||||
fun makeGraph(input: Map<DefinitionKey, DefinitionSkeleton<*, *>>): List<Set<DefinitionKey>> {
|
||||
val dependencies = mutableMapOf<DefinitionKey, MutableSet<DefinitionKey>>()
|
||||
|
||||
for ((key, definition) in input) {
|
||||
definition.before.forEach {
|
||||
dependencies.getOrPut(it, ::mutableSetOf)
|
||||
.add(key)
|
||||
}
|
||||
|
||||
definition.after.forEach {
|
||||
dependencies.getOrPut(key, ::mutableSetOf)
|
||||
.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
val todo = input.keys.toMutableSet()
|
||||
val fulfilled = mutableSetOf<DefinitionKey>()
|
||||
val graphLayers = mutableListOf<Set<DefinitionKey>>()
|
||||
|
||||
while (todo.isNotEmpty()) {
|
||||
val nextLayer = mutableSetOf<DefinitionKey>()
|
||||
for (item in todo) {
|
||||
if (dependencies[item]?.all { it in fulfilled } != false) {
|
||||
nextLayer.add(item)
|
||||
}
|
||||
}
|
||||
|
||||
fulfilled += nextLayer
|
||||
todo -= nextLayer
|
||||
graphLayers.add(nextLayer)
|
||||
}
|
||||
|
||||
return graphLayers
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package me.eater.hefbrug.executor
|
||||
|
||||
import me.eater.hefbrug.dsl.context.SourceContext
|
||||
import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.jvm.dependenciesFromCurrentContext
|
||||
import kotlin.script.experimental.jvm.jvm
|
||||
|
||||
class HefbrugCompilationConfiguration : ScriptCompilationConfiguration({
|
||||
defaultImports(
|
||||
listOf(
|
||||
// Default kotlin imports
|
||||
"kotlin.*",
|
||||
"kotlin.annotations.*",
|
||||
"kotlin.collections.*",
|
||||
"kotlin.comparisons.*",
|
||||
"kotlin.io.*",
|
||||
"kotlin.ranges.*",
|
||||
"kotlin.sequences.*",
|
||||
"kotlin.text.*",
|
||||
"kotlin.jvm.*",
|
||||
"java.lang.*",
|
||||
|
||||
|
||||
// Own imports
|
||||
"me.eater.hefbrug.dsl.context.utils.*",
|
||||
"me.eater.hefbrug.state.ExistenceStatus.*",
|
||||
"me.eater.hefbrug.dsl.annotation.include"
|
||||
)
|
||||
)
|
||||
displayName("Hefbrug definition script")
|
||||
baseClass(HefbrugScript::class)
|
||||
implicitReceivers(SourceContext::class)
|
||||
|
||||
|
||||
jvm {
|
||||
dependenciesFromCurrentContext(wholeClasspath = true)
|
||||
}
|
||||
|
||||
ide {
|
||||
acceptedLocations(
|
||||
ScriptAcceptedLocation.Everywhere,
|
||||
ScriptAcceptedLocation.Project,
|
||||
ScriptAcceptedLocation.Sources
|
||||
)
|
||||
}
|
||||
})
|
|
@ -0,0 +1,9 @@
|
|||
package me.eater.hefbrug.executor
|
||||
|
||||
import kotlin.script.experimental.api.ScriptEvaluationConfiguration
|
||||
import kotlin.script.experimental.api.constructorArgs
|
||||
import kotlin.script.experimental.api.enableScriptsInstancesSharing
|
||||
|
||||
class HefbrugEvaluationConfiguration : ScriptEvaluationConfiguration({
|
||||
enableScriptsInstancesSharing()
|
||||
})
|
21
src/main/kotlin/me/eater/hefbrug/executor/HefbrugScript.kt
Normal file
21
src/main/kotlin/me/eater/hefbrug/executor/HefbrugScript.kt
Normal file
|
@ -0,0 +1,21 @@
|
|||
@file:Suppress("UNUSED")
|
||||
|
||||
package me.eater.hefbrug.executor
|
||||
|
||||
import me.eater.hefbrug.definition.impl.PackageDefinition
|
||||
import me.eater.hefbrug.definition.impl.ServiceDefinition
|
||||
import me.eater.hefbrug.dsl.Location
|
||||
import me.eater.hefbrug.dsl.context.RootContext
|
||||
import kotlin.script.experimental.annotations.KotlinScript
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@KotlinScript(
|
||||
displayName = "Hefbrug definition script",
|
||||
fileExtension = "hb.kts",
|
||||
compilationConfiguration = HefbrugCompilationConfiguration::class,
|
||||
evaluationConfiguration = HefbrugEvaluationConfiguration::class
|
||||
)
|
||||
abstract class HefbrugScript(root: RootContext, location: Location) {
|
||||
val sv = ServiceDefinition.KeyHelper
|
||||
val pkg = PackageDefinition.KeyHelper
|
||||
}
|
18
src/main/kotlin/me/eater/hefbrug/executor/StateCollector.kt
Normal file
18
src/main/kotlin/me/eater/hefbrug/executor/StateCollector.kt
Normal file
|
@ -0,0 +1,18 @@
|
|||
package me.eater.hefbrug.executor
|
||||
|
||||
import me.eater.hefbrug.collector.AbstractCollector
|
||||
import me.eater.hefbrug.definition.DefinitionWildcard
|
||||
import me.eater.hefbrug.dsl.context.extension_util.FactoryRegister
|
||||
import me.eater.hefbrug.state.AbstractState
|
||||
|
||||
class StateCollector(val context: ExecutionContext) {
|
||||
private val collectors = mutableMapOf<String, AbstractCollector<*, *>>()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
suspend fun <S : AbstractState> collect(definition: DefinitionWildcard<S>): S {
|
||||
val collector = collectors.getOrPut(
|
||||
definition.key.group,
|
||||
{ FactoryRegister[definition.key.group].createCollector(context) }) as AbstractCollector<S, DefinitionWildcard<S>>
|
||||
return collector.collect(definition)
|
||||
}
|
||||
}
|
42
src/main/kotlin/me/eater/hefbrug/executor/StateEnforcer.kt
Normal file
42
src/main/kotlin/me/eater/hefbrug/executor/StateEnforcer.kt
Normal file
|
@ -0,0 +1,42 @@
|
|||
package me.eater.hefbrug.executor
|
||||
|
||||
import me.eater.hefbrug.definition.DefinitionWildcard
|
||||
import me.eater.hefbrug.dsl.context.extension_util.FactoryRegister
|
||||
import me.eater.hefbrug.enforcer.AbstractEnforcer
|
||||
import me.eater.hefbrug.logging.Logging
|
||||
import me.eater.hefbrug.logging.message.Messages.foundDifference
|
||||
import me.eater.hefbrug.logging.message.Messages.noDifferences
|
||||
import me.eater.hefbrug.state.AbstractState
|
||||
|
||||
class StateEnforcer(private val context: ExecutionContext) : Logging {
|
||||
private val enforcers = mutableMapOf<String, AbstractEnforcer<*>>()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
suspend fun <S : AbstractState, D : DefinitionWildcard<S>> enforce(
|
||||
definition: D,
|
||||
currentState: S
|
||||
) {
|
||||
val enforcer = enforcers.getOrPut(
|
||||
definition.key.group,
|
||||
{ FactoryRegister[definition.key.group].createEnforcer(context) }) as AbstractEnforcer<S>
|
||||
|
||||
val diff = definition.state.diff(currentState)
|
||||
|
||||
if (diff.isNotEmpty())
|
||||
info(
|
||||
foundDifference(
|
||||
definition.key,
|
||||
context.node,
|
||||
mutableMapOf<String, Pair<Any?, Any?>>().apply {
|
||||
for (k in diff) {
|
||||
put(k, currentState[k].get() to definition.state[k].get())
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
else
|
||||
trace(noDifferences(definition.key, context.node))
|
||||
|
||||
return enforcer.enforce(currentState, definition.state, diff)
|
||||
}
|
||||
}
|
5
src/main/kotlin/me/eater/hefbrug/logging/LogFormat.kt
Normal file
5
src/main/kotlin/me/eater/hefbrug/logging/LogFormat.kt
Normal file
|
@ -0,0 +1,5 @@
|
|||
package me.eater.hefbrug.logging
|
||||
|
||||
interface LogFormat {
|
||||
fun logFormat(): String
|
||||
}
|
71
src/main/kotlin/me/eater/hefbrug/logging/LoggerConfig.kt
Normal file
71
src/main/kotlin/me/eater/hefbrug/logging/LoggerConfig.kt
Normal file
|
@ -0,0 +1,71 @@
|
|||
package me.eater.hefbrug.logging
|
||||
|
||||
import org.apache.logging.log4j.Level
|
||||
import org.apache.logging.log4j.core.LoggerContext
|
||||
import org.apache.logging.log4j.core.appender.ConsoleAppender
|
||||
import org.apache.logging.log4j.core.config.Configuration
|
||||
import org.apache.logging.log4j.core.config.ConfigurationFactory
|
||||
import org.apache.logging.log4j.core.config.ConfigurationSource
|
||||
import org.apache.logging.log4j.core.config.Order
|
||||
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory
|
||||
import org.apache.logging.log4j.core.config.plugins.Plugin
|
||||
import java.net.URI
|
||||
|
||||
@Plugin(name = "LoggerConfig", category = ConfigurationFactory.CATEGORY)
|
||||
@Order(50)
|
||||
class LoggerConfig : ConfigurationFactory() {
|
||||
fun getConfiguration(): Configuration {
|
||||
val builder = ConfigurationBuilderFactory.newConfigurationBuilder()
|
||||
builder.add(
|
||||
builder.newCustomLevel("SCRIPT", 700)
|
||||
)
|
||||
builder.setConfigurationName("HefbrugDefault")
|
||||
builder.setStatusLevel(Level.INFO)
|
||||
builder.add(
|
||||
builder.newAppender("Stdout", "CONSOLE")
|
||||
.addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT)
|
||||
.add(
|
||||
builder.newLayout("PatternLayout")
|
||||
.addAttribute(
|
||||
"pattern",
|
||||
"%blue{[%d{HH:mm:ss}]}%highlight{[%-5level]}{SCRIPT=white} %msg{ansi}%n"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
builder.add(
|
||||
builder.newRootLogger(
|
||||
when (System.getenv("HEFBRUG_LOG")?.toLowerCase()) {
|
||||
"all" -> Level.ALL
|
||||
"trace" -> Level.TRACE
|
||||
"debug" -> Level.DEBUG
|
||||
"warn" -> Level.WARN
|
||||
"error" -> Level.ERROR
|
||||
"fatal" -> Level.FATAL
|
||||
"script" -> Level.forName("SCRIPT", 700)
|
||||
"off" -> Level.OFF
|
||||
else -> Level.INFO
|
||||
}
|
||||
)
|
||||
.add(builder.newAppenderRef("Stdout"))
|
||||
)
|
||||
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
override fun getConfiguration(loggerContext: LoggerContext, source: ConfigurationSource): Configuration =
|
||||
getConfiguration()
|
||||
|
||||
override fun getConfiguration(loggerContext: LoggerContext, name: String, configLocation: URI?): Configuration =
|
||||
getConfiguration()
|
||||
|
||||
override fun getConfiguration(
|
||||
loggerContext: LoggerContext,
|
||||
name: String,
|
||||
configLocation: URI?,
|
||||
loader: ClassLoader?
|
||||
) = getConfiguration()
|
||||
|
||||
override fun getSupportedTypes(): Array<String> = arrayOf("*")
|
||||
|
||||
}
|
106
src/main/kotlin/me/eater/hefbrug/logging/Logging.kt
Normal file
106
src/main/kotlin/me/eater/hefbrug/logging/Logging.kt
Normal file
|
@ -0,0 +1,106 @@
|
|||
package me.eater.hefbrug.logging
|
||||
|
||||
import org.apache.logging.log4j.Level
|
||||
import org.apache.logging.log4j.Marker
|
||||
import org.apache.logging.log4j.kotlin.Logging
|
||||
import org.apache.logging.log4j.message.EntryMessage
|
||||
import org.apache.logging.log4j.message.Message
|
||||
|
||||
@Suppress("UNUSED")
|
||||
interface Logging : Logging {
|
||||
fun log(level: Level, marker: Marker, msg: Message) = logger.log(level, marker, msg)
|
||||
fun log(level: Level, marker: Marker, msg: Message, t: Throwable?) = logger.log(level, marker, msg, t)
|
||||
fun log(level: Level, marker: Marker, msg: CharSequence) = logger.log(level, marker, msg)
|
||||
fun log(level: Level, marker: Marker, msg: CharSequence, t: Throwable?) = logger.log(level, marker, msg, t)
|
||||
fun log(level: Level, marker: Marker, msg: Any) = logger.log(level, marker, msg)
|
||||
fun log(level: Level, marker: Marker, msg: Any, t: Throwable?) = logger.log(level, marker, msg, t)
|
||||
fun log(level: Level, msg: Message) = logger.log(level, msg)
|
||||
fun log(level: Level, msg: Message, t: Throwable?) = logger.log(level, msg, t)
|
||||
fun log(level: Level, msg: CharSequence) = logger.log(level, msg)
|
||||
fun log(level: Level, msg: CharSequence, t: Throwable?) = logger.log(level, msg, t)
|
||||
fun log(level: Level, msg: Any) = logger.log(level, msg)
|
||||
fun log(level: Level, msg: Any, t: Throwable?) = logger.log(level, msg, t)
|
||||
fun log(level: Level, supplier: () -> Any?) = logger.log(level, supplier)
|
||||
fun log(level: Level, t: Throwable, supplier: () -> Any?) = logger.log(level, t, supplier)
|
||||
fun log(level: Level, marker: Marker, supplier: () -> Any?) = logger.log(level, marker, supplier)
|
||||
fun log(level: Level, marker: Marker, t: Throwable?, supplier: () -> Any?) = logger.log(level, marker, t, supplier)
|
||||
fun trace(marker: Marker, msg: Message) = logger.trace(marker, msg)
|
||||
fun trace(marker: Marker, msg: Message, t: Throwable?) = logger.trace(marker, msg, t)
|
||||
fun trace(marker: Marker, msg: CharSequence) = logger.trace(marker, msg)
|
||||
fun trace(marker: Marker, msg: CharSequence, t: Throwable?) = logger.trace(marker, msg, t)
|
||||
fun trace(marker: Marker, msg: Any) = logger.trace(marker, msg)
|
||||
fun trace(marker: Marker, msg: Any, t: Throwable?) = logger.trace(marker, msg, t)
|
||||
fun trace(msg: Message) = logger.trace(msg)
|
||||
fun trace(msg: CharSequence) = logger.trace(msg)
|
||||
fun trace(msg: Any) = logger.trace(msg)
|
||||
fun trace(supplier: () -> Any?) = logger.trace(supplier)
|
||||
fun trace(marker: Marker, supplier: () -> Any?) = logger.trace(marker, supplier)
|
||||
fun trace(marker: Marker, t: Throwable?, supplier: () -> Any?) = logger.trace(marker, t, supplier)
|
||||
fun traceEntry(msg: CharSequence) = logger.traceEntry(msg)
|
||||
fun traceEntry(supplier: () -> CharSequence) = logger.traceEntry(supplier)
|
||||
fun traceEntry(vararg paramSuppliers: () -> Any?) = logger.traceEntry(*paramSuppliers)
|
||||
fun traceEntry(vararg params: Any?) = logger.traceEntry(*params)
|
||||
fun traceEntry(message: Message) = logger.traceEntry(message)
|
||||
fun <R : Any?> runInTrace(block: () -> R): R = logger.runInTrace(block)
|
||||
fun <R : Any?> runInTrace(entryMessage: EntryMessage, block: () -> R): R = logger.runInTrace(entryMessage, block)
|
||||
fun debug(marker: Marker, msg: Message) = logger.debug(marker, msg)
|
||||
fun debug(marker: Marker, msg: Message, t: Throwable?) = logger.debug(marker, msg, t)
|
||||
fun debug(marker: Marker, msg: CharSequence) = logger.debug(marker, msg)
|
||||
fun debug(marker: Marker, msg: CharSequence, t: Throwable?) = logger.debug(marker, msg, t)
|
||||
fun debug(marker: Marker, msg: Any) = logger.debug(marker, msg)
|
||||
fun debug(marker: Marker, msg: Any, t: Throwable?) = logger.debug(marker, msg, t)
|
||||
fun debug(msg: Message) = logger.debug(msg)
|
||||
fun debug(msg: CharSequence) = logger.debug(msg)
|
||||
fun debug(msg: Any) = logger.debug(msg)
|
||||
fun debug(supplier: () -> Any?) = logger.debug(supplier)
|
||||
fun debug(marker: Marker, supplier: () -> Any?) = logger.debug(marker, supplier)
|
||||
fun debug(marker: Marker, t: Throwable?, supplier: () -> Any?) = logger.debug(marker, t, supplier)
|
||||
fun info(marker: Marker, msg: Message) = logger.info(marker, msg)
|
||||
fun info(marker: Marker, msg: Message, t: Throwable?) = logger.info(marker, msg, t)
|
||||
fun info(marker: Marker, msg: CharSequence) = logger.info(marker, msg)
|
||||
fun info(marker: Marker, msg: CharSequence, t: Throwable?) = logger.info(marker, msg, t)
|
||||
fun info(marker: Marker, msg: Any) = logger.info(marker, msg)
|
||||
fun info(marker: Marker, msg: Any, t: Throwable?) = logger.info(marker, msg, t)
|
||||
fun info(msg: Message) = logger.info(msg)
|
||||
fun info(msg: CharSequence) = logger.info(msg)
|
||||
fun info(msg: Any) = logger.info(msg)
|
||||
fun info(supplier: () -> Any?) = logger.info(supplier)
|
||||
fun info(marker: Marker, supplier: () -> Any?) = logger.info(marker, supplier)
|
||||
fun info(marker: Marker, t: Throwable?, supplier: () -> Any?) = logger.info(marker, t, supplier)
|
||||
fun warn(marker: Marker, msg: Message) = logger.warn(marker, msg)
|
||||
fun warn(marker: Marker, msg: Message, t: Throwable?) = logger.warn(marker, msg, t)
|
||||
fun warn(marker: Marker, msg: CharSequence) = logger.warn(marker, msg)
|
||||
fun warn(marker: Marker, msg: CharSequence, t: Throwable?) = logger.warn(marker, msg, t)
|
||||
fun warn(marker: Marker, msg: Any) = logger.warn(marker, msg)
|
||||
fun warn(marker: Marker, msg: Any, t: Throwable?) = logger.warn(marker, msg, t)
|
||||
fun warn(msg: Message) = logger.warn(msg)
|
||||
fun warn(msg: CharSequence) = logger.warn(msg)
|
||||
fun warn(msg: Any) = logger.warn(msg)
|
||||
fun warn(supplier: () -> Any?) = logger.warn(supplier)
|
||||
fun warn(marker: Marker, supplier: () -> Any?) = logger.warn(marker, supplier)
|
||||
fun warn(marker: Marker, t: Throwable?, supplier: () -> Any?) = logger.warn(marker, t, supplier)
|
||||
fun error(marker: Marker, msg: Message) = logger.error(marker, msg)
|
||||
fun error(marker: Marker, msg: Message, t: Throwable?) = logger.error(marker, msg, t)
|
||||
fun error(marker: Marker, msg: CharSequence) = logger.error(marker, msg)
|
||||
fun error(marker: Marker, msg: CharSequence, t: Throwable?) = logger.error(marker, msg, t)
|
||||
fun error(marker: Marker, msg: Any) = logger.error(marker, msg)
|
||||
fun error(marker: Marker, msg: Any, t: Throwable?) = logger.error(marker, msg, t)
|
||||
fun error(msg: Message) = logger.error(msg)
|
||||
fun error(msg: CharSequence) = logger.error(msg)
|
||||
fun error(msg: Any) = logger.error(msg)
|
||||
fun error(supplier: () -> Any?) = logger.error(supplier)
|
||||
fun error(marker: Marker, supplier: () -> Any?) = logger.error(marker, supplier)
|
||||
fun error(marker: Marker, t: Throwable?, supplier: () -> Any?) = logger.error(marker, t, supplier)
|
||||
fun fatal(marker: Marker, msg: Message) = logger.fatal(marker, msg)
|
||||
fun fatal(marker: Marker, msg: Message, t: Throwable?) = logger.fatal(marker, msg, t)
|
||||
fun fatal(marker: Marker, msg: CharSequence) = logger.fatal(marker, msg)
|
||||
fun fatal(marker: Marker, msg: CharSequence, t: Throwable?) = logger.fatal(marker, msg, t)
|
||||
fun fatal(marker: Marker, msg: Any) = logger.fatal(marker, msg)
|
||||
fun fatal(marker: Marker, msg: Any, t: Throwable?) = logger.fatal(marker, msg, t)
|
||||
fun fatal(msg: Message) = logger.fatal(msg)
|
||||
fun fatal(msg: CharSequence) = logger.fatal(msg)
|
||||
fun fatal(msg: Any) = logger.fatal(msg)
|
||||
fun fatal(supplier: () -> Any?) = logger.fatal(supplier)
|
||||
fun fatal(marker: Marker, supplier: () -> Any?) = logger.fatal(marker, supplier)
|
||||
fun fatal(marker: Marker, t: Throwable?, supplier: () -> Any?) = logger.fatal(marker, t, supplier)
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package me.eater.hefbrug.logging.message
|
||||
|
||||
import me.eater.hefbrug.utils.handlebars
|
||||
import org.apache.logging.log4j.message.Message
|
||||
import org.apache.logging.log4j.util.StringBuilderFormattable
|
||||
|
||||
open class HefbrugMessage(private val message: String, private val items: Map<String, Any?>) : Message,
|
||||
StringBuilderFormattable {
|
||||
constructor(message: String, vararg items: Pair<String, Any?>) : this(message, items.toMap())
|
||||
|
||||
override fun getThrowable(): Throwable? = null
|
||||
override fun getParameters(): Array<Any> {
|
||||
return items.entries.toTypedArray()
|
||||
}
|
||||
|
||||
override fun getFormattedMessage(): String {
|
||||
return format.handlebars(items)
|
||||
}
|
||||
|
||||
override fun getFormat(): String {
|
||||
return message
|
||||
}
|
||||
|
||||
override fun formatTo(buffer: StringBuilder) {
|
||||
buffer.append(formattedMessage)
|
||||
}
|
||||
}
|
60
src/main/kotlin/me/eater/hefbrug/logging/message/messages.kt
Normal file
60
src/main/kotlin/me/eater/hefbrug/logging/message/messages.kt
Normal file
|
@ -0,0 +1,60 @@
|
|||
package me.eater.hefbrug.logging.message
|
||||
|
||||
import me.eater.hefbrug.access.ExecutionOutput
|
||||
import me.eater.hefbrug.definition.DefinitionKey
|
||||
import me.eater.hefbrug.logging.LogFormat
|
||||
import me.eater.hefbrug.node.Node
|
||||
import me.eater.hefbrug.utils.escape
|
||||
import me.eater.hefbrug.utils.handlebars
|
||||
|
||||
object Messages {
|
||||
|
||||
fun collectingCurrentState(key: DefinitionKey, node: Node) =
|
||||
HefbrugMessage("Collecting current state for {{key}} for {{node}}", "key" to key, "node" to node)
|
||||
|
||||
|
||||
fun applying(key: DefinitionKey, node: Node) =
|
||||
HefbrugMessage("Applying {{key}} for {{node}}", "key" to key, "node" to node)
|
||||
|
||||
fun startEnforcing(key: DefinitionKey, node: Node) =
|
||||
HefbrugMessage("Enforcing desired state for {{key}} for {{node}}", "key" to key, "node" to node)
|
||||
|
||||
fun foundDifference(key: DefinitionKey, node: Node, differences: Map<String, Pair<Any?, Any?>>) =
|
||||
HefbrugMessage(
|
||||
"enforcing {{key}} on {{node}} to desired state {{diff}}",
|
||||
"key" to key,
|
||||
"node" to node,
|
||||
"diff" to Diff(differences)
|
||||
)
|
||||
|
||||
fun noDifferences(key: DefinitionKey, node: Node) =
|
||||
HefbrugMessage(
|
||||
"{{key}} on {{node}} is in desired state",
|
||||
"key" to key,
|
||||
"node" to node
|
||||
)
|
||||
|
||||
fun failedDefinition(
|
||||
key: DefinitionKey,
|
||||
node: Node,
|
||||
executionOutput: ExecutionOutput
|
||||
) =
|
||||
HefbrugMessage(
|
||||
"Failed enforcing state {{key}} for {{node}} with command [@|yellow {{cmd}}|@] with exit code @|red {{exitcode}}|@",
|
||||
"key" to key,
|
||||
"node" to node,
|
||||
"cmd" to executionOutput.command.command.joinToString(" ") { it.escape() },
|
||||
"exitcode" to executionOutput.exitCode,
|
||||
"execution" to executionOutput
|
||||
)
|
||||
|
||||
|
||||
class Diff(val body: Map<String, Pair<Any?, Any?>>) : LogFormat {
|
||||
override fun logFormat(): String {
|
||||
return "[${body.map { (k, v) ->
|
||||
val (old, new) = v
|
||||
"@|yellow $k|@: @|red {{old}}|@ => @|green {{new}}|@".handlebars("old" to old, "new" to new)
|
||||
}.joinToString(", ")}]"
|
||||
}
|
||||
}
|
||||
}
|
10
src/main/kotlin/me/eater/hefbrug/module/Module.kt
Normal file
10
src/main/kotlin/me/eater/hefbrug/module/Module.kt
Normal file
|
@ -0,0 +1,10 @@
|
|||
package me.eater.hefbrug.module
|
||||
|
||||
import me.eater.hefbrug.dsl.context.ModuleContext
|
||||
import java.util.*
|
||||
|
||||
class Module(val id: String, private val contextUUID: UUID) {
|
||||
private val blocks: MutableSet<suspend ModuleContext.() -> Unit> = mutableSetOf()
|
||||
fun getContext(runtimeUUID: UUID, arguments: Map<String, Any?>): ModuleContext = ModuleContext(runtimeUUID, contextUUID, this, arguments)
|
||||
fun addBlock(block: suspend ModuleContext.() -> Unit) = blocks.add(block)
|
||||
}
|
11
src/main/kotlin/me/eater/hefbrug/node/Node.kt
Normal file
11
src/main/kotlin/me/eater/hefbrug/node/Node.kt
Normal file
|
@ -0,0 +1,11 @@
|
|||
package me.eater.hefbrug.node
|
||||
|
||||
import me.eater.hefbrug.logging.LogFormat
|
||||
|
||||
data class Node(val hostname: String) : LogFormat {
|
||||
val groups = mutableSetOf<String>()
|
||||
|
||||
|
||||
override fun toString() = "Node[$hostname,groups={${groups.joinToString(",")}}]"
|
||||
override fun logFormat() = "@|green $this|@"
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package me.eater.hefbrug.platform_utils
|
||||
|
||||
import me.eater.hefbrug.access.AccessSkeleton
|
||||
import me.eater.hefbrug.executor.ExecutionContext
|
||||
import me.eater.hefbrug.logging.Logging
|
||||
|
||||
interface PlatformUtil<out M> {
|
||||
suspend fun isSupported(access: AccessSkeleton): Boolean
|
||||
suspend fun getManager(context: ExecutionContext): M
|
||||
|
||||
abstract class Provider<M: Any>(val name: String, vararg manager: PlatformUtil<M>) : Logging {
|
||||
private val managers: MutableSet<PlatformUtil<M>> = mutableSetOf(*manager)
|
||||
|
||||
suspend fun getManager(context: ExecutionContext): M {
|
||||
val manager = managers.find {
|
||||
debug("Trying $name ${it.javaClass.name}")
|
||||
it.isSupported(context.roAccess)
|
||||
}?.getManager(context)
|
||||
?: throw RuntimeException("Couldn't find suitable $name for this system")
|
||||
|
||||
debug("Selected $name ${manager.javaClass.name}")
|
||||
|
||||
return manager
|
||||
}
|
||||
|
||||
|
||||
fun register(packageManager: PlatformUtil<M>) {
|
||||
managers.add(packageManager)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package me.eater.hefbrug.platform_utils.`package`
|
||||
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import me.eater.hefbrug.executor.ExecutionContext
|
||||
import me.eater.hefbrug.platform_utils.PlatformUtil
|
||||
|
||||
abstract class PackageManager(protected val context: ExecutionContext) {
|
||||
protected val ro = context.roAccess
|
||||
protected val rw = context.rwAccess
|
||||
private var hasSynced = false
|
||||
private var syncLock = Mutex()
|
||||
|
||||
abstract suspend fun isInstalled(name: String): Boolean
|
||||
abstract suspend fun install(name: String)
|
||||
abstract suspend fun remove(name: String)
|
||||
protected abstract suspend fun sync()
|
||||
|
||||
suspend fun sync(force: Boolean = false) {
|
||||
syncLock.withLock {
|
||||
if (!hasSynced || force) {
|
||||
sync()
|
||||
hasSynced = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract suspend fun upgrade(name: String)
|
||||
|
||||
companion object : PlatformUtil.Provider<PackageManager>(
|
||||
"package manager",
|
||||
XbpsManager.Util
|
||||
)
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package me.eater.hefbrug.platform_utils.`package`
|
||||
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import me.eater.hefbrug.access.AccessSkeleton
|
||||
import me.eater.hefbrug.executor.ExecutionContext
|
||||
import me.eater.hefbrug.platform_utils.PlatformUtil
|
||||
|
||||
class XbpsManager(context: ExecutionContext) : PackageManager(context) {
|
||||
private val executionLock = Mutex()
|
||||
|
||||
override suspend fun isInstalled(name: String): Boolean {
|
||||
|
||||
return executionLock.withLock {
|
||||
ro.execute("xbps-query", name).exitCode == 0
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun install(name: String) {
|
||||
executionLock.withLock {
|
||||
rw.execute("xbps-install", "-y", name).orThrow()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun remove(name: String) {
|
||||
executionLock.withLock {
|
||||
rw.execute("xbps-remove", "-y", name).orThrow()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun sync() {
|
||||
executionLock.withLock {
|
||||
rw.execute("xbps-install", "-S").orThrow()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun upgrade(name: String) {
|
||||
executionLock.withLock {
|
||||
rw.execute("xbps-install", "-uy", name).orThrow()
|
||||
}
|
||||
}
|
||||
|
||||
object Util : PlatformUtil<XbpsManager> {
|
||||
override suspend fun isSupported(access: AccessSkeleton): Boolean {
|
||||
return access.execute("which", "xbps-install").exitCode == 0 &&
|
||||
access.execute("which", "xbps-remove").exitCode == 0
|
||||
}
|
||||
|
||||
override suspend fun getManager(context: ExecutionContext) = XbpsManager(context)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package me.eater.hefbrug.platform_utils.service
|
||||
|
||||
import me.eater.hefbrug.access.AccessSkeleton
|
||||
import me.eater.hefbrug.access.FileType
|
||||
import me.eater.hefbrug.executor.ExecutionContext
|
||||
import me.eater.hefbrug.platform_utils.PlatformUtil
|
||||
|
||||
class RunitManager(context: ExecutionContext) : ServiceManager(context) {
|
||||
|
||||
override suspend fun isRunning(name: String): Boolean {
|
||||
return ro.execute("sv", "status", name).stdout.startsWith("run:")
|
||||
}
|
||||
|
||||
override suspend fun isEnabled(name: String): Boolean {
|
||||
return ro.exists("/var/service/$name", FileType.Directory)
|
||||
}
|
||||
|
||||
override suspend fun hasAutoStart(name: String): Boolean {
|
||||
return !ro.exists("/etc/sv/$name/stop")
|
||||
}
|
||||
|
||||
override suspend fun setState(name: String, enabled: Boolean, running: Boolean, autostart: Boolean) {
|
||||
val serviceConf = "/etc/sv/$name"
|
||||
val stopFile = "$serviceConf/stop"
|
||||
val activeService = "/var/service/$name"
|
||||
|
||||
if (!autostart) {
|
||||
rw.execute("touch", stopFile).orThrow()
|
||||
} else if (!hasAutoStart(name)) {
|
||||
rw.execute("rm", stopFile).orThrow()
|
||||
}
|
||||
|
||||
val isEnabled = isEnabled(name)
|
||||
|
||||
if (isEnabled && !enabled) {
|
||||
rw.execute("rm", activeService).orThrow()
|
||||
}
|
||||
|
||||
if (!isEnabled && (enabled || running)) {
|
||||
rw.execute("ln", "-s", serviceConf, "/var/service").orThrow()
|
||||
}
|
||||
|
||||
val isRunning = isRunning(name)
|
||||
|
||||
if (isRunning && !running) {
|
||||
rw.execute("sv", "stop", name).orThrow()
|
||||
}
|
||||
|
||||
if (!isRunning && running) {
|
||||
rw.execute("sv", "start", name).orThrow()
|
||||
}
|
||||
}
|
||||
|
||||
object Util : PlatformUtil<RunitManager> {
|
||||
override suspend fun isSupported(access: AccessSkeleton): Boolean {
|
||||
return access.exists("/var/service", FileType.Directory) &&
|
||||
access.exists("/etc/sv", FileType.Directory) &&
|
||||
access.execute("which", "sv").exitCode == 0
|
||||
}
|
||||
|
||||
override suspend fun getManager(context: ExecutionContext) = RunitManager(context)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package me.eater.hefbrug.platform_utils.service
|
||||
|
||||
import me.eater.hefbrug.executor.ExecutionContext
|
||||
import me.eater.hefbrug.platform_utils.PlatformUtil
|
||||
|
||||
abstract class ServiceManager(protected val context: ExecutionContext) {
|
||||
protected val ro = context.roAccess
|
||||
protected val rw = context.rwAccess
|
||||
|
||||
abstract suspend fun isRunning(name: String): Boolean
|
||||
abstract suspend fun isEnabled(name: String): Boolean
|
||||
abstract suspend fun hasAutoStart(name: String): Boolean
|
||||
abstract suspend fun setState(
|
||||
name: String,
|
||||
enabled: Boolean,
|
||||
running: Boolean,
|
||||
autostart: Boolean = enabled && running
|
||||
)
|
||||
|
||||
companion object : PlatformUtil.Provider<ServiceManager>(
|
||||
"service manager",
|
||||
RunitManager.Util
|
||||
)
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package me.eater.hefbrug.platform_utils.user
|
||||
|
||||
import me.eater.hefbrug.executor.ExecutionContext
|
||||
import me.eater.hefbrug.platform_utils.PlatformUtil
|
||||
|
||||
abstract class UserManager(val context: ExecutionContext) {
|
||||
protected val ro = context.roAccess
|
||||
protected val rw = context.rwAccess
|
||||
|
||||
|
||||
companion object {
|
||||
private val managers: Set<PlatformUtil<UserManager>> = setOf(
|
||||
UsermodManager.Util
|
||||
)
|
||||
|
||||
suspend fun getManager(context: ExecutionContext): UserManager {
|
||||
return managers.find { it.isSupported(context.roAccess) }?.getManager(context)
|
||||
?: error("Can't find appropriate User Manager for your system")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package me.eater.hefbrug.platform_utils.user
|
||||
|
||||
import me.eater.hefbrug.access.AccessSkeleton
|
||||
import me.eater.hefbrug.executor.ExecutionContext
|
||||
import me.eater.hefbrug.platform_utils.PlatformUtil
|
||||
|
||||
class UsermodManager(context: ExecutionContext) : UserManager(context) {
|
||||
|
||||
|
||||
object Util : PlatformUtil<UsermodManager> {
|
||||
override suspend fun isSupported(access: AccessSkeleton): Boolean {
|
||||
return access.execute("which", "usermod").exitCode == 0
|
||||
}
|
||||
|
||||
override suspend fun getManager(context: ExecutionContext) = UsermodManager(context)
|
||||
}
|
||||
}
|
11
src/main/kotlin/me/eater/hefbrug/selector/AllSelector.kt
Normal file
11
src/main/kotlin/me/eater/hefbrug/selector/AllSelector.kt
Normal file
|
@ -0,0 +1,11 @@
|
|||
package me.eater.hefbrug.selector
|
||||
|
||||
import me.eater.hefbrug.node.Node
|
||||
|
||||
class AllSelector(private val selectors: Collection<SelectorInterface>) : SelectorInterface {
|
||||
override fun matches(node: Node) = selectors.all {
|
||||
it.matches(node)
|
||||
}
|
||||
|
||||
constructor(vararg selector: SelectorInterface) : this(selector.asList())
|
||||
}
|
7
src/main/kotlin/me/eater/hefbrug/selector/AndSelector.kt
Normal file
7
src/main/kotlin/me/eater/hefbrug/selector/AndSelector.kt
Normal file
|
@ -0,0 +1,7 @@
|
|||
package me.eater.hefbrug.selector
|
||||
|
||||
import me.eater.hefbrug.node.Node
|
||||
|
||||
class AndSelector(private val left: SelectorInterface, private val right: SelectorInterface) : SelectorInterface {
|
||||
override fun matches(node: Node): Boolean = left.matches(node) && right.matches(node)
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package me.eater.hefbrug.selector
|
||||
|
||||
import me.eater.hefbrug.node.Node
|
||||
|
||||
class GroupSelector(private val group: String) : SelectorInterface {
|
||||
override fun matches(node: Node): Boolean = node.groups.contains(group)
|
||||
}
|
42
src/main/kotlin/me/eater/hefbrug/selector/NodeSelector.kt
Normal file
42
src/main/kotlin/me/eater/hefbrug/selector/NodeSelector.kt
Normal file
|
@ -0,0 +1,42 @@
|
|||
package me.eater.hefbrug.selector
|
||||
|
||||
import me.eater.hefbrug.node.Node
|
||||
|
||||
class NodeSelector(private val selector: NameSelector) : SelectorInterface {
|
||||
override fun matches(node: Node): Boolean {
|
||||
return selector.match(node.hostname)
|
||||
}
|
||||
|
||||
constructor(selector: String) : this(
|
||||
if ("*" in selector) {
|
||||
NameSelector.RegexMatch(globToRegex(selector))
|
||||
} else {
|
||||
NameSelector.ExactMatch(selector)
|
||||
}
|
||||
)
|
||||
|
||||
constructor(selector: Regex) : this(NameSelector.RegexMatch(selector))
|
||||
|
||||
sealed class NameSelector {
|
||||
data class ExactMatch(val name: String) : NameSelector() {
|
||||
override fun match(name: String) = name == this.name
|
||||
}
|
||||
|
||||
data class RegexMatch(val regex: Regex) : NameSelector() {
|
||||
override fun match(name: String) = regex.matches(name)
|
||||
}
|
||||
|
||||
abstract fun match(name: String): Boolean
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun globToRegex(glob: String): Regex {
|
||||
val regexStr = glob
|
||||
.replace(Regex("""\*+""")) { """\E.+\Q""" }
|
||||
.replace(Regex("""\?+""")) { """\E[^\.]+\Q""" }
|
||||
.replace("""\Q\E""", "")
|
||||
|
||||
return Regex("""^\Q$regexStr\E$""", RegexOption.IGNORE_CASE)
|
||||
}
|
||||
}
|
||||
}
|
7
src/main/kotlin/me/eater/hefbrug/selector/NotSelector.kt
Normal file
7
src/main/kotlin/me/eater/hefbrug/selector/NotSelector.kt
Normal file
|
@ -0,0 +1,7 @@
|
|||
package me.eater.hefbrug.selector
|
||||
|
||||
import me.eater.hefbrug.node.Node
|
||||
|
||||
class NotSelector(private val selector: SelectorInterface) : SelectorInterface {
|
||||
override fun matches(node: Node): Boolean = !selector.matches(node)
|
||||
}
|
7
src/main/kotlin/me/eater/hefbrug/selector/OrSelector.kt
Normal file
7
src/main/kotlin/me/eater/hefbrug/selector/OrSelector.kt
Normal file
|
@ -0,0 +1,7 @@
|
|||
package me.eater.hefbrug.selector
|
||||
|
||||
import me.eater.hefbrug.node.Node
|
||||
|
||||
class OrSelector(private val left: SelectorInterface, private val right: SelectorInterface) : SelectorInterface {
|
||||
override fun matches(node: Node): Boolean = left.matches(node) || right.matches(node)
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package me.eater.hefbrug.selector
|
||||
|
||||
import me.eater.hefbrug.dsl.annotation.HefbrugDSL
|
||||
import me.eater.hefbrug.node.Node
|
||||
|
||||
@HefbrugDSL
|
||||
interface SelectorInterface {
|
||||
fun matches(node: Node): Boolean
|
||||
|
||||
operator fun not(): SelectorInterface = NotSelector(this)
|
||||
infix fun and(right: SelectorInterface): SelectorInterface = AndSelector(this, right)
|
||||
infix fun or(right: SelectorInterface): SelectorInterface = OrSelector(this, right)
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package me.eater.hefbrug.selector
|
||||
|
||||
import me.eater.hefbrug.node.Node
|
||||
|
||||
class TrueSelector : SelectorInterface {
|
||||
override fun matches(node: Node) = true
|
||||
}
|
75
src/main/kotlin/me/eater/hefbrug/state/AbstractState.kt
Normal file
75
src/main/kotlin/me/eater/hefbrug/state/AbstractState.kt
Normal file
|
@ -0,0 +1,75 @@
|
|||
package me.eater.hefbrug.state
|
||||
|
||||
import me.eater.hefbrug.state.property.ExtendedPropertyDelegate
|
||||
import me.eater.hefbrug.state.property.PropertyDelegate
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
abstract class AbstractState(val id: String) {
|
||||
val properties: MutableMap<String, PropertyDelegate<*>> = mutableMapOf()
|
||||
|
||||
protected fun <T> state(
|
||||
default: T? = null,
|
||||
required: Boolean = false,
|
||||
writeOnce: Boolean = false
|
||||
): PropertyDelegate<T> =
|
||||
state<T>(required, writeOnce) { default }
|
||||
|
||||
protected fun <T> extState(
|
||||
default: T? = null,
|
||||
required: Boolean = false,
|
||||
writeOnce: Boolean = false,
|
||||
equals: ExtendedPropertyDelegate.DiffScope<T>.() -> Boolean
|
||||
): ExtendedPropertyDelegate<T> =
|
||||
extState<T>(required, writeOnce, { default }, equals)
|
||||
|
||||
protected fun <T> state(required: Boolean = false, writeOnce: Boolean = false, default: () -> T?) =
|
||||
PropertyDelegate<T>(default, required, writeOnce)
|
||||
|
||||
protected fun <T> extState(
|
||||
required: Boolean = false,
|
||||
writeOnce: Boolean = false,
|
||||
default: () -> T?,
|
||||
equals: ExtendedPropertyDelegate.DiffScope<T>.() -> Boolean
|
||||
) =
|
||||
ExtendedPropertyDelegate<T>(default, required, writeOnce, equals)
|
||||
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
operator fun <T> get(property: KProperty<T>): PropertyDelegate<T> =
|
||||
(properties[property.name] as? PropertyDelegate<T>)!!
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
operator fun get(property: String): PropertyDelegate<*> =
|
||||
properties[property]!!
|
||||
|
||||
override fun toString(): String =
|
||||
"${this::class.simpleName ?: "State"}[id=$id] {${if (properties.isNotEmpty())
|
||||
"\n ${this.properties.map { (k, v) -> "[$k]: ${v.get()}" }.joinToString("\n ")}\n"
|
||||
else ""
|
||||
}}"
|
||||
|
||||
open fun diff(currentState: AbstractState): Set<String> {
|
||||
if (javaClass != currentState.javaClass) {
|
||||
error("When diffing states both should be the same kind of state")
|
||||
}
|
||||
|
||||
val changed = mutableSetOf<String>()
|
||||
for ((name, property) in properties) {
|
||||
val otherProperty = currentState.properties[name] ?: continue
|
||||
|
||||
if (property is ExtendedPropertyDelegate<*> && otherProperty is ExtendedPropertyDelegate<*>) {
|
||||
if (property.hasChanged(otherProperty)) {
|
||||
changed.add(name)
|
||||
}
|
||||
} else {
|
||||
if (property.get() != otherProperty.get()) {
|
||||
changed.add(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return changed
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package me.eater.hefbrug.state
|
||||
|
||||
enum class ExistenceStatus {
|
||||
Allow,
|
||||
Present,
|
||||
Absent
|
||||
}
|
35
src/main/kotlin/me/eater/hefbrug/state/impl/PackageState.kt
Normal file
35
src/main/kotlin/me/eater/hefbrug/state/impl/PackageState.kt
Normal file
|
@ -0,0 +1,35 @@
|
|||
package me.eater.hefbrug.state.impl
|
||||
|
||||
import me.eater.hefbrug.state.AbstractState
|
||||
import me.eater.hefbrug.state.ExistenceStatus
|
||||
import me.eater.hefbrug.state.ExistenceStatus.Allow
|
||||
|
||||
class PackageState(id: String) : AbstractState(id) {
|
||||
var upgrade: Boolean by extState(false) {
|
||||
if (status == Allow)
|
||||
true
|
||||
else
|
||||
left == right
|
||||
}
|
||||
var name: String by state(id, true)
|
||||
var status: ExistenceStatus by extState(Allow) {
|
||||
Allow == right || Allow == left || left == right
|
||||
}
|
||||
|
||||
/*override fun diff(currentState: AbstractState): Set<String> {
|
||||
if (currentState is PackageState) {
|
||||
return diff(currentState)
|
||||
}
|
||||
|
||||
return super.diff(currentState)
|
||||
}
|
||||
|
||||
fun diff(currentState: PackageState): Set<String> {
|
||||
val diff = super.diff(currentState)
|
||||
if ("upgrade" in diff && status == Allow && currentState.status == Absent) {
|
||||
return diff - "upgrade"
|
||||
}
|
||||
|
||||
return diff
|
||||
}*/
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue