From 654aa922ed253eea57685ab5dd39d0389a652a6a Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 31 Jan 2019 22:26:38 -0500 Subject: [PATCH 1/3] Rename appName to app Ref #183 --- .../reactivecli/runtime/kubernetes/Deployment.scala | 4 ++-- .../rp/reactivecli/runtime/kubernetes/Job.scala | 2 +- .../reactivecli/runtime/kubernetes/PodTemplate.scala | 4 ++-- .../rp/reactivecli/runtime/kubernetes/Service.scala | 6 +++--- .../runtime/kubernetes/DeploymentJsonTest.scala | 10 +++++----- .../reactivecli/runtime/kubernetes/JobJsonTest.scala | 2 +- .../runtime/kubernetes/KubernetesPackageTest.scala | 8 ++++---- .../runtime/kubernetes/ServiceJsonTest.scala | 12 ++++++------ 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Deployment.scala b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Deployment.scala index 1489847b..cd302513 100644 --- a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Deployment.scala +++ b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Deployment.scala @@ -58,7 +58,7 @@ object Deployment { val appNameVersion = serviceName(s"$appName$VersionSeparator$version") val labels = Map( - "appName" -> appName, + "app" -> appName, "appNameVersion" -> appNameVersion) ++ annotations.akkaClusterBootstrapSystemName.fold(Map.empty[String, String])(system => Map("actorSystemName" -> system)) val podTemplate = @@ -87,7 +87,7 @@ object Deployment { (appNameVersion, Json("appNameVersion" -> appNameVersion.asJson)) case RollingDeploymentType => - (appName, Json("appName" -> appName.asJson)) + (appName, Json("app" -> appName.asJson)) } Deployment( diff --git a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Job.scala b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Job.scala index e3ac94e1..749cf141 100644 --- a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Job.scala +++ b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Job.scala @@ -58,7 +58,7 @@ object Job { val appNameVersion = serviceName(s"$appName$VersionSeparator$version") val labels = Map( - "appName" -> appName, + "app" -> appName, "appNameVersion" -> appNameVersion) ++ annotations.akkaClusterBootstrapSystemName.fold(Map.empty[String, String])(system => Map("actorSystemName" -> system)) val podTemplate = diff --git a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/PodTemplate.scala b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/PodTemplate.scala index 3635178d..01493e2a 100644 --- a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/PodTemplate.scala +++ b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/PodTemplate.scala @@ -95,7 +95,7 @@ object PodTemplate { s"-Dakka.management.cluster.bootstrap.contact-point-discovery.port-name=$managementEndpointName", s"-Dakka.management.cluster.bootstrap.contact-point-discovery.effective-name=$serviceResourceName", s"-Dakka.management.cluster.bootstrap.contact-point-discovery.required-contact-point-nr=$noOfReplicas", - akkaClusterBootstrapSystemName.fold("-Dakka.discovery.kubernetes-api.pod-label-selector=appName=%s")(systemName => s"-Dakka.discovery.kubernetes-api.pod-label-selector=actorSystemName=$systemName"), + akkaClusterBootstrapSystemName.fold("-Dakka.discovery.kubernetes-api.pod-label-selector=app=%s")(systemName => s"-Dakka.discovery.kubernetes-api.pod-label-selector=actorSystemName=$systemName"), s"${if (akkaClusterJoinExisting) "-Dakka.management.cluster.bootstrap.form-new-cluster=false" else ""}") .filter(_.nonEmpty) .mkString(" ")), @@ -158,7 +158,7 @@ object PodTemplate { * If the akkaClusterJoinExisting flag is provided, these labels are removed from the pod template so that * it isn't used for bootstrap. */ - private[kubernetes] val PodDiscoveryLabels = Set("appName", "actorSystemName") + private[kubernetes] val PodDiscoveryLabels = Set("app", "actorSystemName") /** * Represents possible values for imagePullPolicy field within the Kubernetes pod template. diff --git a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Service.scala b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Service.scala index c2369c71..b72f62c6 100644 --- a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Service.scala +++ b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Service.scala @@ -69,8 +69,8 @@ object Service { val selector = deploymentType match { - case CanaryDeploymentType => Json("appName" -> appName.asJson) - case RollingDeploymentType => Json("appName" -> appName.asJson) + case CanaryDeploymentType => Json("app" -> appName.asJson) + case RollingDeploymentType => Json("app" -> appName.asJson) case BlueGreenDeploymentType => Json("appNameVersion" -> appNameVersion.asJson) } @@ -85,7 +85,7 @@ object Service { "kind" -> "Service".asJson, "metadata" -> Json( "labels" -> Json( - "appName" -> appName.asJson), + "app" -> appName.asJson), "name" -> appName.asJson) .deepmerge( annotations.namespace.fold(jEmptyObject)(ns => Json("namespace" -> serviceName(ns).asJson))), diff --git a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/DeploymentJsonTest.scala b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/DeploymentJsonTest.scala index c287936d..aa75a2f1 100644 --- a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/DeploymentJsonTest.scala +++ b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/DeploymentJsonTest.scala @@ -113,7 +113,7 @@ object DeploymentJsonTest extends TestSuite { | "metadata": { | "name": "friendimpl-v3-2-1-snapshot", | "labels": { - | "appName": "friendimpl", + | "app": "friendimpl", | "appNameVersion": "friendimpl-v3-2-1-snapshot" | }, | "namespace": "chirper" @@ -128,7 +128,7 @@ object DeploymentJsonTest extends TestSuite { | "template": { | "metadata": { | "labels": { - | "appName": "friendimpl", + | "app": "friendimpl", | "appNameVersion": "friendimpl-v3-2-1-snapshot" | }, | "annotations": { @@ -257,7 +257,7 @@ object DeploymentJsonTest extends TestSuite { | "metadata": { | "name": "friendimpl-v3-2-1-snapshot", | "labels": { - | "appName": "friendimpl", + | "app": "friendimpl", | "appNameVersion": "friendimpl-v3-2-1-snapshot" | }, | "namespace": "chirper" @@ -272,7 +272,7 @@ object DeploymentJsonTest extends TestSuite { | "template": { | "metadata": { | "labels": { - | "appName": "friendimpl", + | "app": "friendimpl", | "appNameVersion": "friendimpl-v3-2-1-snapshot" | }, | "annotations": { @@ -351,7 +351,7 @@ object DeploymentJsonTest extends TestSuite { | }, | { | "name": "RP_JAVA_OPTS", - | "value": "-Dconfig.resource=my-config.conf -Dakka.management.cluster.bootstrap.contact-point-discovery.discovery-method=kubernetes-api -Dakka.management.cluster.bootstrap.contact-point-discovery.port-name=management -Dakka.management.cluster.bootstrap.contact-point-discovery.effective-name=friendimpl -Dakka.management.cluster.bootstrap.contact-point-discovery.required-contact-point-nr=1 -Dakka.discovery.kubernetes-api.pod-label-selector=appName=%s" + | "value": "-Dconfig.resource=my-config.conf -Dakka.management.cluster.bootstrap.contact-point-discovery.discovery-method=kubernetes-api -Dakka.management.cluster.bootstrap.contact-point-discovery.port-name=management -Dakka.management.cluster.bootstrap.contact-point-discovery.effective-name=friendimpl -Dakka.management.cluster.bootstrap.contact-point-discovery.required-contact-point-nr=1 -Dakka.discovery.kubernetes-api.pod-label-selector=app=%s" | }, | { | "name": "RP_KUBERNETES_POD_IP", diff --git a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/JobJsonTest.scala b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/JobJsonTest.scala index 01f8d326..d7f40922 100644 --- a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/JobJsonTest.scala +++ b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/JobJsonTest.scala @@ -69,7 +69,7 @@ object JobJsonTest extends TestSuite { "metadata" -> jObjectFields( "name" -> jString("friendimpl-v3-2-1-snapshot"), "labels" -> jObjectFields( - "appName" -> jString("friendimpl"), + "app" -> jString("friendimpl"), "appNameVersion" -> jString("friendimpl-v3-2-1-snapshot"))), "spec" -> jObjectFields( "template" -> jObjectFields( diff --git a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/KubernetesPackageTest.scala b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/KubernetesPackageTest.scala index 2a5933f7..c51bb22b 100644 --- a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/KubernetesPackageTest.scala +++ b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/KubernetesPackageTest.scala @@ -131,7 +131,7 @@ object KubernetesPackageTest extends TestSuite { | "metadata": { | "name": "my-app-v3-2-1-snapshot", | "labels": { - | "appName": "my-app", + | "app": "my-app", | "appNameVersion": "my-app-v3-2-1-snapshot" | }, | "namespace": "chirper" @@ -146,7 +146,7 @@ object KubernetesPackageTest extends TestSuite { | "template": { | "metadata": { | "labels": { - | "appName": "my-app", + | "app": "my-app", | "appNameVersion": "my-app-v3-2-1-snapshot" | } | }, @@ -259,7 +259,7 @@ object KubernetesPackageTest extends TestSuite { | "kind": "Service", | "metadata": { | "labels": { - | "appName": "my-app" + | "app": "my-app" | }, | "name": "my-app", | "namespace": "chirper" @@ -286,7 +286,7 @@ object KubernetesPackageTest extends TestSuite { | } | ], | "selector": { - | "appName": "my-app" + | "app": "my-app" | } | } |} diff --git a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/ServiceJsonTest.scala b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/ServiceJsonTest.scala index 24ea16d1..e4b598df 100644 --- a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/ServiceJsonTest.scala +++ b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/ServiceJsonTest.scala @@ -65,7 +65,7 @@ object ServiceJsonTest extends TestSuite { .get .payload .map { j => - val result = (j.hcursor --\ "spec" --\ "selector" --\ "appName").focus + val result = (j.hcursor --\ "spec" --\ "selector" --\ "app").focus val expected = Some(jString("friendimpl")) assert(result == expected) @@ -89,7 +89,7 @@ object ServiceJsonTest extends TestSuite { .get .get .payload - .map(j => assert((j.hcursor --\ "spec" --\ "selector" --\ "appName").focus.contains(jString("friendimpl")))) + .map(j => assert((j.hcursor --\ "spec" --\ "selector" --\ "app").focus.contains(jString("friendimpl")))) } } @@ -113,7 +113,7 @@ object ServiceJsonTest extends TestSuite { | "kind": "Service", | "metadata": { | "labels": { - | "appName": "friendimpl" + | "app": "friendimpl" | }, | "name": "friendimpl", | "namespace": "chirper" @@ -128,7 +128,7 @@ object ServiceJsonTest extends TestSuite { | } | ], | "selector": { - | "appName": "friendimpl" + | "app": "friendimpl" | } | } |} @@ -145,7 +145,7 @@ object ServiceJsonTest extends TestSuite { | "kind": "Service", | "metadata": { | "labels": { - | "appName": "friendimpl" + | "app": "friendimpl" | }, | "name": "friendimpl", | "namespace": "chirper" @@ -163,7 +163,7 @@ object ServiceJsonTest extends TestSuite { | ], | "type": "NodePort", | "selector": { - | "appName": "friendimpl" + | "app": "friendimpl" | } | } |} From dae04995ab9624d2d7bfcdd9e33f4f1dbf9ffb12 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 31 Jan 2019 23:24:58 -0500 Subject: [PATCH 2/3] Define akka.lightbend.com/service-name label Fixes #183 For the purpose of Akka Cluster Bootstrap, a specialized label `akka.lightbend.com/service-name` defined. This denotes the Akka Cluster to join when a pod comes up. - The value of the this label will default to either the app name or the app version name depending on the deployment type. - It can be overridden by user using `akkaClusterBootstrapSystemName` setting as described in https://developer.lightbend.com/docs/lightbend-orchestration/current/features/akka-cluster-bootstrap.html. - Deployment pods are labeled with `"akka.lightbend.com/service-name": "friendimpl"` etc - The label selector is overridden as `-Dakka.discovery.kubernetes-api.pod-label-selector=akka.lightbend.com/service-name=%s` (as opposed to using `app=%s`) - The effective name is overridden as `-Dakka.management.cluster.bootstrap.contact-point-discovery.effective-name=friendimpl` etc --- .../runtime/kubernetes/Deployment.scala | 10 +++++++++- .../rp/reactivecli/runtime/kubernetes/Job.scala | 9 ++++++++- .../runtime/kubernetes/PodTemplate.scala | 8 ++++++-- .../reactivecli/runtime/kubernetes/package.scala | 1 + .../runtime/kubernetes/DeploymentJsonTest.scala | 14 +++++++++----- .../runtime/kubernetes/JobJsonTest.scala | 6 ++++-- .../runtime/kubernetes/KubernetesPackageTest.scala | 6 ++++-- 7 files changed, 41 insertions(+), 13 deletions(-) diff --git a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Deployment.scala b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Deployment.scala index cd302513..a9e5ce04 100644 --- a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Deployment.scala +++ b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Deployment.scala @@ -57,9 +57,17 @@ object Deployment { val appName = serviceName(rawAppName) val appNameVersion = serviceName(s"$appName$VersionSeparator$version") + val serviceResourceName = + deploymentType match { + case CanaryDeploymentType => appName + case BlueGreenDeploymentType => appNameVersion + case RollingDeploymentType => appName + } + val labels = Map( "app" -> appName, - "appNameVersion" -> appNameVersion) ++ annotations.akkaClusterBootstrapSystemName.fold(Map.empty[String, String])(system => Map("actorSystemName" -> system)) + "appNameVersion" -> appNameVersion) ++ annotations.akkaClusterBootstrapSystemName.fold(Map( + serviceNameLabel -> serviceResourceName))(system => Map(serviceNameLabel -> system)) val podTemplate = PodTemplate.generate( diff --git a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Job.scala b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Job.scala index 749cf141..154193a6 100644 --- a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Job.scala +++ b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Job.scala @@ -56,10 +56,17 @@ object Job { |@| restartPolicyValidation(restartPolicy)) { (applicationArgs, rawAppName, version, restartPolicy) => val appName = serviceName(rawAppName) val appNameVersion = serviceName(s"$appName$VersionSeparator$version") + val serviceResourceName = + deploymentType match { + case CanaryDeploymentType => appName + case BlueGreenDeploymentType => appNameVersion + case RollingDeploymentType => appName + } val labels = Map( "app" -> appName, - "appNameVersion" -> appNameVersion) ++ annotations.akkaClusterBootstrapSystemName.fold(Map.empty[String, String])(system => Map("actorSystemName" -> system)) + "appNameVersion" -> appNameVersion) ++ annotations.akkaClusterBootstrapSystemName.fold(Map( + serviceNameLabel -> serviceResourceName))(system => Map(serviceNameLabel -> system)) val podTemplate = PodTemplate.generate( diff --git a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/PodTemplate.scala b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/PodTemplate.scala index 01493e2a..c75a374b 100644 --- a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/PodTemplate.scala +++ b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/PodTemplate.scala @@ -93,9 +93,13 @@ object PodTemplate { Seq( s"-Dakka.management.cluster.bootstrap.contact-point-discovery.discovery-method=kubernetes-api", s"-Dakka.management.cluster.bootstrap.contact-point-discovery.port-name=$managementEndpointName", - s"-Dakka.management.cluster.bootstrap.contact-point-discovery.effective-name=$serviceResourceName", + // https://github.com/akka/akka-management/blob/v0.20.0/cluster-bootstrap/src/main/resources/reference.conf + akkaClusterBootstrapSystemName match { + case Some(systemName) => s"-Dakka.management.cluster.bootstrap.contact-point-discovery.effective-name=$systemName" + case _ => s"-Dakka.management.cluster.bootstrap.contact-point-discovery.effective-name=$serviceResourceName" + }, s"-Dakka.management.cluster.bootstrap.contact-point-discovery.required-contact-point-nr=$noOfReplicas", - akkaClusterBootstrapSystemName.fold("-Dakka.discovery.kubernetes-api.pod-label-selector=app=%s")(systemName => s"-Dakka.discovery.kubernetes-api.pod-label-selector=actorSystemName=$systemName"), + "-Dakka.discovery.kubernetes-api.pod-label-selector=akka.lightbend.com/service-name=%s", s"${if (akkaClusterJoinExisting) "-Dakka.management.cluster.bootstrap.form-new-cluster=false" else ""}") .filter(_.nonEmpty) .mkString(" ")), diff --git a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/package.scala b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/package.scala index 9885f66b..68cf2902 100644 --- a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/package.scala +++ b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/package.scala @@ -34,6 +34,7 @@ import slogging.LazyLogging import Scalaz._ package object kubernetes extends LazyLogging { + private[reactivecli] val serviceNameLabel = "akka.lightbend.com/service-name" private[reactivecli] val LivenessInitialDelaySeconds = 60 private[reactivecli] val StatusPeriodSeconds = 10 diff --git a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/DeploymentJsonTest.scala b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/DeploymentJsonTest.scala index aa75a2f1..1193a9b4 100644 --- a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/DeploymentJsonTest.scala +++ b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/DeploymentJsonTest.scala @@ -114,7 +114,8 @@ object DeploymentJsonTest extends TestSuite { | "name": "friendimpl-v3-2-1-snapshot", | "labels": { | "app": "friendimpl", - | "appNameVersion": "friendimpl-v3-2-1-snapshot" + | "appNameVersion": "friendimpl-v3-2-1-snapshot", + | "akka.lightbend.com/service-name": "friendimpl" | }, | "namespace": "chirper" | }, @@ -129,7 +130,8 @@ object DeploymentJsonTest extends TestSuite { | "metadata": { | "labels": { | "app": "friendimpl", - | "appNameVersion": "friendimpl-v3-2-1-snapshot" + | "appNameVersion": "friendimpl-v3-2-1-snapshot", + | "akka.lightbend.com/service-name": "friendimpl" | }, | "annotations": { | "annotationKey0": "annotationValue0", @@ -258,7 +260,8 @@ object DeploymentJsonTest extends TestSuite { | "name": "friendimpl-v3-2-1-snapshot", | "labels": { | "app": "friendimpl", - | "appNameVersion": "friendimpl-v3-2-1-snapshot" + | "appNameVersion": "friendimpl-v3-2-1-snapshot", + | "akka.lightbend.com/service-name": "friendimpl" | }, | "namespace": "chirper" | }, @@ -273,7 +276,8 @@ object DeploymentJsonTest extends TestSuite { | "metadata": { | "labels": { | "app": "friendimpl", - | "appNameVersion": "friendimpl-v3-2-1-snapshot" + | "appNameVersion": "friendimpl-v3-2-1-snapshot", + | "akka.lightbend.com/service-name": "friendimpl" | }, | "annotations": { | "annotationKey0": "annotationValue0", @@ -351,7 +355,7 @@ object DeploymentJsonTest extends TestSuite { | }, | { | "name": "RP_JAVA_OPTS", - | "value": "-Dconfig.resource=my-config.conf -Dakka.management.cluster.bootstrap.contact-point-discovery.discovery-method=kubernetes-api -Dakka.management.cluster.bootstrap.contact-point-discovery.port-name=management -Dakka.management.cluster.bootstrap.contact-point-discovery.effective-name=friendimpl -Dakka.management.cluster.bootstrap.contact-point-discovery.required-contact-point-nr=1 -Dakka.discovery.kubernetes-api.pod-label-selector=app=%s" + | "value": "-Dconfig.resource=my-config.conf -Dakka.management.cluster.bootstrap.contact-point-discovery.discovery-method=kubernetes-api -Dakka.management.cluster.bootstrap.contact-point-discovery.port-name=management -Dakka.management.cluster.bootstrap.contact-point-discovery.effective-name=friendimpl -Dakka.management.cluster.bootstrap.contact-point-discovery.required-contact-point-nr=1 -Dakka.discovery.kubernetes-api.pod-label-selector=akka.lightbend.com/service-name=%s" | }, | { | "name": "RP_KUBERNETES_POD_IP", diff --git a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/JobJsonTest.scala b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/JobJsonTest.scala index d7f40922..b862adb9 100644 --- a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/JobJsonTest.scala +++ b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/JobJsonTest.scala @@ -70,12 +70,14 @@ object JobJsonTest extends TestSuite { "name" -> jString("friendimpl-v3-2-1-snapshot"), "labels" -> jObjectFields( "app" -> jString("friendimpl"), - "appNameVersion" -> jString("friendimpl-v3-2-1-snapshot"))), + "appNameVersion" -> jString("friendimpl-v3-2-1-snapshot"), + "akka.lightbend.com/service-name" -> jString("friendimpl"))), "spec" -> jObjectFields( "template" -> jObjectFields( "metadata" -> jObjectFields( "labels" -> jObjectFields( - "appNameVersion" -> jString("friendimpl-v3-2-1-snapshot"))), + "appNameVersion" -> jString("friendimpl-v3-2-1-snapshot"), + "akka.lightbend.com/service-name" -> jString("friendimpl"))), "spec" -> jObjectFields( "restartPolicy" -> jString("OnFailure"), "containers" -> jArrayElements( diff --git a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/KubernetesPackageTest.scala b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/KubernetesPackageTest.scala index c51bb22b..63e2d143 100644 --- a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/KubernetesPackageTest.scala +++ b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/KubernetesPackageTest.scala @@ -132,7 +132,8 @@ object KubernetesPackageTest extends TestSuite { | "name": "my-app-v3-2-1-snapshot", | "labels": { | "app": "my-app", - | "appNameVersion": "my-app-v3-2-1-snapshot" + | "appNameVersion": "my-app-v3-2-1-snapshot", + | "akka.lightbend.com/service-name": "my-app" | }, | "namespace": "chirper" | }, @@ -147,7 +148,8 @@ object KubernetesPackageTest extends TestSuite { | "metadata": { | "labels": { | "app": "my-app", - | "appNameVersion": "my-app-v3-2-1-snapshot" + | "appNameVersion": "my-app-v3-2-1-snapshot", + | "akka.lightbend.com/service-name": "my-app" | } | }, | "spec": { From cc51750c7c58aa4b302afb28ecefa9dd926ad302 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Fri, 1 Feb 2019 15:10:44 -0500 Subject: [PATCH 3/3] YAML generation for Akka Cluster Boostrap using DNS This implemets YAML generation for Akka Cluster Boostrap using DNS. --- .../reactivecli/annotations/Annotations.scala | 18 + .../rp/reactivecli/argparse/CommandArgs.scala | 15 + .../rp/reactivecli/argparse/InputArgs.scala | 13 + .../runtime/kubernetes/Deployment.scala | 2 + .../reactivecli/runtime/kubernetes/Job.scala | 2 + .../runtime/kubernetes/PodTemplate.scala | 43 +- .../runtime/kubernetes/Service.scala | 70 ++-- .../runtime/kubernetes/package.scala | 3 + .../annotations/AnnotationsTest.scala | 8 + .../kubernetes/DeploymentJsonTest.scala | 88 +++- .../runtime/kubernetes/IngressJsonTest.scala | 1 + .../runtime/kubernetes/JobJsonTest.scala | 5 +- .../kubernetes/KubernetesPackageTest.scala | 383 ++++++++++++++++-- .../kubernetes/NamespaceJsonTest.scala | 1 + .../runtime/kubernetes/ServiceJsonTest.scala | 138 ++++++- .../marathon/RpEnvironmentVariablesTest.scala | 4 + .../bootstrap-demo/kubernetes-api/build.sbt | 2 +- .../bootstrap-demo/kubernetes-dns/build.sbt | 87 ++++ .../kubernetes-dns/project/Dependencies.scala | 21 + .../kubernetes-dns/project/build.properties | 2 + .../kubernetes-dns/project/plugins.sbt | 2 + .../src/main/resources/application.conf | 3 + .../src/main/scala/foo/ClusterApp.scala | 66 +++ .../src/main/scala/foo/NoisySingleton.scala | 21 + .../bootstrap-demo/kubernetes-dns/test | 4 + 25 files changed, 904 insertions(+), 98 deletions(-) create mode 100644 integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/build.sbt create mode 100644 integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/project/Dependencies.scala create mode 100644 integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/project/build.properties create mode 100644 integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/project/plugins.sbt create mode 100644 integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/src/main/resources/application.conf create mode 100644 integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/src/main/scala/foo/ClusterApp.scala create mode 100644 integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/src/main/scala/foo/NoisySingleton.scala create mode 100644 integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/test diff --git a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/annotations/Annotations.scala b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/annotations/Annotations.scala index d4143398..072443a7 100644 --- a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/annotations/Annotations.scala +++ b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/annotations/Annotations.scala @@ -37,6 +37,7 @@ case class Annotations( cpu: Option[Double], endpoints: Map[String, Endpoint], managementEndpointName: Option[String], + remotingEndpointName: Option[String], secrets: Seq[Secret], annotations: Seq[Annotation] = Seq.empty, privileged: Boolean, @@ -45,6 +46,18 @@ case class Annotations( modules: Set[String], akkaClusterBootstrapSystemName: Option[String]) { + def headlessEndpoints: Map[String, Endpoint] = + endpoints filterKeys { (k: String) => + (k.some === managementEndpointName) || + (k.some === remotingEndpointName) + } + + def publicEndpoints: Map[String, Endpoint] = + endpoints filterKeys { (k: String) => + !((k.some === managementEndpointName) || + (k.some === remotingEndpointName)) + } + def applicationValidation(application: Option[String]): ValidationNel[String, Option[Seq[String]]] = application match { case None => @@ -118,6 +131,7 @@ object Annotations extends LazyLogging { cpu = args.cpu.orElse(cpu(labels)), endpoints = endpoints(selectArrayWithIndex(labels, ns("endpoints")), applicationVersion), managementEndpointName = managementEndpointName(labels), + remotingEndpointName = remotingEndpointName(labels), secrets = secrets(selectArray(labels, ns("secrets"))), annotations = annotations(selectArray(labels, ns("annotations"))), privileged = privileged(labels), @@ -217,6 +231,10 @@ object Annotations extends LazyLogging { labels .get(ns("management-endpoint")) + private[annotations] def remotingEndpointName(labels: Map[String, String]): Option[String] = + labels + .get(ns("remoting-endpoint")) + private[annotations] def endpoints(endpoints: Seq[(Int, Map[String, String])], version: Option[String]): Map[String, Endpoint] = endpoints.flatMap(v => endpoint(v._2, v._1, version)).toMap diff --git a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/argparse/CommandArgs.scala b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/argparse/CommandArgs.scala index 0902d660..5d53167f 100644 --- a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/argparse/CommandArgs.scala +++ b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/argparse/CommandArgs.scala @@ -60,6 +60,20 @@ case object CanaryDeploymentType extends DeploymentType case object BlueGreenDeploymentType extends DeploymentType case object RollingDeploymentType extends DeploymentType +/** + * Represents the discovery method during Akka Boostrap on Kubernetes. + */ +sealed trait DiscoveryMethod +object DiscoveryMethod { + case object KubernetesApi extends DiscoveryMethod { + override def toString: String = "kubernetes-api" + } + case object AkkaDns extends DiscoveryMethod { + override def toString = "akka-dns" + } + def all = Seq(AkkaDns, KubernetesApi) +} + /** * Represents the input argument for `generate-deployment` command. */ @@ -68,6 +82,7 @@ case class GenerateDeploymentArgs( akkaClusterJoinExisting: Boolean = false, akkaClusterSkipValidation: Boolean = false, deploymentType: DeploymentType = CanaryDeploymentType, + discoveryMethod: DiscoveryMethod = DiscoveryMethod.AkkaDns, dockerImages: Seq[String] = Seq.empty, name: Option[String] = None, version: Option[String] = None, diff --git a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/argparse/InputArgs.scala b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/argparse/InputArgs.scala index 1929a93e..f6767bd5 100644 --- a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/argparse/InputArgs.scala +++ b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/argparse/InputArgs.scala @@ -37,6 +37,14 @@ object InputArgs { throw new IllegalArgumentException(s"Invalid deployment type $v. Available: ${DeploymentType.All.mkString(", ")}") } + implicit val discoveryMethodRead: scopt.Read[DiscoveryMethod] = + scopt.Read.reads { + case v if v.toLowerCase == DiscoveryMethod.AkkaDns.toString => DiscoveryMethod.AkkaDns + case v if v.toLowerCase == DiscoveryMethod.KubernetesApi.toString => DiscoveryMethod.KubernetesApi + case v => + throw new IllegalArgumentException(s"Invalid discovery method $v. Available: ${DiscoveryMethod.all.mkString(", ")}") + } + implicit val logLevelsRead: scopt.Read[LogLevel] = scopt.Read.reads { case v if v.toLowerCase == "error" => LogLevel.ERROR @@ -122,6 +130,11 @@ object InputArgs { .optional() .action(GenerateDeploymentArgs.set((t, args) => args.copy(deploymentType = t))), + opt[DiscoveryMethod]("discovery-method") + .text(s"Sets the discovery method. Default: ${DiscoveryMethod.AkkaDns}; Available: ${DiscoveryMethod.all.mkString(", ")}") + .optional() + .action(GenerateDeploymentArgs.set((t, args) => args.copy(discoveryMethod = t))), + opt[String]("env") /* note: this argument will apply for other targets */ .text("Sets an environment variable. Format: NAME=value") .minOccurs(0) diff --git a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Deployment.scala b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Deployment.scala index a9e5ce04..efeee4bb 100644 --- a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Deployment.scala +++ b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Deployment.scala @@ -47,6 +47,7 @@ object Deployment { noOfReplicas: Int, externalServices: Map[String, Seq[String]], deploymentType: DeploymentType, + discoveryMethod: DiscoveryMethod, jsonTransform: JsonTransform, akkaClusterJoinExisting: Boolean): ValidationNel[String, Deployment] = @@ -80,6 +81,7 @@ object Deployment { RestartPolicy.Always, // The only valid RestartPolicy for Deployment externalServices, deploymentType, + discoveryMethod, akkaClusterJoinExisting, applicationArgs, appName, diff --git a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Job.scala b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Job.scala index 154193a6..28b9f20f 100644 --- a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Job.scala +++ b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Job.scala @@ -47,6 +47,7 @@ object Job { noOfReplicas: Int, externalServices: Map[String, Seq[String]], deploymentType: DeploymentType, + discoveryMethod: DiscoveryMethod, jsonTransform: JsonTransform, akkaClusterJoinExisting: Boolean): ValidationNel[String, Job] = @@ -79,6 +80,7 @@ object Job { if (restartPolicy == RestartPolicy.Default) RestartPolicy.OnFailure else restartPolicy, externalServices, deploymentType, + discoveryMethod, akkaClusterJoinExisting, applicationArgs, appName, diff --git a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/PodTemplate.scala b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/PodTemplate.scala index c75a374b..7d22edbe 100644 --- a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/PodTemplate.scala +++ b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/PodTemplate.scala @@ -50,7 +50,7 @@ object PodTemplate { /** * Generates pod environment variables specific for RP applications. */ - def envs(annotations: Annotations, serviceResourceName: String, noOfReplicas: Int, externalServices: Map[String, Seq[String]], akkaClusterJoinExisting: Boolean): Map[String, EnvironmentVariable] = + def envs(annotations: Annotations, serviceResourceName: String, noOfReplicas: Int, externalServices: Map[String, Seq[String]], akkaClusterJoinExisting: Boolean, discoveryMethod: DiscoveryMethod): Map[String, EnvironmentVariable] = mergeEnvs( PodEnvs, appNameEnvs(annotations.appName), @@ -58,6 +58,8 @@ object PodTemplate { appTypeEnvs(annotations.appType, annotations.modules), configEnvs(annotations.configResource), akkaClusterEnvs( + annotations.appName, + discoveryMethod, annotations.modules, annotations.namespace, serviceResourceName, @@ -78,6 +80,8 @@ object PodTemplate { }.toMap private[kubernetes] def akkaClusterEnvs( + appName: Option[String], + discoveryMethod: DiscoveryMethod, modules: Set[String], namespace: Option[String], serviceResourceName: String, @@ -90,17 +94,29 @@ object PodTemplate { else Map( "RP_JAVA_OPTS" -> LiteralEnvironmentVariable( - Seq( - s"-Dakka.management.cluster.bootstrap.contact-point-discovery.discovery-method=kubernetes-api", - s"-Dakka.management.cluster.bootstrap.contact-point-discovery.port-name=$managementEndpointName", - // https://github.com/akka/akka-management/blob/v0.20.0/cluster-bootstrap/src/main/resources/reference.conf - akkaClusterBootstrapSystemName match { - case Some(systemName) => s"-Dakka.management.cluster.bootstrap.contact-point-discovery.effective-name=$systemName" - case _ => s"-Dakka.management.cluster.bootstrap.contact-point-discovery.effective-name=$serviceResourceName" - }, - s"-Dakka.management.cluster.bootstrap.contact-point-discovery.required-contact-point-nr=$noOfReplicas", - "-Dakka.discovery.kubernetes-api.pod-label-selector=akka.lightbend.com/service-name=%s", - s"${if (akkaClusterJoinExisting) "-Dakka.management.cluster.bootstrap.form-new-cluster=false" else ""}") + ((discoveryMethod match { + case DiscoveryMethod.KubernetesApi => + List( + s"-Dakka.management.cluster.bootstrap.contact-point-discovery.discovery-method=kubernetes-api", + s"-Dakka.management.cluster.bootstrap.contact-point-discovery.port-name=$managementEndpointName", + // https://github.com/akka/akka-management/blob/v0.20.0/cluster-bootstrap/src/main/resources/reference.conf + akkaClusterBootstrapSystemName match { + case Some(systemName) => s"-Dakka.management.cluster.bootstrap.contact-point-discovery.effective-name=$systemName" + case _ => s"-Dakka.management.cluster.bootstrap.contact-point-discovery.effective-name=$serviceResourceName" + }, + "-Dakka.discovery.kubernetes-api.pod-label-selector=akka.lightbend.com/service-name=%s") + case DiscoveryMethod.AkkaDns => + List( + s"-Dakka.management.cluster.bootstrap.contact-point-discovery.discovery-method=akka-dns", + s"-Dakka.management.cluster.bootstrap.contact-point-discovery.port-name=$managementEndpointName", + appName match { + case Some(name) => s"-Dakka.management.cluster.bootstrap.contact-point-discovery.service-name=$name-internal" + case _ => sys.error("appName was expected") + }) + }) ++ + List( + s"-Dakka.management.cluster.bootstrap.contact-point-discovery.required-contact-point-nr=$noOfReplicas", + s"${if (akkaClusterJoinExisting) "-Dakka.management.cluster.bootstrap.form-new-cluster=false" else ""}")) .filter(_.nonEmpty) .mkString(" ")), "RP_DYN_JAVA_OPTS" -> LiteralEnvironmentVariable( @@ -278,6 +294,7 @@ object PodTemplate { restartPolicy: RestartPolicy.Value, externalServices: Map[String, Seq[String]], deploymentType: DeploymentType, + discoveryMethod: DiscoveryMethod, akkaClusterJoinExisting: Boolean, applicationArgs: Option[Seq[String]], appName: String, @@ -372,7 +389,7 @@ object PodTemplate { "imagePullPolicy" -> imagePullPolicy.asJson, "env" -> RpEnvironmentVariables.mergeEnvs( annotations.environmentVariables ++ - RpEnvironmentVariables.envs(annotations, serviceResourceName, noOfReplicas, externalServices, akkaClusterJoinExisting)).asJson, + RpEnvironmentVariables.envs(annotations, serviceResourceName, noOfReplicas, externalServices, akkaClusterJoinExisting, discoveryMethod)).asJson, "ports" -> annotations.endpoints.asJson, "volumeMounts" -> secretNames .map { diff --git a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Service.scala b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Service.scala index b72f62c6..b04b4137 100644 --- a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Service.scala +++ b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/Service.scala @@ -59,12 +59,14 @@ object Service { apiVersion: String, clusterIp: Option[String], deploymentType: DeploymentType, + discoveryMethod: DiscoveryMethod, jsonTransform: JsonTransform, loadBalancerIp: Option[String], - serviceType: Option[String]): ValidationNel[String, Option[Service]] = + serviceType: Option[String]): ValidationNel[String, List[Service]] = (annotations.appNameValidation |@| annotations.versionValidation) { (rawAppName, version) => // FIXME there's a bit of code duplicate in Deployment val appName = serviceName(rawAppName) + val internalAppname = appName + "-internal" val appNameVersion = serviceName(s"$appName${PodTemplate.VersionSeparator}$version") val selector = @@ -74,28 +76,50 @@ object Service { case BlueGreenDeploymentType => Json("appNameVersion" -> appNameVersion.asJson) } - if (annotations.endpoints.isEmpty) - None - else - Some( - Service( - appName, - Json( - "apiVersion" -> apiVersion.asJson, - "kind" -> "Service".asJson, - "metadata" -> Json( - "labels" -> Json( - "app" -> appName.asJson), - "name" -> appName.asJson) - .deepmerge( - annotations.namespace.fold(jEmptyObject)(ns => Json("namespace" -> serviceName(ns).asJson))), - "spec" -> Json( - "ports" -> annotations.endpoints.asJson, - "selector" -> selector) - .deepmerge(clusterIp.fold(jEmptyObject)(cIp => Json("clusterIP" -> jString(cIp)))) - .deepmerge(serviceType.fold(jEmptyObject)(svcType => Json("type" -> jString(svcType)))) - .deepmerge(loadBalancerIp.fold(jEmptyObject)(lbIp => Json("loadBalancerIP" -> jString(lbIp))))), - jsonTransform)) + def svc(endpoints: Map[String, Endpoint]) = Service( + appName, + Json( + "apiVersion" -> apiVersion.asJson, + "kind" -> "Service".asJson, + "metadata" -> Json( + "labels" -> Json( + "app" -> appName.asJson), + "name" -> appName.asJson) + .deepmerge( + annotations.namespace.fold(jEmptyObject)(ns => Json("namespace" -> serviceName(ns).asJson))), + "spec" -> Json( + "ports" -> endpoints.asJson, + "selector" -> selector) + .deepmerge(clusterIp.fold(jEmptyObject)(cIp => Json("clusterIP" -> jString(cIp)))) + .deepmerge(serviceType.fold(jEmptyObject)(svcType => Json("type" -> jString(svcType)))) + .deepmerge(loadBalancerIp.fold(jEmptyObject)(lbIp => Json("loadBalancerIP" -> jString(lbIp))))), + jsonTransform) + + def headlessService(endpoints: Map[String, Endpoint]) = Service( + internalAppname, + Json( + "apiVersion" -> apiVersion.asJson, + "kind" -> "Service".asJson, + "metadata" -> Json( + "labels" -> Json( + "app" -> appName.asJson), + "annotations" -> Json( + "service.alpha.kubernetes.io/tolerate-unready-endpoints" -> jString("true")), + "name" -> internalAppname.asJson) + .deepmerge( + annotations.namespace.fold(jEmptyObject)(ns => Json("namespace" -> serviceName(ns).asJson))), + "spec" -> Json( + "ports" -> endpoints.asJson, + "selector" -> selector, + "clusterIP" -> jString("None"), + "publishNotReadyAddresses" -> jTrue)), + jsonTransform) + + if (annotations.endpoints.isEmpty) List() + else if (discoveryMethod == DiscoveryMethod.AkkaDns) List( + headlessService(annotations.headlessEndpoints), + svc(annotations.publicEndpoints)) + else List(svc(annotations.endpoints)) } } diff --git a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/package.scala b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/package.scala index 68cf2902..d373cbfa 100644 --- a/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/package.scala +++ b/cli/shared/src/main/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/package.scala @@ -91,6 +91,7 @@ package object kubernetes extends LazyLogging { kubernetesArgs.podControllerArgs.numberOfReplicas, generateDeploymentArgs.externalServices, generateDeploymentArgs.deploymentType, + generateDeploymentArgs.discoveryMethod, kubernetesArgs.transformPodControllers.fold(JsonTransform.noop)(JsonTransform.jq), generateDeploymentArgs.akkaClusterJoinExisting) @@ -105,6 +106,7 @@ package object kubernetes extends LazyLogging { kubernetesArgs.podControllerArgs.numberOfReplicas, generateDeploymentArgs.externalServices, generateDeploymentArgs.deploymentType, + generateDeploymentArgs.discoveryMethod, kubernetesArgs.transformPodControllers.fold(JsonTransform.noop)(JsonTransform.jq), generateDeploymentArgs.akkaClusterJoinExisting) } @@ -114,6 +116,7 @@ package object kubernetes extends LazyLogging { serviceApiVersion, kubernetesArgs.serviceArgs.clusterIp, generateDeploymentArgs.deploymentType, + generateDeploymentArgs.discoveryMethod, kubernetesArgs.transformServices.fold(JsonTransform.noop)(JsonTransform.jq), kubernetesArgs.serviceArgs.loadBalancerIp, kubernetesArgs.serviceArgs.serviceType) diff --git a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/annotations/AnnotationsTest.scala b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/annotations/AnnotationsTest.scala index f6a8e291..4b3130a0 100644 --- a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/annotations/AnnotationsTest.scala +++ b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/annotations/AnnotationsTest.scala @@ -121,6 +121,7 @@ object AnnotationsTest extends TestSuite { cpu = None, endpoints = Map.empty, managementEndpointName = None, + remotingEndpointName = None, secrets = Seq.empty, privileged = false, environmentVariables = Map.empty, @@ -202,6 +203,7 @@ object AnnotationsTest extends TestSuite { "ep2" -> TcpEndpoint(1, "ep2", 1234), "ep3" -> UdpEndpoint(2, "ep3", 1234)), managementEndpointName = Some("management"), + remotingEndpointName = Some("remoting"), secrets = Seq.empty, annotations = Vector( Annotation("annotationKey0", "annotationValue0"), @@ -245,6 +247,7 @@ object AnnotationsTest extends TestSuite { cpu = Some(0.5), endpoints = Map.empty, managementEndpointName = None, + remotingEndpointName = None, secrets = Seq.empty, privileged = false, environmentVariables = Map( @@ -272,6 +275,7 @@ object AnnotationsTest extends TestSuite { cpu = None, endpoints = Map.empty, managementEndpointName = None, + remotingEndpointName = None, secrets = Seq.empty, privileged = false, environmentVariables = Map.empty, @@ -295,6 +299,7 @@ object AnnotationsTest extends TestSuite { cpu = None, endpoints = Map.empty, managementEndpointName = None, + remotingEndpointName = None, secrets = Seq.empty, privileged = false, environmentVariables = Map.empty, @@ -318,6 +323,7 @@ object AnnotationsTest extends TestSuite { cpu = None, endpoints = Map.empty, managementEndpointName = None, + remotingEndpointName = None, secrets = Seq.empty, privileged = false, environmentVariables = Map.empty, @@ -347,6 +353,7 @@ object AnnotationsTest extends TestSuite { endpoints = Map( "ep2" -> TcpEndpoint(1, "ep2", 1234)), managementEndpointName = None, + remotingEndpointName = None, secrets = Seq.empty, privileged = false, environmentVariables = Map.empty, @@ -374,6 +381,7 @@ object AnnotationsTest extends TestSuite { endpoints = Map( "ep2" -> TcpEndpoint(1, "ep2", 1234)), managementEndpointName = None, + remotingEndpointName = None, secrets = Seq.empty, privileged = false, environmentVariables = Map.empty, diff --git a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/DeploymentJsonTest.scala b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/DeploymentJsonTest.scala index 1193a9b4..1e1b4672 100644 --- a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/DeploymentJsonTest.scala +++ b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/DeploymentJsonTest.scala @@ -47,6 +47,7 @@ object DeploymentJsonTest extends TestSuite { cpu = Some(0.5D), endpoints = endpoints, managementEndpointName = None, + remotingEndpointName = None, secrets = Seq(Secret("acme.co", "my-secret")), annotations = Seq( Annotation("annotationKey0", "annotationValue0"), @@ -66,7 +67,7 @@ object DeploymentJsonTest extends TestSuite { "deploymentType" - { "Canary" - { Deployment - .generate(annotations, "apps/v1beta2", None, imageName, PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Default, noOfReplicas = 1, Map.empty, CanaryDeploymentType, JsonTransform.noop, false) + .generate(annotations, "apps/v1beta2", None, imageName, PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Default, noOfReplicas = 1, Map.empty, CanaryDeploymentType, DiscoveryMethod.AkkaDns, JsonTransform.noop, false) .toOption .get .payload @@ -79,7 +80,7 @@ object DeploymentJsonTest extends TestSuite { "BlueGreen" - { Deployment - .generate(annotations, "v1", None, imageName, PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Default, noOfReplicas = 1, Map.empty, BlueGreenDeploymentType, JsonTransform.noop, false) + .generate(annotations, "v1", None, imageName, PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Default, noOfReplicas = 1, Map.empty, BlueGreenDeploymentType, DiscoveryMethod.AkkaDns, JsonTransform.noop, false) .toOption .get .payload @@ -92,7 +93,7 @@ object DeploymentJsonTest extends TestSuite { "Rolling" - { Deployment - .generate(annotations, "v1", None, imageName, PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Default, noOfReplicas = 1, Map.empty, RollingDeploymentType, JsonTransform.noop, false) + .generate(annotations, "v1", None, imageName, PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Default, noOfReplicas = 1, Map.empty, RollingDeploymentType, DiscoveryMethod.AkkaDns, JsonTransform.noop, false) .toOption .get .payload @@ -242,7 +243,8 @@ object DeploymentJsonTest extends TestSuite { """.stripMargin.parse.right.get val result = Deployment.generate(annotations, "apps/v1beta2", None, imageName, - PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Default, noOfReplicas = 1, Map.empty, CanaryDeploymentType, JsonTransform.noop, false).toOption.get + PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Default, noOfReplicas = 1, Map.empty, CanaryDeploymentType, + DiscoveryMethod.AkkaDns, JsonTransform.noop, false).toOption.get if (result.json != expectedJson) { println(s"deployment K8 JSON:\n" + PrettyParams.spaces2.copy(colonLeft = "").pretty(result.json)) @@ -250,6 +252,67 @@ object DeploymentJsonTest extends TestSuite { assert(result == Deployment("friendimpl-v3-2-1-snapshot", expectedJson, JsonTransform.noop)) } + "should generate JAVA_OPTS override for Akka Cluster Boostrapping" - { + val result = Deployment.generate(annotations.copy( + modules = Set("akka-management", "status", "akka-cluster-bootstrapping"), + managementEndpointName = Some("management")), "apps/v1beta2", None, imageName, + PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Default, noOfReplicas = 1, Map.empty, + CanaryDeploymentType, DiscoveryMethod.KubernetesApi, JsonTransform.noop, false).toOption.get + + result + .payload + .map({ j => + val javaOpts = (j.hcursor --\ "spec" --\ "template" --\ "spec" --\ "containers") + .downArray + .first + .downField("env") + .downN(4) + .downField("value") + .focus + .get + .string + .get + .split(' ') + .toSet + assert(javaOpts.contains("-Dakka.management.cluster.bootstrap.contact-point-discovery.discovery-method=kubernetes-api") && + javaOpts.contains("-Dakka.management.cluster.bootstrap.contact-point-discovery.port-name=management") && + javaOpts.contains("-Dakka.management.cluster.bootstrap.contact-point-discovery.effective-name=friendimpl") && + javaOpts.contains("-Dakka.discovery.kubernetes-api.pod-label-selector=akka.lightbend.com/service-name=%s") + ) + }) + } + + "should generate JAVA_OPTS override for Akka Cluster Boostrapping using DNS" - { + val result = Deployment.generate(annotations.copy( + modules = Set("akka-management", "status", "akka-cluster-bootstrapping"), + managementEndpointName = Some("management")), "apps/v1beta2", None, imageName, + PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Default, noOfReplicas = 1, Map.empty, + CanaryDeploymentType, DiscoveryMethod.AkkaDns, JsonTransform.noop, false).toOption.get + + result + .payload + .map({ j => + val javaOpts = (j.hcursor --\ "spec" --\ "template" --\ "spec" --\ "containers") + .downArray + .first + .downField("env") + .downN(4) + .downField("value") + .focus + .get + .string + .get + .split(' ') + .toSet + assert(javaOpts.contains("-Dakka.management.cluster.bootstrap.contact-point-discovery.discovery-method=akka-dns") && + javaOpts.contains("-Dakka.management.cluster.bootstrap.contact-point-discovery.service-name=friendimpl-internal") && + javaOpts.contains("-Dakka.management.cluster.bootstrap.contact-point-discovery.port-name=management") && + !javaOpts.contains("-Dakka.management.cluster.bootstrap.contact-point-discovery.effective-name=friendimpl") && + !javaOpts.contains("-Dakka.discovery.kubernetes-api.pod-label-selector=akka.lightbend.com/service-name=%s") + ) + }) + } + "should generate application health check given status module" - { val expectedJson = """ @@ -355,7 +418,7 @@ object DeploymentJsonTest extends TestSuite { | }, | { | "name": "RP_JAVA_OPTS", - | "value": "-Dconfig.resource=my-config.conf -Dakka.management.cluster.bootstrap.contact-point-discovery.discovery-method=kubernetes-api -Dakka.management.cluster.bootstrap.contact-point-discovery.port-name=management -Dakka.management.cluster.bootstrap.contact-point-discovery.effective-name=friendimpl -Dakka.management.cluster.bootstrap.contact-point-discovery.required-contact-point-nr=1 -Dakka.discovery.kubernetes-api.pod-label-selector=akka.lightbend.com/service-name=%s" + | "value": "-Dconfig.resource=my-config.conf -Dakka.management.cluster.bootstrap.contact-point-discovery.discovery-method=kubernetes-api -Dakka.management.cluster.bootstrap.contact-point-discovery.port-name=management -Dakka.management.cluster.bootstrap.contact-point-discovery.effective-name=friendimpl -Dakka.discovery.kubernetes-api.pod-label-selector=akka.lightbend.com/service-name=%s -Dakka.management.cluster.bootstrap.contact-point-discovery.required-contact-point-nr=1" | }, | { | "name": "RP_KUBERNETES_POD_IP", @@ -412,7 +475,8 @@ object DeploymentJsonTest extends TestSuite { val result = Deployment.generate(annotations.copy( modules = Set("akka-management", "status", "akka-cluster-bootstrapping"), managementEndpointName = Some("management")), "apps/v1beta2", None, imageName, - PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Default, noOfReplicas = 1, Map.empty, CanaryDeploymentType, JsonTransform.noop, false).toOption.get + PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Default, noOfReplicas = 1, Map.empty, + CanaryDeploymentType, DiscoveryMethod.KubernetesApi, JsonTransform.noop, false).toOption.get if (result.json != expectedJson) { println(s"deployment K8 JSON:\n" + PrettyParams.spaces2.copy(colonLeft = "").pretty(result.json)) } @@ -421,16 +485,16 @@ object DeploymentJsonTest extends TestSuite { "should fail if application name is not defined" - { val invalid = annotations.copy(appName = None) - assert(Deployment.generate(invalid, "apps/v1beta2", None, imageName, PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Default, 1, Map.empty, CanaryDeploymentType, JsonTransform.noop, false).toOption.isEmpty) + assert(Deployment.generate(invalid, "apps/v1beta2", None, imageName, PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Default, 1, Map.empty, CanaryDeploymentType, DiscoveryMethod.AkkaDns, JsonTransform.noop, false).toOption.isEmpty) } "should fail when restart policy is wrong" - { - assert(Deployment.generate(annotations, "apps/v1beta2", None, imageName, PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Never, 1, Map.empty, CanaryDeploymentType, JsonTransform.noop, false).toOption.isEmpty) + assert(Deployment.generate(annotations, "apps/v1beta2", None, imageName, PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Never, 1, Map.empty, CanaryDeploymentType, DiscoveryMethod.AkkaDns, JsonTransform.noop, false).toOption.isEmpty) } "jq" - { Deployment - .generate(annotations, "apps/v1beta2", None, imageName, PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Default, 1, Map.empty, CanaryDeploymentType, JsonTransform.jq(JsonTransformExpression(".jqTest = \"test\"")), false) + .generate(annotations, "apps/v1beta2", None, imageName, PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Default, 1, Map.empty, CanaryDeploymentType, DiscoveryMethod.AkkaDns, JsonTransform.jq(JsonTransformExpression(".jqTest = \"test\"")), false) .toOption .get .payload @@ -440,7 +504,7 @@ object DeploymentJsonTest extends TestSuite { "applications" - { "should select default given no application" - { Deployment - .generate(annotations.copy(applications = Vector("test" -> Vector("arg1", "arg2"), "default" -> Vector("def1"))), "apps/v1beta2", None, imageName, PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Default, 1, Map.empty, CanaryDeploymentType, JsonTransform.noop, false) + .generate(annotations.copy(applications = Vector("test" -> Vector("arg1", "arg2"), "default" -> Vector("def1"))), "apps/v1beta2", None, imageName, PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Default, 1, Map.empty, CanaryDeploymentType, DiscoveryMethod.AkkaDns, JsonTransform.noop, false) .toOption .get .payload @@ -458,7 +522,7 @@ object DeploymentJsonTest extends TestSuite { "should select requested application given an application" - { Deployment - .generate(annotations.copy(applications = Vector("test" -> Vector("arg1", "arg2"), "default" -> Vector("def1"))), "apps/v1beta2", Some("test"), imageName, PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Default, 1, Map.empty, CanaryDeploymentType, JsonTransform.noop, false) + .generate(annotations.copy(applications = Vector("test" -> Vector("arg1", "arg2"), "default" -> Vector("def1"))), "apps/v1beta2", Some("test"), imageName, PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Default, 1, Map.empty, CanaryDeploymentType, DiscoveryMethod.AkkaDns, JsonTransform.noop, false) .toOption .get .payload @@ -492,7 +556,7 @@ object DeploymentJsonTest extends TestSuite { val generatedJson = Deployment - .generate(annotations, "apps/v1beta2", None, imageName, PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Default, 1, Map.empty, CanaryDeploymentType, JsonTransform.noop, false) + .generate(annotations, "apps/v1beta2", None, imageName, PodTemplate.ImagePullPolicy.Never, PodTemplate.RestartPolicy.Default, 1, Map.empty, CanaryDeploymentType, DiscoveryMethod.AkkaDns, JsonTransform.noop, false) .toOption .get .payload diff --git a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/IngressJsonTest.scala b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/IngressJsonTest.scala index 73040f49..08fd46fd 100644 --- a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/IngressJsonTest.scala +++ b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/IngressJsonTest.scala @@ -46,6 +46,7 @@ object IngressJsonTest extends TestSuite { HttpIngress(Seq(80, 443), Seq("hello.com"), Seq.empty), HttpIngress(Seq(80, 443), Seq("hello.com", "world.io"), Seq(urlOne, urlTwo))))), managementEndpointName = None, + remotingEndpointName = None, secrets = Seq.empty, privileged = true, environmentVariables = Map( diff --git a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/JobJsonTest.scala b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/JobJsonTest.scala index b862adb9..7b95ad59 100644 --- a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/JobJsonTest.scala +++ b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/JobJsonTest.scala @@ -18,7 +18,7 @@ package com.lightbend.rp.reactivecli.runtime.kubernetes import argonaut._ import com.lightbend.rp.reactivecli.annotations.{ Annotations, LiteralEnvironmentVariable, Secret } -import com.lightbend.rp.reactivecli.argparse.CanaryDeploymentType +import com.lightbend.rp.reactivecli.argparse.{ CanaryDeploymentType, DiscoveryMethod } import com.lightbend.rp.reactivecli.json.JsonTransform import scala.collection.immutable.Seq import utest._ @@ -37,6 +37,7 @@ object JobJsonTest extends TestSuite { cpu = None, endpoints = Map.empty, managementEndpointName = None, + remotingEndpointName = None, secrets = Seq.empty, privileged = false, environmentVariables = Map.empty, @@ -58,6 +59,7 @@ object JobJsonTest extends TestSuite { noOfReplicas = 1, Map.empty, CanaryDeploymentType, + DiscoveryMethod.AkkaDns, JsonTransform.noop, true) @@ -112,6 +114,7 @@ object JobJsonTest extends TestSuite { noOfReplicas = 1, Map.empty, CanaryDeploymentType, + DiscoveryMethod.AkkaDns, JsonTransform.noop, true) diff --git a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/KubernetesPackageTest.scala b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/KubernetesPackageTest.scala index 63e2d143..86a9c77c 100644 --- a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/KubernetesPackageTest.scala +++ b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/KubernetesPackageTest.scala @@ -17,7 +17,7 @@ package com.lightbend.rp.reactivecli.runtime.kubernetes import argonaut._ -import com.lightbend.rp.reactivecli.argparse.GenerateDeploymentArgs +import com.lightbend.rp.reactivecli.argparse.{ GenerateDeploymentArgs, DiscoveryMethod } import com.lightbend.rp.reactivecli.argparse.kubernetes.KubernetesArgs import com.lightbend.rp.reactivecli.concurrent._ import com.lightbend.rp.reactivecli.docker.Config @@ -67,34 +67,43 @@ object KubernetesPackageTest extends TestSuite { "com.lightbend.rp.environment-variables.2.type" -> "kubernetes.fieldRef", "com.lightbend.rp.environment-variables.2.name" -> "testing3", "com.lightbend.rp.environment-variables.2.field-path" -> "metadata.name", - "com.lightbend.rp.endpoints.0.name" -> "ep1", - "com.lightbend.rp.endpoints.0.protocol" -> "http", - "com.lightbend.rp.endpoints.0.version" -> "9", - "com.lightbend.rp.endpoints.0.ingress.0.type" -> "http", - "com.lightbend.rp.endpoints.0.ingress.0.paths.0" -> "/pizza", - "com.lightbend.rp.endpoints.0.some-key" -> "test", - "com.lightbend.rp.endpoints.0.acls.0.some-key" -> "test", - "com.lightbend.rp.endpoints.1.name" -> "ep2", + "com.lightbend.rp.endpoints.0.name" -> "remoting", + "com.lightbend.rp.endpoints.0.protocol" -> "tcp", + "com.lightbend.rp.endpoints.0.port" -> "2552", + "com.lightbend.rp.endpoints.1.name" -> "management", "com.lightbend.rp.endpoints.1.protocol" -> "tcp", - "com.lightbend.rp.endpoints.1.version" -> "1", - "com.lightbend.rp.endpoints.1.port" -> "1234", + "com.lightbend.rp.endpoints.1.port" -> "8558", "com.lightbend.rp.endpoints.2.name" -> "ep3", - "com.lightbend.rp.endpoints.2.protocol" -> "udp", - "com.lightbend.rp.endpoints.2.port" -> "1234")))) + "com.lightbend.rp.endpoints.2.protocol" -> "http", + "com.lightbend.rp.endpoints.2.version" -> "9", + "com.lightbend.rp.endpoints.2.ingress.0.type" -> "http", + "com.lightbend.rp.endpoints.2.ingress.0.paths.0" -> "/pizza", + "com.lightbend.rp.endpoints.2.some-key" -> "test", + "com.lightbend.rp.endpoints.2.acls.0.some-key" -> "test", + "com.lightbend.rp.endpoints.3.name" -> "ep4", + "com.lightbend.rp.endpoints.3.protocol" -> "tcp", + "com.lightbend.rp.endpoints.3.version" -> "1", + "com.lightbend.rp.endpoints.3.port" -> "1234", + "com.lightbend.rp.endpoints.4.name" -> "ep5", + "com.lightbend.rp.endpoints.4.protocol" -> "udp", + "com.lightbend.rp.endpoints.4.port" -> "1234", + "com.lightbend.rp.remoting-endpoint" -> "remoting", + "com.lightbend.rp.management-endpoint" -> "management")))) "generates kubernetes deployment + service resource" - { val k8sArgs = kubernetesArgs.copy(generateNamespaces = true, namespace = Some("chirper")) - generateResources(imageName, dockerConfig, generateDeploymentArgs.copy(targetRuntimeArgs = Some(k8sArgs)), k8sArgs) + generateResources(imageName, dockerConfig, generateDeploymentArgs + .copy(targetRuntimeArgs = Some(k8sArgs), discoveryMethod = DiscoveryMethod.AkkaDns), k8sArgs) .map(_.toOption) .flatMap { result => assert(result.nonEmpty) val generatedResources = result.get - val (namespace, deployment, service, ingress) = generatedResources match { - case Seq(namespace: Namespace, deployment: Deployment, service: Service, ingress: Ingress) => - (namespace, deployment, service, ingress) + val (namespace, deployment, headlessService, service, ingress) = generatedResources match { + case Seq(namespace: Namespace, deployment: Deployment, headlessService: Service, service: Service, ingress: Ingress) => + (namespace, deployment, headlessService, service, ingress) } var asserts = List.empty[Future[Unit]] @@ -231,16 +240,24 @@ object KubernetesPackageTest extends TestSuite { | ], | "ports": [ | { + | "containerPort": 2552, + | "name": "remoting" + | }, + | { + | "containerPort": 8558, + | "name": "management" + | }, + | { | "containerPort": 10000, - | "name": "ep1" + | "name": "ep3" | }, | { | "containerPort": 1234, - | "name": "ep2" + | "name": "ep4" | }, | { | "containerPort": 1234, - | "name": "ep3" + | "name": "ep5" | } | ] | } @@ -253,6 +270,47 @@ object KubernetesPackageTest extends TestSuite { """.stripMargin.parse.right.get assertPayload("deployment", deployment, deploymentJsonExpected) + assert(headlessService.name == "my-app-internal") + val headlessServiceJsonExpected = + """ + |{ + | "apiVersion": "v1", + | "kind": "Service", + | "metadata": { + | "labels": { + | "app": "my-app" + | }, + | "annotations": { + | "service.alpha.kubernetes.io/tolerate-unready-endpoints" : "true" + | }, + | "name": "my-app-internal", + | "namespace": "chirper" + | }, + | "spec": { + | "ports": [ + | { + | "name": "management", + | "port": 8558, + | "protocol": "TCP", + | "targetPort": 8558 + | }, + | { + | "name": "remoting", + | "port": 2552, + | "protocol": "TCP", + | "targetPort": 2552 + | } + | ], + | "selector": { + | "app": "my-app" + | }, + | "clusterIP" : "None", + | "publishNotReadyAddresses" : true + | } + |} + """.stripMargin.parse.right.get + assertPayload("headless service", headlessService, headlessServiceJsonExpected) + assert(service.name == "my-app") val serviceJsonExpected = """ @@ -269,22 +327,22 @@ object KubernetesPackageTest extends TestSuite { | "spec": { | "ports": [ | { - | "name": "ep1", + | "name": "ep5", + | "port": 1234, + | "protocol": "UDP", + | "targetPort": 1234 + | }, + | { + | "name": "ep3", | "port": 10000, | "protocol": "TCP", | "targetPort": 10000 | }, | { - | "name": "ep2", + | "name": "ep4", | "port": 1234, | "protocol": "TCP", | "targetPort": 1234 - | }, - | { - | "name": "ep3", - | "port": 1234, - | "protocol": "UDP", - | "targetPort": 1234 | } | ], | "selector": { @@ -326,13 +384,278 @@ object KubernetesPackageTest extends TestSuite { } } + "generates kubernetes deployment + service resource using Kubernetes API" - { + val k8sArgs = kubernetesArgs.copy(generateNamespaces = true, namespace = Some("chirper")) + + generateResources(imageName, dockerConfig, generateDeploymentArgs + .copy(targetRuntimeArgs = Some(k8sArgs), discoveryMethod = DiscoveryMethod.KubernetesApi), k8sArgs) + .map(_.toOption) + .flatMap { result => + assert(result.nonEmpty) + + val generatedResources = result.get + + val (namespace, deployment, service, ingress) = generatedResources match { + case Seq(namespace: Namespace, deployment: Deployment, service: Service, ingress: Ingress) => + (namespace, deployment, service, ingress) + } + + var asserts = List.empty[Future[Unit]] + def assertPayload(label: String, generatedResource: GeneratedResource[Json], jsonExpected: Json): Unit = { + asserts ::= generatedResource.payload.map { p => + if (p != jsonExpected) + println(s"$label payload:\n" + PrettyParams.spaces2.copy(colonLeft = "").pretty(p)) + assert(p == jsonExpected) + } + } + + assert(namespace.name == "chirper") + val namespaceJsonExpected = + """ + |{ + | "apiVersion": "v1", + | "kind": "Namespace", + | "metadata": { + | "name": "chirper", + | "labels": { + | "name": "chirper" + | } + | } + |} + """.stripMargin.parse.right.get + assertPayload("namespace", namespace, namespaceJsonExpected) + + assert(deployment.name == "my-app-v3-2-1-snapshot") + val deploymentJsonExpected = + """ + |{ + | "apiVersion": "apps/v1beta2", + | "kind": "Deployment", + | "metadata": { + | "name": "my-app-v3-2-1-snapshot", + | "labels": { + | "app": "my-app", + | "appNameVersion": "my-app-v3-2-1-snapshot", + | "akka.lightbend.com/service-name": "my-app" + | }, + | "namespace": "chirper" + | }, + | "spec": { + | "replicas": 1, + | "selector": { + | "matchLabels": { + | "appNameVersion": "my-app-v3-2-1-snapshot" + | } + | }, + | "template": { + | "metadata": { + | "labels": { + | "app": "my-app", + | "appNameVersion": "my-app-v3-2-1-snapshot", + | "akka.lightbend.com/service-name": "my-app" + | } + | }, + | "spec": { + | "restartPolicy": "Always", + | "containers": [ + | { + | "name": "my-app", + | "image": "fsat/testimpl:1.0.0-SNAPSHOT", + | "resources": { + | "limits": { + | "cpu": 0.5, + | "memory": 8192 + | }, + | "requests": { + | "cpu": 0.5, + | "memory": 8192 + | } + | }, + | "imagePullPolicy": "IfNotPresent", + | "volumeMounts": [], + | "env": [ + | { + | "name": "RP_APP_NAME", + | "value": "my-app" + | }, + | { + | "name": "RP_APP_VERSION", + | "value": "3.2.1-SNAPSHOT" + | }, + | { + | "name": "RP_KUBERNETES_POD_IP", + | "valueFrom": { + | "fieldRef": { + | "fieldPath": "status.podIP" + | } + | } + | }, + | { + | "name": "RP_KUBERNETES_POD_NAME", + | "valueFrom": { + | "fieldRef": { + | "fieldPath": "metadata.name" + | } + | } + | }, + | { + | "name": "RP_NAMESPACE", + | "valueFrom": { + | "fieldRef": { + | "fieldPath": "metadata.namespace" + | } + | } + | }, + | { + | "name": "RP_PLATFORM", + | "value": "kubernetes" + | }, + | { + | "name": "testing1", + | "value": "testingvalue1" + | }, + | { + | "name": "testing2", + | "valueFrom": { + | "configMapKeyRef": { + | "name": "mymap", + | "key": "mykey" + | } + | } + | }, + | { + | "name": "testing3", + | "valueFrom": { + | "fieldRef": { + | "fieldPath": "metadata.name" + | } + | } + | } + | ], + | "ports": [ + | { + | "containerPort": 2552, + | "name": "remoting" + | }, + | { + | "containerPort": 8558, + | "name": "management" + | }, + | { + | "containerPort": 10000, + | "name": "ep3" + | }, + | { + | "containerPort": 1234, + | "name": "ep4" + | }, + | { + | "containerPort": 1234, + | "name": "ep5" + | } + | ] + | } + | ], + | "volumes": [] + | } + | } + | } + |} + """.stripMargin.parse.right.get + assertPayload("deployment", deployment, deploymentJsonExpected) + + assert(service.name == "my-app") + val serviceJsonExpected = + """ + |{ + | "apiVersion": "v1", + | "kind": "Service", + | "metadata": { + | "labels": { + | "app": "my-app" + | }, + | "name": "my-app", + | "namespace": "chirper" + | }, + | "spec": { + | "ports": [ + | { + | "name": "ep5", + | "port": 1234, + | "protocol": "UDP", + | "targetPort": 1234 + | }, + | { + | "name": "ep3", + | "port": 10000, + | "protocol": "TCP", + | "targetPort": 10000 + | }, + | { + | "name": "ep4", + | "port": 1234, + | "protocol": "TCP", + | "targetPort": 1234 + | }, + | { + | "name": "management", + | "port": 8558, + | "protocol": "TCP", + | "targetPort": 8558 + | }, + | { + | "name": "remoting", + | "port": 2552, + | "protocol": "TCP", + | "targetPort": 2552 + | } + | ], + | "selector": { + | "app": "my-app" + | } + | } + |} + """.stripMargin.parse.right.get + assertPayload("service", service, serviceJsonExpected) + + assert(ingress.name == "my-app") + val ingressJsonExpected = + """ + |{ + | "apiVersion": "extensions/v1beta1", + | "kind": "Ingress", + | "metadata": { + | "name": "my-app", + | "namespace": "chirper" + | }, + | "spec": { + | "rules": [{ + | "http": { + | "paths": [{ + | "path": "/pizza", + | "backend": { + | "serviceName": "my-app", + | "servicePort": 10000 + | } + | }] + | } + | }] + | } + |} + """.stripMargin.parse.right.get + assertPayload("ingress", ingress, ingressJsonExpected) + + Future.sequence(asserts.reverse) + } + } + "honor generate flags" - { "generateNamespaces" - { val k8sArgs = kubernetesArgs.copy(generateNamespaces = true, namespace = Some("test")) generateResources(imageName, dockerConfig, generateDeploymentArgs.copy(targetRuntimeArgs = Some(k8sArgs)), k8sArgs) .map(_.toOption.get) .map { result => - assert(result.length == 4) + assert(result.length == 5) assert(result.head.resourceType == "namespace") } } @@ -362,7 +685,7 @@ object KubernetesPackageTest extends TestSuite { generateResources(imageName, dockerConfig, generateDeploymentArgs.copy(targetRuntimeArgs = Some(k8sArgs)), k8sArgs) .map(_.toOption.get) .map { result => - assert(result.length == 1) + assert(result.length == 2) assert(result.head.resourceType == "service") } } diff --git a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/NamespaceJsonTest.scala b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/NamespaceJsonTest.scala index fa7872a8..7caee1a3 100644 --- a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/NamespaceJsonTest.scala +++ b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/NamespaceJsonTest.scala @@ -41,6 +41,7 @@ object NamespaceJsonTest extends TestSuite { cpu = None, endpoints = Map.empty, managementEndpointName = None, + remotingEndpointName = None, secrets = Seq.empty, privileged = false, environmentVariables = Map.empty, diff --git a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/ServiceJsonTest.scala b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/ServiceJsonTest.scala index e4b598df..d41953f6 100644 --- a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/ServiceJsonTest.scala +++ b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/kubernetes/ServiceJsonTest.scala @@ -18,7 +18,7 @@ package com.lightbend.rp.reactivecli.runtime.kubernetes import argonaut._ import com.lightbend.rp.reactivecli.annotations._ -import com.lightbend.rp.reactivecli.argparse.{ CanaryDeploymentType, BlueGreenDeploymentType, RollingDeploymentType } +import com.lightbend.rp.reactivecli.argparse.{ CanaryDeploymentType, BlueGreenDeploymentType, RollingDeploymentType, DiscoveryMethod } import com.lightbend.rp.reactivecli.concurrent._ import com.lightbend.rp.reactivecli.json.{ JsonTransform, JsonTransformExpression } import scala.collection.immutable.Seq @@ -38,8 +38,11 @@ object ServiceJsonTest extends TestSuite { memory = Some(8192L), cpu = Some(0.5D), endpoints = Map( - "ep1" -> TcpEndpoint(0, "ep1", 1234)), - managementEndpointName = None, + "remoting" -> TcpEndpoint(0, "remoting", 2552), + "management" -> TcpEndpoint(1, "management", 8558), + "ep3" -> TcpEndpoint(2, "ep3", 1234)), + managementEndpointName = Some("management"), + remotingEndpointName = Some("remoting"), secrets = Seq.empty, privileged = true, environmentVariables = Map( @@ -51,7 +54,7 @@ object ServiceJsonTest extends TestSuite { val tests = this{ "json serialization" - { "empty" - { - val result = Service.generate(annotations.copy(endpoints = Map.empty), "v1", clusterIp = None, CanaryDeploymentType, JsonTransform.noop, None, None).toOption.get.isEmpty + val result = Service.generate(annotations.copy(endpoints = Map.empty), "v1", clusterIp = None, CanaryDeploymentType, DiscoveryMethod.AkkaDns, JsonTransform.noop, None, None).toOption.get.isEmpty assert(result) } @@ -59,10 +62,10 @@ object ServiceJsonTest extends TestSuite { "deploymentType" - { "Canary" - { Service - .generate(annotations, "v1", clusterIp = None, CanaryDeploymentType, JsonTransform.noop, None, None) + .generate(annotations, "v1", clusterIp = None, CanaryDeploymentType, DiscoveryMethod.KubernetesApi, JsonTransform.noop, None, None) .toOption .get - .get + .head .payload .map { j => val result = (j.hcursor --\ "spec" --\ "selector" --\ "app").focus @@ -74,20 +77,20 @@ object ServiceJsonTest extends TestSuite { "BlueGreen" - { Service - .generate(annotations, "v1", clusterIp = None, BlueGreenDeploymentType, JsonTransform.noop, None, None) + .generate(annotations, "v1", clusterIp = None, BlueGreenDeploymentType, DiscoveryMethod.KubernetesApi, JsonTransform.noop, None, None) .toOption .get - .get + .head .payload .map(j => assert((j.hcursor --\ "spec" --\ "selector" --\ "appNameVersion").focus.contains(jString("friendimpl-v3-2-1-snapshot")))) } "Rolling" - { Service - .generate(annotations, "v1", clusterIp = None, RollingDeploymentType, JsonTransform.noop, None, None) + .generate(annotations, "v1", clusterIp = None, RollingDeploymentType, DiscoveryMethod.KubernetesApi, JsonTransform.noop, None, None) .toOption .get - .get + .head .payload .map(j => assert((j.hcursor --\ "spec" --\ "selector" --\ "app").focus.contains(jString("friendimpl")))) } @@ -95,17 +98,92 @@ object ServiceJsonTest extends TestSuite { "jq" - { Service - .generate(annotations, "v1", clusterIp = None, CanaryDeploymentType, JsonTransform.jq(JsonTransformExpression(".jqTest = \"test\"")), None, None) + .generate(annotations, "v1", clusterIp = None, CanaryDeploymentType, DiscoveryMethod.KubernetesApi, JsonTransform.jq(JsonTransformExpression(".jqTest = \"test\"")), None, None) .toOption .get - .get + .head .payload .map(j => assert((j.hcursor --\ "jqTest").focus.contains(jString("test")))) } + "discovery method" - { + "Akka DNS" - { + val generatedJson = Service.generate(annotations, "v1", clusterIp = None, CanaryDeploymentType, DiscoveryMethod.AkkaDns, JsonTransform.noop, None, None).toOption.get + val headlessJson = + """ + |{ + | "apiVersion": "v1", + | "kind": "Service", + | "metadata": { + | "labels": { + | "app": "friendimpl" + | }, + | "annotations": { + | "service.alpha.kubernetes.io/tolerate-unready-endpoints" : "true" + | }, + | "name": "friendimpl-internal", + | "namespace": "chirper" + | }, + | "spec": { + | "ports": [ + | { + | "name": "remoting", + | "port": 2552, + | "protocol": "TCP", + | "targetPort": 2552 + | }, + | { + | "name": "management", + | "port": 8558, + | "protocol": "TCP", + | "targetPort": 8558 + | } + | ], + | "selector": { + | "app": "friendimpl" + | }, + | "clusterIP" : "None", + | "publishNotReadyAddresses" : true + | } + |} + """.stripMargin.parse.right.get + val serviceJson = + """ + |{ + | "apiVersion": "v1", + | "kind": "Service", + | "metadata": { + | "labels": { + | "app": "friendimpl" + | }, + | "name": "friendimpl", + | "namespace": "chirper" + | }, + | "spec": { + | "ports": [ + | { + | "name": "ep3", + | "port": 1234, + | "protocol": "TCP", + | "targetPort": 1234 + | } + | ], + | "selector": { + | "app": "friendimpl" + | } + | } + |} + """.stripMargin.parse.right.get + assert(generatedJson == List( + Service("friendimpl-internal", headlessJson, JsonTransform.noop), + Service("friendimpl", serviceJson, JsonTransform.noop))) + + } + } + "options" - { "not defined" - { - val generatedJson = Service.generate(annotations, "v1", clusterIp = None, CanaryDeploymentType, JsonTransform.noop, None, None).toOption.get + val generatedJson = Service.generate(annotations, "v1", clusterIp = None, CanaryDeploymentType, DiscoveryMethod.KubernetesApi, JsonTransform.noop, None, None).toOption.get val expectedJson = """ |{ @@ -121,7 +199,19 @@ object ServiceJsonTest extends TestSuite { | "spec": { | "ports": [ | { - | "name": "ep1", + | "name": "remoting", + | "port": 2552, + | "protocol": "TCP", + | "targetPort": 2552 + | }, + | { + | "name": "management", + | "port": 8558, + | "protocol": "TCP", + | "targetPort": 8558 + | }, + | { + | "name": "ep3", | "port": 1234, | "protocol": "TCP", | "targetPort": 1234 @@ -133,11 +223,11 @@ object ServiceJsonTest extends TestSuite { | } |} """.stripMargin.parse.right.get - assert(generatedJson.get == Service("friendimpl", expectedJson, JsonTransform.noop)) + assert(generatedJson == List(Service("friendimpl", expectedJson, JsonTransform.noop))) } "defined" - { - val generatedJson = Service.generate(annotations, "v1", clusterIp = Some("10.0.0.5"), CanaryDeploymentType, JsonTransform.noop, Some("10.0.0.1"), Some("NodePort")).toOption.get + val generatedJson = Service.generate(annotations, "v1", clusterIp = Some("10.0.0.5"), CanaryDeploymentType, DiscoveryMethod.KubernetesApi, JsonTransform.noop, Some("10.0.0.1"), Some("NodePort")).toOption.get val expectedJson = """ |{ @@ -155,7 +245,19 @@ object ServiceJsonTest extends TestSuite { | "clusterIP": "10.0.0.5", | "ports": [ | { - | "name": "ep1", + | "name": "remoting", + | "port": 2552, + | "protocol": "TCP", + | "targetPort": 2552 + | }, + | { + | "name": "management", + | "port": 8558, + | "protocol": "TCP", + | "targetPort": 8558 + | }, + | { + | "name": "ep3", | "port": 1234, | "protocol": "TCP", | "targetPort": 1234 @@ -169,7 +271,7 @@ object ServiceJsonTest extends TestSuite { |} """.stripMargin.parse.right.get - assert(generatedJson.get == Service("friendimpl", expectedJson, JsonTransform.noop)) + assert(generatedJson == List(Service("friendimpl", expectedJson, JsonTransform.noop))) } } } diff --git a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/marathon/RpEnvironmentVariablesTest.scala b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/marathon/RpEnvironmentVariablesTest.scala index 44cdab01..a2d467c4 100644 --- a/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/marathon/RpEnvironmentVariablesTest.scala +++ b/cli/shared/src/test/scala/com/lightbend/rp/reactivecli/runtime/marathon/RpEnvironmentVariablesTest.scala @@ -43,6 +43,7 @@ object RpEnvironmentVariablesTest extends TestSuite { endpoints = Map( "ep1" -> TcpEndpoint(0, "ep1", 1234)), managementEndpointName = None, + remotingEndpointName = None, secrets = Seq.empty, privileged = true, environmentVariables = Map( @@ -91,6 +92,7 @@ object RpEnvironmentVariablesTest extends TestSuite { endpoints = Map( "ep1" -> TcpEndpoint(0, "ep1", 1234)), managementEndpointName = None, + remotingEndpointName = None, secrets = Seq.empty, privileged = true, environmentVariables = Map( @@ -141,6 +143,7 @@ object RpEnvironmentVariablesTest extends TestSuite { endpoints = Map( "ep1" -> TcpEndpoint(0, "ep1", 1234)), managementEndpointName = Some("management"), + remotingEndpointName = Some("remoting"), secrets = Seq.empty, privileged = true, environmentVariables = Map( @@ -191,6 +194,7 @@ object RpEnvironmentVariablesTest extends TestSuite { endpoints = Map( "ep1" -> TcpEndpoint(0, "ep1", 1234)), managementEndpointName = None, + remotingEndpointName = None, secrets = Seq.empty, privileged = true, environmentVariables = Map( diff --git a/integration-test/src/sbt-test/bootstrap-demo/kubernetes-api/build.sbt b/integration-test/src/sbt-test/bootstrap-demo/kubernetes-api/build.sbt index 308cedf9..00c0dfc9 100644 --- a/integration-test/src/sbt-test/bootstrap-demo/kubernetes-api/build.sbt +++ b/integration-test/src/sbt-test/bootstrap-demo/kubernetes-api/build.sbt @@ -45,7 +45,7 @@ lazy val root = (project in file(".")) val v = version.value val namespace = "reactivelibtest1" val rpPath = file(sys.props("reactiveclipath")) / "reactive-cli-out" - val out = Process(s"$rpPath generate-kubernetes-resources --registry-use-local --generate-all $nm:$v --pod-controller-replicas 3").!! + val out = Process(s"$rpPath generate-kubernetes-resources --registry-use-local --generate-all --discovery-method kubernetes-api $nm:$v --pod-controller-replicas 3").!! val x = if (!Deckhand.isOpenShift) out.replaceAllLiterally("imagePullPolicy: IfNotPresent", "imagePullPolicy: Never") diff --git a/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/build.sbt b/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/build.sbt new file mode 100644 index 00000000..fbe72234 --- /dev/null +++ b/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/build.sbt @@ -0,0 +1,87 @@ +// https://github.com/akka/akka-management/tree/master/bootstrap-demo/kubernetes-dns + +import Dependencies._ +import scala.sys.process.Process +import scala.util.control.NonFatal + +ThisBuild / version := "0.1.6" +ThisBuild / organization := "com.example" +ThisBuild / scalaVersion := "2.12.7" + +lazy val check = taskKey[Unit]("check") +lazy val generateYaml = taskKey[Unit]("generateYaml") + +lazy val root = (project in file(".")) + .enablePlugins(SbtReactiveAppPlugin) + .settings( + name := "bootstrap-kubernetes-dns-demo", + scalacOptions ++= Seq( + "-encoding", + "UTF-8", + "-feature", + "-unchecked", + "-deprecation", + "-Xlint", + "-Yno-adapted-args", + ), + libraryDependencies ++= Seq( + akkaManagement, + akkaClusterHttp, + akkaCluster, + akkaClusterSharding, + akkaClusterTools, + akkaDiscoveryDns, + akkaSlj4j, + logback, + scalaTest + ), + enableAkkaClusterBootstrap := true, + + // run nativeLink in the host build first + generateYaml := { + val s = streams.value + val nm = name.value + val v = version.value + val namespace = "reactivelibtest1" + val rpPath = file(sys.props("reactiveclipath")) / "reactive-cli-out" + val out = Process(s"$rpPath generate-kubernetes-resources --registry-use-local --generate-all $nm:$v --pod-controller-replicas 3 --stacktrace").!! + val x = + if (!Deckhand.isOpenShift) + out.replaceAllLiterally("imagePullPolicy: IfNotPresent", "imagePullPolicy: Never") + else out + .replaceAllLiterally("imagePullPolicy: IfNotPresent", "imagePullPolicy: Always") + .replaceAllLiterally("image: \"" + s"$nm:$v" + "\"", s"image: docker-registry-default.centralpark.lightbend.com/$namespace/$nm:$v") + s.log.info("generated YAML: " + x) + IO.write(target.value / "temp.yaml", x) + }, + + // this logic was taken from test.sh + check := { + val s = streams.value + val nm = name.value + val v = version.value + val namespace = "reactivelibtest1" + val kubectl = Deckhand.kubectl(s.log) + val docker = Deckhand.docker(s.log) + + try { + if (!Deckhand.isOpenShift) { + kubectl.tryCreate(s"namespace $namespace") + kubectl.setCurrentNamespace(namespace) + } else { + // work around: /rp-start: line 60: /opt/docker/bin/bootstrap-kapi-demo: Permission denied + kubectl.command(s"adm policy add-scc-to-user anyuid system:serviceaccount:$namespace:default") + kubectl.command(s"policy add-role-to-user system:image-builder system:serviceaccount:$namespace:default") + docker.tag(s"$nm:$v docker-registry-default.centralpark.lightbend.com/$namespace/$nm:$v") + docker.push(s"docker-registry-default.centralpark.lightbend.com/$namespace/$nm") + } + kubectl.apply(target.value / "temp.yaml") + kubectl.waitForPods(3) + kubectl.describe("pods") + kubectl.checkAkkaCluster(3, _.contains(nm)) + } finally { + kubectl.delete(s"services,pods,deployment --all --namespace $namespace") + kubectl.waitForPods(0) + } + } + ) diff --git a/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/project/Dependencies.scala b/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/project/Dependencies.scala new file mode 100644 index 00000000..c8759277 --- /dev/null +++ b/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/project/Dependencies.scala @@ -0,0 +1,21 @@ +import sbt._ + +object Dependencies { + val akkaVersion = "2.5.19" + val akkaManagementVersion = "0.20.0" + + val akkaActor = "com.typesafe.akka" %% "akka-actor" % akkaVersion + val akkaCluster = "com.typesafe.akka" %% "akka-cluster" % akkaVersion + val akkaClusterSharding = "com.typesafe.akka" %% "akka-cluster-sharding" % akkaVersion + val akkaClusterTools = "com.typesafe.akka" %% "akka-cluster-tools" % akkaVersion + val akkaSlj4j = "com.typesafe.akka" %% "akka-slf4j" % akkaVersion + + val akkaManagement = "com.lightbend.akka.management" %% "akka-management" % akkaManagementVersion + val akkaBootstrap = "com.lightbend.akka.management" %% "akka-management-cluster-bootstrap" % akkaManagementVersion + val akkaDiscoveryDns = "com.lightbend.akka.discovery" %% "akka-discovery-dns" % akkaManagementVersion + val akkaClusterHttp = "com.lightbend.akka.management" %% "akka-management-cluster-http" % akkaManagementVersion + + val logback = "ch.qos.logback" % "logback-classic" % "1.2.3" + + val scalaTest = "org.scalatest" %% "scalatest" % "3.0.5" % Test +} diff --git a/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/project/build.properties b/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/project/build.properties new file mode 100644 index 00000000..ee0a5b57 --- /dev/null +++ b/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/project/build.properties @@ -0,0 +1,2 @@ +sbt.version=1.2.7 + diff --git a/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/project/plugins.sbt b/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/project/plugins.sbt new file mode 100644 index 00000000..fbb53bc1 --- /dev/null +++ b/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/project/plugins.sbt @@ -0,0 +1,2 @@ +addSbtPlugin("com.lightbend.rp" % "sbt-reactive-app" % "1.7.0-M1") +addSbtPlugin("com.lightbend.rp" % "sbt-deckhand" % "0.1.0") diff --git a/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/src/main/resources/application.conf b/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/src/main/resources/application.conf new file mode 100644 index 00000000..be0b09ed --- /dev/null +++ b/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/src/main/resources/application.conf @@ -0,0 +1,3 @@ +akka { + loglevel = DEBUG +} diff --git a/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/src/main/scala/foo/ClusterApp.scala b/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/src/main/scala/foo/ClusterApp.scala new file mode 100644 index 00000000..cdc16437 --- /dev/null +++ b/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/src/main/scala/foo/ClusterApp.scala @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2017-2018 Lightbend Inc. + */ + +package foo + +import akka.actor.{ Actor, ActorLogging, ActorSystem, PoisonPill, Props } +import akka.cluster.ClusterEvent.ClusterDomainEvent +import akka.cluster.singleton.{ ClusterSingletonManager, ClusterSingletonManagerSettings } +import akka.cluster.{ Cluster, ClusterEvent } +import akka.http.scaladsl.Http +import akka.http.scaladsl.model._ +import akka.http.scaladsl.server.Directives._ +import akka.stream.ActorMaterializer + +object ClusterApp { + + def main(args: Array[String]): Unit = { + + implicit val system = ActorSystem() + implicit val materializer = ActorMaterializer() + implicit val executionContext = system.dispatcher + + val cluster = Cluster(system) + system.log.info("Starting Akka Management") + system.log.info("something2") + // AkkaManagement(system).start() + // ClusterBootstrap(system).start() + + system.actorOf( + ClusterSingletonManager.props( + Props[NoisySingleton], + PoisonPill, + ClusterSingletonManagerSettings(system))) + Cluster(system).subscribe( + system.actorOf(Props[ClusterWatcher]), + ClusterEvent.InitialStateAsEvents, + classOf[ClusterDomainEvent]) + + // add real app routes here + val routes = + path("hello") { + get { + complete( + HttpEntity(ContentTypes.`text/html(UTF-8)`, "

