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