diff --git a/.github/workflows/native-image-tests.yml b/.github/workflows/native-image-tests.yml index 21f5af9f..609e7296 100644 --- a/.github/workflows/native-image-tests.yml +++ b/.github/workflows/native-image-tests.yml @@ -43,14 +43,12 @@ jobs: run: |- sbt "publishLocal; publishM2" - #- name: Akka Management native image test app build - # run: |- - # cd native-image-tests/grpc-scala - # sbt nativeImage -Dakka.grpc.version=`cat ~/.version` - # # run the binary, netty client backend - # target/native-image/grpc-scala - # # akka-http client backend - # target/native-image/grpc-scala akka-http-client + - name: Akka Management native image test app build + run: |- + cd native-image-tests + sbt nativeImage -Dakka.management.version=`cat ~/.version` + # run the binary to see it can bootstrap a cluster + target/native-image/native-image-tests - name: Email on failure if: ${{ failure() }} diff --git a/docs/src/main/paradox/native-image.md b/docs/src/main/paradox/native-image.md index 3078f7f4..374e6148 100644 --- a/docs/src/main/paradox/native-image.md +++ b/docs/src/main/paradox/native-image.md @@ -5,9 +5,9 @@ Building native images with Akka HTTP is supported out of the box for the follow * akka-management * akka-management-cluster-bootstrap * akka-management-cluster-http - * akka-management-discovery-kubernetes-api + * akka-discovery-kubernetes-api * akka-lease-kubernetes - * akka-rolling-upgrade-kubernetes + * akka-rolling-update-kubernetes Other modules can likely be used but will require figuring out and adding additional native-image metadata. diff --git a/native-image-tests/.gitignore b/native-image-tests/.gitignore new file mode 100644 index 00000000..c4ddf444 --- /dev/null +++ b/native-image-tests/.gitignore @@ -0,0 +1,12 @@ +target/ + +.settings +.project +.classpath + +.idea +*.iml + +.metals +.bloop +.bsp diff --git a/native-image-tests/build.sbt b/native-image-tests/build.sbt new file mode 100644 index 00000000..1af47ce0 --- /dev/null +++ b/native-image-tests/build.sbt @@ -0,0 +1,51 @@ +name := "native-image-tests" + +version := "1.0" + +scalaVersion := s"2.13.12" + +resolvers += "Akka library repository".at("https://repo.akka.io/maven") + +lazy val akkaVersion = sys.props.getOrElse("akka.version", "2.9.1") +lazy val akkaHttpVersion = sys.props.getOrElse("akka.http.version", "10.6.0") + +// Note: this default isn't really used anywhere so not important to bump +lazy val akkaManagementVersion = sys.props.getOrElse("akka.management.version", "1.5.0") + +// Run in a separate JVM, to make sure sbt waits until all threads have +// finished before returning. +// If you want to keep the application running while executing other +// sbt tasks, consider https://github.com/spray/sbt-revolver/ +fork := true + +libraryDependencies ++= Seq( + "com.typesafe.akka" %% "akka-actor-typed" % akkaVersion, + "com.typesafe.akka" %% "akka-cluster-typed" % akkaVersion, + "com.typesafe.akka" %% "akka-cluster" % akkaVersion, + "com.typesafe.akka" %% "akka-discovery" % akkaVersion, + "com.typesafe.akka" %% "akka-persistence" % akkaVersion, + "com.typesafe.akka" %% "akka-cluster-sharding" % akkaVersion, + "com.typesafe.akka" %% "akka-remote" % akkaVersion, + "com.typesafe.akka" %% "akka-http" % akkaHttpVersion, + "com.typesafe.akka" %% "akka-http-core" % akkaHttpVersion, + "com.typesafe.akka" %% "akka-http-spray-json" % akkaHttpVersion, + "com.lightbend.akka.management" %% "akka-management" % akkaManagementVersion, + "com.lightbend.akka.management" %% "akka-management-cluster-bootstrap" % akkaManagementVersion, + "com.lightbend.akka.management" %% "akka-management-cluster-http" % akkaManagementVersion, + "com.lightbend.akka.discovery" %% "akka-discovery-kubernetes-api" % akkaManagementVersion, + "com.lightbend.akka.management" %% "akka-lease-kubernetes" % akkaManagementVersion, + "com.lightbend.akka.management" %% "akka-rolling-update-kubernetes" % akkaManagementVersion, + "ch.qos.logback" % "logback-classic" % "1.2.13" +) + +// useful for investigations: sbt nativeImageRunAgent + +// GraalVM native image build +enablePlugins(NativeImagePlugin) +nativeImageJvm := "graalvm-community" +nativeImageVersion := "21.0.2" +nativeImageOptions := Seq( + "--no-fallback", + "--verbose", + "-Dakka.native-image.debug=true" +) diff --git a/native-image-tests/project/build.properties b/native-image-tests/project/build.properties new file mode 100644 index 00000000..abbbce5d --- /dev/null +++ b/native-image-tests/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.9.8 diff --git a/native-image-tests/project/plugins.sbt b/native-image-tests/project/plugins.sbt new file mode 100644 index 00000000..f28fde7a --- /dev/null +++ b/native-image-tests/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("org.scalameta" % "sbt-native-image" % "0.3.4") diff --git a/native-image-tests/src/main/resources/application.conf b/native-image-tests/src/main/resources/application.conf new file mode 100644 index 00000000..f921292b --- /dev/null +++ b/native-image-tests/src/main/resources/application.conf @@ -0,0 +1,37 @@ +akka { + actor.provider = "cluster" + extensions = ["akka.management.cluster.bootstrap.ClusterBootstrap"] + + + discovery { + aggregate { + discovery-methods = ["kubernetes-api", "config"] + } + config.services.local-cluster.endpoints = [ + { + host = "127.0.0.1" + port = 8558 + } + ] + } + + + management { + http.hostname = "127.0.0.1" + http.port = 8558 + cluster.bootstrap { + contact-point-discovery { + # to allow easier testing, we aggregate kubernetes-api and then use config as a fallback, + # native-image-brokenness would cause class not found or linker error, so k8 api discovery failing because + # not running inside k8 means things likely works as expected (not a watertight check though) + discovery-method = aggregate + service-name = "local-cluster" + required-contact-point-nr = 1 + } + } + } + + cluster { + downing-provider-class = "akka.cluster.sbr.SplitBrainResolverProvider" + } +} \ No newline at end of file diff --git a/native-image-tests/src/main/resources/logback.xml b/native-image-tests/src/main/resources/logback.xml new file mode 100644 index 00000000..b1fe9ae9 --- /dev/null +++ b/native-image-tests/src/main/resources/logback.xml @@ -0,0 +1,20 @@ + + + + + [%date{ISO8601}] [%level] [%logger] [%thread] [%X{akkaSource}] - %msg%n + + + + + 1024 + true + + + + + + + + diff --git a/native-image-tests/src/main/scala/com/lightbend/Main.scala b/native-image-tests/src/main/scala/com/lightbend/Main.scala new file mode 100644 index 00000000..680ad2f7 --- /dev/null +++ b/native-image-tests/src/main/scala/com/lightbend/Main.scala @@ -0,0 +1,42 @@ +package com.lightbend + +import akka.actor.typed.ActorSystem +import akka.actor.typed.Behavior +import akka.actor.typed.scaladsl.Behaviors +import akka.cluster.typed.Cluster +import akka.cluster.typed.SelfUp +import akka.cluster.typed.Subscribe +import akka.management.scaladsl.AkkaManagement + +import scala.concurrent.duration.DurationInt + +object RootBehavior { + def apply(): Behavior[AnyRef] = Behaviors.setup { context => + Behaviors.withTimers { timers => + // Note that some exceptions in the log from k8 api discovery is expected, see application.conf + AkkaManagement(context.system).start() + timers.startSingleTimer("Timeout", 30.seconds) + Cluster(context.system).subscriptions ! Subscribe(context.self, classOf[SelfUp]) + + // FIXME cover k8 lease + // FIXME cover rolling-update + + Behaviors.receiveMessagePartial { + case SelfUp(_) => + context.log.info("Managed to bootstrap cluster, shutting down") + Behaviors.stopped + + case "Timeout" => + context.log.error("Didn't manage to bootstrap within 30s, something is off") + System.exit(1) + Behaviors.same + } + } + } +} + +object Main extends App { + + val system: ActorSystem[AnyRef] = ActorSystem(RootBehavior(), "ManagementNativeImageTests") + +}