mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-29 01:15:43 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ef52c8e92 | ||
|
|
1353f3bc82 | ||
|
|
aa5564c2c9 | ||
|
|
b81341ece4 |
@@ -285,6 +285,11 @@ jobs:
|
||||
env:
|
||||
EAGLE_IMAGE: ${{ needs.build-eagle.outputs.image_tag }}
|
||||
SHARDOK_IMAGE: ${{ needs.build-shardok.outputs.image_tag }}
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
GPT_MODEL_NAME: ${{ secrets.GPT_MODEL_NAME }}
|
||||
EAGLE_ENABLE_S3: ${{ secrets.EAGLE_ENABLE_S3 }}
|
||||
DO_SPACES_ACCESS_KEY: ${{ secrets.DO_SPACES_ACCESS_KEY }}
|
||||
DO_SPACES_SECRET_KEY: ${{ secrets.DO_SPACES_SECRET_KEY }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
@@ -305,11 +310,22 @@ jobs:
|
||||
username: deploy
|
||||
key: ${{ secrets.DO_SSH_KEY }}
|
||||
script_stop: true
|
||||
envs: EAGLE_IMAGE,SHARDOK_IMAGE
|
||||
envs: EAGLE_IMAGE,SHARDOK_IMAGE,OPENAI_API_KEY,GPT_MODEL_NAME,EAGLE_ENABLE_S3,DO_SPACES_ACCESS_KEY,DO_SPACES_SECRET_KEY
|
||||
script: |
|
||||
set -x
|
||||
cd /opt/eagle0
|
||||
|
||||
# Write env vars to .env file for docker-compose
|
||||
rm -f .env 2>/dev/null || true
|
||||
cat > .env << EOF
|
||||
OPENAI_API_KEY=${OPENAI_API_KEY:-}
|
||||
GPT_MODEL_NAME=${GPT_MODEL_NAME:-gpt-4o}
|
||||
EAGLE_ENABLE_S3=${EAGLE_ENABLE_S3:-false}
|
||||
DO_SPACES_ACCESS_KEY=${DO_SPACES_ACCESS_KEY:-}
|
||||
DO_SPACES_SECRET_KEY=${DO_SPACES_SECRET_KEY:-}
|
||||
EOF
|
||||
chmod 600 .env
|
||||
|
||||
# Login to registry
|
||||
echo "${{ secrets.DO_REGISTRY_TOKEN }}" | docker login registry.digitalocean.com -u "${{ secrets.DO_REGISTRY_TOKEN }}" --password-stdin
|
||||
|
||||
|
||||
@@ -20,6 +20,10 @@ services:
|
||||
- "40032:40032"
|
||||
environment:
|
||||
OPENAI_API_KEY: "${OPENAI_API_KEY:-}"
|
||||
EAGLE_ENABLE_S3: "${EAGLE_ENABLE_S3:-false}"
|
||||
DO_SPACES_ENDPOINT: "${DO_SPACES_ENDPOINT:-https://sfo3.digitaloceanspaces.com}"
|
||||
DO_SPACES_ACCESS_KEY: "${DO_SPACES_ACCESS_KEY:-}"
|
||||
DO_SPACES_SECRET_KEY: "${DO_SPACES_SECRET_KEY:-}"
|
||||
volumes:
|
||||
- ./saves:/app/saves
|
||||
depends_on:
|
||||
|
||||
@@ -314,6 +314,7 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/service/persistence:s3_persister",
|
||||
"//src/main/scala/net/eagle0/eagle/service/persistence:s3_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/service/persistence:save_directory",
|
||||
"//src/main/scala/net/eagle0/eagle/service/persistence/credentials",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -395,6 +396,7 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/service/persistence:s3_persister",
|
||||
"//src/main/scala/net/eagle0/eagle/service/persistence:s3_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/service/persistence:save_directory",
|
||||
"//src/main/scala/net/eagle0/eagle/service/persistence/credentials",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:battle_resolution",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:battle_update",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:battle_update_receiver",
|
||||
|
||||
@@ -8,11 +8,10 @@ import net.eagle0.eagle.service.persistence.{
|
||||
S3Utils,
|
||||
SaveDirectory
|
||||
}
|
||||
import net.eagle0.eagle.service.persistence.credentials.S3Credentials
|
||||
import net.eagle0.eagle.GameId
|
||||
|
||||
object LocalGamePersisterCreation extends GamePersisterCreation {
|
||||
private val includeS3 = false
|
||||
|
||||
private def localFilePersister(gameId: GameId) = LocalFilePersister(
|
||||
SaveDirectory.saveDirectoryForGame(gameId)
|
||||
)
|
||||
@@ -20,7 +19,7 @@ object LocalGamePersisterCreation extends GamePersisterCreation {
|
||||
S3Persister(S3Utils.mainBucketName, S3Utils.makeS3Prefix(gameId))
|
||||
|
||||
def persisterForGame(gameId: GameId): Persister =
|
||||
if includeS3 then
|
||||
if S3Credentials.isEnabled then
|
||||
CompoundPersister(
|
||||
Vector(localFilePersister(gameId), s3Persister(gameId))
|
||||
)
|
||||
|
||||
@@ -12,6 +12,7 @@ import net.eagle0.common.shardok_internal_interface.ShardokInternalInterfaceGrpc
|
||||
import net.eagle0.common.shardok_internal_interface.ShardokInternalInterfaceGrpc.ShardokInternalInterfaceStub
|
||||
import net.eagle0.eagle.service.new_game_creation.FixedNewGameCreation
|
||||
import net.eagle0.eagle.service.persistence.{CompoundPersister, LocalFilePersister, S3Persister, S3Utils, SaveDirectory}
|
||||
import net.eagle0.eagle.service.persistence.credentials.S3Credentials
|
||||
import net.eagle0.eagle.shardok_interface.ShardokInterfaceGrpcClient
|
||||
import net.eagle0.shardok.common.hex_map.HexMap
|
||||
|
||||
@@ -59,16 +60,19 @@ object ServerSetupHelpers {
|
||||
.get
|
||||
|
||||
val localPersister = LocalFilePersister(SaveDirectory.saveDirectory)
|
||||
val s3Persister =
|
||||
S3Persister(S3Utils.mainBucketName, S3Utils.runningGamesKeyPrefix)
|
||||
val _ = s3Persister
|
||||
|
||||
val persisters = if S3Credentials.isEnabled then {
|
||||
val s3Persister =
|
||||
S3Persister(S3Utils.mainBucketName, S3Utils.runningGamesKeyPrefix)
|
||||
Vector(localPersister, s3Persister)
|
||||
} else {
|
||||
Vector(localPersister)
|
||||
}
|
||||
|
||||
GamesManager(
|
||||
shardokInternalInterface = shardokInternalInterface,
|
||||
randomGenerator = new SecureRandom,
|
||||
persister = CompoundPersister(
|
||||
Vector(localPersister)
|
||||
),
|
||||
persister = CompoundPersister(persisters),
|
||||
gameCreation = FixedNewGameCreation,
|
||||
gamePersisterCreation = LocalGamePersisterCreation,
|
||||
gptModelName = gptModelName,
|
||||
|
||||
@@ -24,18 +24,19 @@ object S3Utils {
|
||||
|
||||
private def syncClient() = S3Client
|
||||
.builder()
|
||||
.endpointOverride(URI.create("https://sfo3.digitaloceanspaces.com"))
|
||||
.endpointOverride(URI.create(S3Credentials.endpoint))
|
||||
.region(Region.US_EAST_1)
|
||||
.credentialsProvider(S3Credentials.credentialsProvider)
|
||||
.build
|
||||
|
||||
private val transferManager: S3TransferManager =
|
||||
private lazy val transferManager: S3TransferManager =
|
||||
S3TransferManager.builder
|
||||
.s3Client(
|
||||
S3AsyncClient
|
||||
.builder()
|
||||
.endpointOverride(URI.create("https://sfo3.digitaloceanspaces.com"))
|
||||
.endpointOverride(URI.create(S3Credentials.endpoint))
|
||||
.region(Region.US_EAST_1)
|
||||
.credentialsProvider(S3Credentials.credentialsProvider)
|
||||
.build()
|
||||
)
|
||||
.build
|
||||
|
||||
@@ -4,7 +4,7 @@ scala_library(
|
||||
name = "credentials",
|
||||
srcs = ["S3Credentials.scala"],
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle/service/persistence:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/service:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"@maven//:software_amazon_awssdk_auth",
|
||||
|
||||
+57
-6
@@ -7,6 +7,15 @@ import scala.util.{Try, Using}
|
||||
|
||||
import software.amazon.awssdk.auth.credentials.{AwsBasicCredentials, AwsCredentialsProvider, StaticCredentialsProvider}
|
||||
|
||||
/**
|
||||
* Centralized S3/DO Spaces configuration.
|
||||
*
|
||||
* Environment variables:
|
||||
* - EAGLE_ENABLE_S3: Set to "true" to enable S3 persistence
|
||||
* - DO_SPACES_ENDPOINT: S3 endpoint URL (default: https://sfo3.digitaloceanspaces.com)
|
||||
* - DO_SPACES_ACCESS_KEY: Access key (falls back to ~/.s3cfg)
|
||||
* - DO_SPACES_SECRET_KEY: Secret key (falls back to ~/.s3cfg)
|
||||
*/
|
||||
object S3Credentials {
|
||||
private def makeS3cfgMap(): Try[Map[String, String]] = {
|
||||
val s3cfgFile = new File(System.getProperty("user.home"), ".s3cfg")
|
||||
@@ -32,12 +41,54 @@ object S3Credentials {
|
||||
}
|
||||
}
|
||||
|
||||
private val s3cfgMap = makeS3cfgMap().get
|
||||
/** Whether S3 persistence is enabled (EAGLE_ENABLE_S3=true and credentials available). */
|
||||
val isEnabled: Boolean = {
|
||||
val envValue = Option(System.getenv("EAGLE_ENABLE_S3"))
|
||||
.map(_.toLowerCase)
|
||||
.contains("true")
|
||||
|
||||
if envValue && !hasCredentials then {
|
||||
System.err.println(
|
||||
"WARNING: EAGLE_ENABLE_S3=true but no S3 credentials configured. S3 persistence disabled."
|
||||
)
|
||||
false
|
||||
} else envValue
|
||||
}
|
||||
|
||||
/** S3 endpoint URL. Defaults to DO Spaces SFO3 region. */
|
||||
val endpoint: String = Option(System.getenv("DO_SPACES_ENDPOINT"))
|
||||
.getOrElse("https://sfo3.digitaloceanspaces.com")
|
||||
|
||||
/** Check if S3 credentials are available (either from env vars or ~/.s3cfg). */
|
||||
private def hasCredentials: Boolean = {
|
||||
val hasEnvVars = Option(System.getenv("DO_SPACES_ACCESS_KEY")).isDefined &&
|
||||
Option(System.getenv("DO_SPACES_SECRET_KEY")).isDefined
|
||||
|
||||
val hasS3cfg = {
|
||||
val s3cfgFile = new File(System.getProperty("user.home"), ".s3cfg")
|
||||
s3cfgFile.exists()
|
||||
}
|
||||
|
||||
hasEnvVars || hasS3cfg
|
||||
}
|
||||
|
||||
/** Lazily load credentials from environment variables or ~/.s3cfg file. */
|
||||
lazy val credentialsProvider: AwsCredentialsProvider = {
|
||||
val accessKey = Option(System.getenv("DO_SPACES_ACCESS_KEY"))
|
||||
val secretKey = Option(System.getenv("DO_SPACES_SECRET_KEY"))
|
||||
|
||||
val credentials = (accessKey, secretKey) match {
|
||||
case (Some(ak), Some(sk)) =>
|
||||
AwsBasicCredentials.create(ak, sk)
|
||||
case _ =>
|
||||
// Fall back to ~/.s3cfg file
|
||||
val s3cfgMap = makeS3cfgMap().get
|
||||
AwsBasicCredentials.create(
|
||||
s3cfgMap("access_key"),
|
||||
s3cfgMap("secret_key")
|
||||
)
|
||||
}
|
||||
|
||||
private val credentials = AwsBasicCredentials.create(
|
||||
s3cfgMap("access_key"),
|
||||
s3cfgMap("secret_key")
|
||||
)
|
||||
val credentialsProvider: AwsCredentialsProvider =
|
||||
StaticCredentialsProvider.create(credentials)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user