Hello

")) + } + } + + Http().bindAndHandle(routes, "0.0.0.0", 8080) + + system.log.info( + s"Server online at http://localhost:8080/\nPress RETURN to stop...") + + cluster.registerOnMemberUp(() => { + system.log.info("Cluster member is up!") + }) + } + + class ClusterWatcher extends Actor with ActorLogging { + val cluster = Cluster(context.system) + + override def receive = { + case msg ⇒ log.info(s"Cluster ${cluster.selfAddress} >>> " + msg) + } + } +} diff --git a/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/src/main/scala/foo/NoisySingleton.scala b/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/src/main/scala/foo/NoisySingleton.scala new file mode 100644 index 00000000..f874b1a6 --- /dev/null +++ b/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/src/main/scala/foo/NoisySingleton.scala @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017-2018 Lightbend Inc. + */ + +package foo + +import akka.actor.Actor +import akka.actor.ActorLogging + +class NoisySingleton extends Actor with ActorLogging { + + override def preStart(): Unit = + log.info("Noisy singleton started") + + override def postStop(): Unit = + log.info("Noisy singleton stopped") + + override def receive: Receive = { + case msg => log.info("Msg: {}", msg) + } +} diff --git a/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/test b/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/test new file mode 100644 index 00000000..acd08eb1 --- /dev/null +++ b/integration-test/src/sbt-test/bootstrap-demo/kubernetes-dns/test @@ -0,0 +1,4 @@ +> Docker/publishLocal +> generateYaml +$exists target/temp.yaml +> check