├── .gitignore ├── .travis.yml ├── README.md ├── build.sbt ├── grpc-service └── src │ └── main │ ├── protobuf │ └── helloworld.proto │ ├── resources │ └── application.conf │ └── scala │ └── com │ └── example │ └── helloworld │ ├── GreeterServer.scala │ └── GreeterServiceImpl.scala ├── http-to-grpc └── src │ └── main │ ├── protobuf │ └── helloworld.proto │ ├── resources │ ├── application.conf │ └── logback.xml │ └── scala │ └── com │ └── example │ └── helloworld │ └── HttpToGrpc.scala ├── kubernetes ├── grpcservice.yml └── httptogrpc.yml ├── project ├── build.properties └── plugins.sbt └── scripts ├── setup-minikube-for-linux.sh └── test.sh /.gitignore: -------------------------------------------------------------------------------- 1 | /grpc-service/target/ 2 | /http-to-grpc/target/ 3 | /project/project/ 4 | /project/target/ 5 | /target/ 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | version: ~> 1.0 2 | dist: xenial 3 | language: scala 4 | addons: 5 | apt: 6 | packages: 7 | - conntrack 8 | scala: 9 | - 2.12.12 10 | env: 11 | - TRAVIS_JDK=adopt@1.8.0-272 12 | - TRAVIS_JDK=adopt@1.11.0-9 13 | 14 | before_install: 15 | - curl -sL https://raw.githubusercontent.com/shyiko/jabba/0.11.2/install.sh | bash && . ~/.jabba/jabba.sh 16 | 17 | install: 18 | - $JABBA_HOME/bin/jabba install $TRAVIS_JDK && export JAVA_HOME="$JABBA_HOME/jdk/$TRAVIS_JDK" && export PATH="$JAVA_HOME/bin:$PATH" && java -Xmx32m -version 19 | 20 | script: 21 | - ./scripts/setup-minikube-for-linux.sh && ./scripts/test.sh 22 | 23 | cache: 24 | directories: 25 | - $HOME/.cache/coursier 26 | - $HOME/.ivy2/cache 27 | - $HOME/.sbt 28 | - $HOME/.jabba/jdk 29 | 30 | before_cache: 31 | - rm -rf $HOME/.ivy2/cache/com.typesafe.play/* 32 | - rm -rf $HOME/.ivy2/cache/scala_*/sbt_*/com.typesafe.play/* 33 | - find $HOME/.ivy2/cache -name "ivydata-*.properties" -print -delete 34 | - find $HOME/.sbt -name "*.lock" -print -delete 35 | 36 | # safelist 37 | branches: 38 | only: 39 | - master 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Akka gRPC Kubernetes 2 | 3 | This is an example of an [Akka HTTP](https://doc.akka.io/docs/akka-http/current) application communicating with an [Akka gRPC](https://developer.lightbend.com/docs/akka-grpc/current/) application inside of Kubernetes. 4 | 5 | This example does not show Akka Cluster. If you are interested in Akka Cluster, see for the 'Cluster' examples (for [Scala](https://developer.lightbend.com/start/?group=akka&project=akka-samples-cluster-scala) or [Java](https://developer.lightbend.com/start/?group=akka&project=akka-samples-cluster-java)), 'Akka Cluster with docker-compose' (for [Scala](https://developer.lightbend.com/start/?group=akka&project=akka-sample-cluster-docker-compose-scala) or [Java](https://developer.lightbend.com/start/?group=akka&project=akka-sample-cluster-docker-compose-java)) or 'Akka Cluster on Kubernetes (for [Java](https://developer.lightbend.com/start/?group=akka&project=akka-sample-cluster-kubernetes-java)) 6 | 7 | The Akka HTTP application discovers the Akka gRPC application using [Akka Discovery](https://developer.lightbend.com/docs/akka-management/current/discovery.html). 8 | It uses the `akka-dns` mechanism which relies on the `SRV` records created by kubernetes. 9 | 10 | All the technologies used in this example are open source. 11 | 12 | ## Other approaches 13 | 14 | This project uses sbt and the Scala language. 15 | 16 | If you are using Java and sbt you can use exactly the same approach. 17 | 18 | If you are using another build tool, such as Maven or Gradle, the code would 19 | still be the same, but you would have to build the Docker image and deploy it to Kubernetes yourself. 20 | 21 | ## Usage 22 | 23 | ### Prerequisites 24 | 25 | Install the following: 26 | 27 | * [Docker](https://docs.docker.com/install/) 28 | * [Kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) 29 | * [Minikube](https://github.com/kubernetes/minikube) 30 | * [Sbt](https://www.scala-sbt.org/) 31 | 32 | ### Running 33 | 34 | Once minikube is running and ingress enabled with `minikube addons enable ingress`, the two applications can be deployed using: 35 | 36 | `kubectl apply -f kubernetes/grpcservice.yml` 37 | 38 | and 39 | 40 | `kubectl apply -f kubernetes/httptogrpc.yml` 41 | 42 | Verify the deployments: 43 | 44 | ``` 45 | $ kubectl get deployments 46 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 47 | grpcservice-v0-1-0-snapshot 1 1 1 1 40s 48 | httptogrpc-v0-1-0-snapshot 1 1 1 1 40s 49 | 50 | ``` 51 | 52 | There are services for both: 53 | ``` 54 | $ kubectl get services 55 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 56 | grpcservice ClusterIP 10.106.188.203 8080/TCP 1m 57 | httptogrpc ClusterIP 10.103.134.197 8080/TCP 1m 58 | ``` 59 | 60 | Ingress just for the HTTP app: 61 | 62 | ``` 63 | $ kubectl get ingress 64 | NAME HOSTS ADDRESS PORTS AGE 65 | httptogrpc superservice.com 80 2m 66 | ``` 67 | 68 | The HTTP application periodically hits the gRPC applicaton which you can see in the logs: 69 | 70 | ``` 71 | [INFO] [08/15/2018 07:02:51.712] [HttpToGrpc-akka.actor.default-dispatcher-4] [akka.actor.ActorSystemImpl(HttpToGrpc)] Scheduled say hello to chris 72 | [INFO] [08/15/2018 07:02:51.730] [HttpToGrpc-akka.actor.default-dispatcher-4] [akka.actor.ActorSystemImpl(HttpToGrpc)] Scheduled say hello response Success(HelloReply(Hello, Christopher)) 73 | ``` 74 | 75 | And you can send a HTTP request via `Ingress` to the `httptogrpc` application: 76 | 77 | ``` 78 | $ curl -v --header 'Host: superservice.com' $(minikube ip)/hello/donkey 79 | > GET /hello/donkey HTTP/1.1 80 | > Host: superservice.com 81 | > User-Agent: curl/7.59.0 82 | > Accept: */* 83 | > 84 | < HTTP/1.1 200 OK 85 | < Server: nginx/1.13.12 86 | < Date: Wed, 15 Aug 2018 07:03:56 GMT 87 | < Content-Type: text/plain; charset=UTF-8 88 | < Content-Length: 13 89 | < Connection: keep-alive 90 | < 91 | * Connection #0 to host 192.168.99.100 left intact 92 | Hello, donkey% 93 | ``` 94 | 95 | The `Host` header needs to be set as that is how minikube [Ingress](https://github.com/kubernetes/ingress-nginx) routes requests to services. 96 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | name := "akka-grpc-kubernetes" 2 | scalaVersion := "2.13.3" 3 | 4 | resolvers += "Akka library repository".at("https://repo.akka.io/maven") 5 | 6 | lazy val akkaVersion = "2.6.14" 7 | lazy val discoveryVersion = "1.0.9" 8 | lazy val akkaHttpVersion = "10.2.3" 9 | 10 | lazy val root = (project in file(".")) 11 | .aggregate(httpToGrpc, grpcService) 12 | 13 | // Http front end that calls out to a gRPC back end 14 | lazy val httpToGrpc = (project in file("http-to-grpc")) 15 | .enablePlugins(AkkaGrpcPlugin, DockerPlugin, JavaAppPackaging) 16 | .settings( 17 | libraryDependencies ++= Seq( 18 | "com.typesafe.akka" %% "akka-actor-typed" % akkaVersion, 19 | "com.typesafe.akka" %% "akka-slf4j" % akkaVersion, 20 | "com.typesafe.akka" %% "akka-discovery" % akkaVersion, 21 | "com.typesafe.akka" %% "akka-stream" % akkaVersion, 22 | 23 | "com.typesafe.akka" %% "akka-parsing" % akkaHttpVersion, 24 | "com.typesafe.akka" %% "akka-http-core" % akkaHttpVersion, 25 | "com.typesafe.akka" %% "akka-http" % akkaHttpVersion, 26 | "com.typesafe.akka" %% "akka-http-spray-json" % akkaHttpVersion, 27 | "com.typesafe.akka" %% "akka-http2-support" % akkaHttpVersion, 28 | 29 | "com.lightbend.akka.discovery" %% "akka-discovery-kubernetes-api" % discoveryVersion, 30 | 31 | "ch.qos.logback" % "logback-classic" % "1.2.3" 32 | ), 33 | dockerExposedPorts := Seq(8080), 34 | ) 35 | 36 | lazy val grpcService = (project in file("grpc-service")) 37 | .enablePlugins(AkkaGrpcPlugin, DockerPlugin, JavaAppPackaging) 38 | .settings( 39 | dockerExposedPorts := Seq(8080), 40 | libraryDependencies ++= Seq( 41 | "com.typesafe.akka" %% "akka-actor" % akkaVersion, 42 | "com.typesafe.akka" %% "akka-actor-typed" % akkaVersion, 43 | "com.typesafe.akka" %% "akka-slf4j" % akkaVersion, 44 | "com.typesafe.akka" %% "akka-stream" % akkaVersion, 45 | "com.typesafe.akka" %% "akka-discovery" % akkaVersion, 46 | 47 | "com.typesafe.akka" %% "akka-http" % akkaHttpVersion, 48 | "com.typesafe.akka" %% "akka-http2-support" % akkaHttpVersion, 49 | 50 | "ch.qos.logback" % "logback-classic" % "1.2.3" 51 | ) 52 | ) 53 | 54 | 55 | -------------------------------------------------------------------------------- /grpc-service/src/main/protobuf/helloworld.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option java_multiple_files = true; 4 | option java_package = "com.example.helloworld"; 5 | option java_outer_classname = "HelloWorldProto"; 6 | 7 | service GreeterService { 8 | rpc SayHello (HelloRequest) returns (HelloReply) {} 9 | rpc SayHelloToAll (stream HelloRequest) returns (stream HelloReply) {} 10 | } 11 | 12 | message HelloRequest { 13 | string name = 1; 14 | } 15 | 16 | message HelloReply { 17 | string message = 1; 18 | } 19 | -------------------------------------------------------------------------------- /grpc-service/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | akka { 2 | loglevel = "DEBUG" 3 | http.server.preview.enable-http2 = on 4 | } 5 | -------------------------------------------------------------------------------- /grpc-service/src/main/scala/com/example/helloworld/GreeterServer.scala: -------------------------------------------------------------------------------- 1 | package com.example.helloworld 2 | 3 | import akka.actor.ActorSystem 4 | import akka.http.scaladsl.model.{HttpRequest, HttpResponse} 5 | import akka.http.scaladsl.{Http, HttpConnectionContext} 6 | import akka.stream.{ActorMaterializer, Materializer} 7 | 8 | import scala.concurrent.{ExecutionContext, Future} 9 | 10 | 11 | object GreeterServer { 12 | 13 | def main(args: Array[String]): Unit = { 14 | val system: ActorSystem = ActorSystem("GreeterServer") 15 | new GreeterServer(system).run() 16 | } 17 | } 18 | 19 | class GreeterServer(system: ActorSystem) { 20 | 21 | def run(): Future[Http.ServerBinding] = { 22 | implicit val sys: ActorSystem = system 23 | implicit val mat: Materializer = Materializer(sys) 24 | implicit val ec: ExecutionContext = sys.dispatcher 25 | 26 | val service: HttpRequest => Future[HttpResponse] = 27 | GreeterServiceHandler(new GreeterServiceImpl(mat, system.log)) 28 | 29 | val bound = Http().newServerAt("0.0.0.0", 8080).bind(service) 30 | 31 | bound.foreach { binding => 32 | sys.log.info("gRPC server bound to: {}", binding.localAddress) 33 | } 34 | 35 | bound 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /grpc-service/src/main/scala/com/example/helloworld/GreeterServiceImpl.scala: -------------------------------------------------------------------------------- 1 | package com.example.helloworld 2 | 3 | import scala.concurrent.Future 4 | import akka.NotUsed 5 | import akka.event.LoggingAdapter 6 | import akka.stream.Materializer 7 | import akka.stream.scaladsl.BroadcastHub 8 | import akka.stream.scaladsl.Keep 9 | import akka.stream.scaladsl.MergeHub 10 | import akka.stream.scaladsl.Sink 11 | import akka.stream.scaladsl.Source 12 | 13 | 14 | class GreeterServiceImpl(materializer: Materializer, log: LoggingAdapter) extends GreeterService { 15 | 16 | 17 | private implicit val mat: Materializer = materializer 18 | 19 | val (inboundHub: Sink[HelloRequest, NotUsed], outboundHub: Source[HelloReply, NotUsed]) = 20 | MergeHub.source[HelloRequest] 21 | .map(request => HelloReply(s"Hello, ${request.name}")) 22 | .toMat(BroadcastHub.sink[HelloReply])(Keep.both) 23 | .run() 24 | 25 | override def sayHello(request: HelloRequest): Future[HelloReply] = { 26 | log.info("sayHello {}", request) 27 | Future.successful(HelloReply(s"Hello, ${request.name}")) 28 | } 29 | 30 | override def sayHelloToAll(in: Source[HelloRequest, NotUsed]): Source[HelloReply, NotUsed] = { 31 | log.info("sayHelloToAll") 32 | in.runWith(inboundHub) 33 | outboundHub 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /http-to-grpc/src/main/protobuf/helloworld.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option java_multiple_files = true; 4 | option java_package = "com.example.helloworld"; 5 | option java_outer_classname = "HelloWorldProto"; 6 | 7 | service GreeterService { 8 | rpc SayHello (HelloRequest) returns (HelloReply) {} 9 | 10 | rpc SayHelloToAll (stream HelloRequest) returns (stream HelloReply) {} 11 | } 12 | 13 | message HelloRequest { 14 | string name = 1; 15 | } 16 | 17 | message HelloReply { 18 | string message = 1; 19 | } 20 | -------------------------------------------------------------------------------- /http-to-grpc/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | akka.grpc.client { 2 | "helloworld.GreeterService" { 3 | service-discovery { 4 | mechanism = "akka-dns" 5 | service-name = "grpcservice.default.svc.cluster.local" 6 | protocol = "tcp" 7 | port-name = "http" 8 | } 9 | use-tls = false 10 | } 11 | } 12 | 13 | akka { 14 | loglevel = DEBUG 15 | discovery.method = akka-dns 16 | io.dns.resolver = async-dns 17 | } 18 | -------------------------------------------------------------------------------- /http-to-grpc/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | System.out 6 | 7 | %date{MM/dd HH:mm:ss} %-5level[%thread] %logger{1} - %m%n%xException 8 | 9 | 10 | 11 | 12 | 1024 13 | true 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /http-to-grpc/src/main/scala/com/example/helloworld/HttpToGrpc.scala: -------------------------------------------------------------------------------- 1 | package com.example.helloworld 2 | 3 | import akka.actor.ActorSystem 4 | import akka.event.LoggingAdapter 5 | import akka.grpc.GrpcClientSettings 6 | import akka.http.scaladsl.Http 7 | import akka.http.scaladsl.model._ 8 | import akka.http.scaladsl.server.Directives._ 9 | import akka.stream.{ActorMaterializer, Materializer} 10 | 11 | import scala.concurrent.duration._ 12 | import scala.concurrent.{ExecutionContext, Future} 13 | import scala.util.{Failure, Success} 14 | 15 | 16 | object HttpToGrpc { 17 | 18 | def main(args: Array[String]): Unit = { 19 | implicit val system: ActorSystem = ActorSystem("HttpToGrpc") 20 | implicit val mat: Materializer = Materializer(system) 21 | implicit val ec: ExecutionContext = system.dispatcher 22 | val log: LoggingAdapter = system.log 23 | 24 | val settings = GrpcClientSettings.fromConfig("helloworld.GreeterService") 25 | val client = GreeterServiceClient(settings) 26 | 27 | system.scheduler.scheduleAtFixedRate(5.seconds, 5.seconds)(() => { 28 | log.info("Scheduled say hello to chris") 29 | val response: Future[HelloReply] = client.sayHello(HelloRequest("Christopher")) 30 | response.onComplete { r => 31 | log.info("Scheduled say hello response {}", r) 32 | } 33 | }) 34 | 35 | val route = 36 | path("hello" / Segment) { name => 37 | get { 38 | log.info("hello request") 39 | onComplete(client.sayHello(HelloRequest(name))) { 40 | case Success(reply) => complete(reply.message) 41 | case Failure(t) => 42 | log.error(t, "Request failed") 43 | complete(StatusCodes.InternalServerError, t.getMessage) 44 | } 45 | } 46 | } 47 | 48 | val bindingFuture = Http().newServerAt("0.0.0.0", 8080).bindFlow(route) 49 | bindingFuture.onComplete { 50 | case Success(sb) => 51 | log.info("Bound: {}", sb) 52 | case Failure(t) => 53 | log.error(t, "Failed to bind. Shutting down") 54 | system.terminate() 55 | } 56 | 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /kubernetes/grpcservice.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: "apps/v1" 3 | kind: Deployment 4 | metadata: 5 | name: "grpcservice-v0-1-0-snapshot" 6 | labels: 7 | appName: grpcservice 8 | appNameVersion: "grpcservice-v0-1-0-snapshot" 9 | spec: 10 | replicas: 1 11 | selector: 12 | matchLabels: 13 | appNameVersion: "grpcservice-v0-1-0-snapshot" 14 | template: 15 | metadata: 16 | labels: 17 | appName: grpcservice 18 | appNameVersion: "grpcservice-v0-1-0-snapshot" 19 | spec: 20 | restartPolicy: Always 21 | containers: 22 | - name: grpcservice 23 | image: "grpcservice:0.1.0-SNAPSHOT" 24 | imagePullPolicy: IfNotPresent 25 | ports: 26 | - containerPort: 8080 27 | name: http 28 | volumeMounts: [] 29 | volumes: [] 30 | --- 31 | apiVersion: v1 32 | kind: Service 33 | metadata: 34 | labels: 35 | appName: grpcservice 36 | name: grpcservice 37 | spec: 38 | ports: 39 | - name: http 40 | port: 8080 41 | protocol: TCP 42 | targetPort: 8080 43 | selector: 44 | appName: grpcservice 45 | -------------------------------------------------------------------------------- /kubernetes/httptogrpc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: "apps/v1" 3 | kind: Deployment 4 | metadata: 5 | name: "httptogrpc-v0-1-0-snapshot" 6 | labels: 7 | appName: httptogrpc 8 | appNameVersion: "httptogrpc-v0-1-0-snapshot" 9 | spec: 10 | replicas: 1 11 | selector: 12 | matchLabels: 13 | appNameVersion: "httptogrpc-v0-1-0-snapshot" 14 | template: 15 | metadata: 16 | labels: 17 | appName: httptogrpc 18 | appNameVersion: "httptogrpc-v0-1-0-snapshot" 19 | spec: 20 | restartPolicy: Always 21 | containers: 22 | - name: httptogrpc 23 | image: "httptogrpc:0.1.0-SNAPSHOT" 24 | imagePullPolicy: IfNotPresent 25 | ports: 26 | - containerPort: 8080 27 | name: http 28 | volumeMounts: [] 29 | volumes: [] 30 | --- 31 | apiVersion: v1 32 | kind: Service 33 | metadata: 34 | labels: 35 | appName: httptogrpc 36 | name: httptogrpc 37 | spec: 38 | ports: 39 | - name: http 40 | port: 8080 41 | protocol: TCP 42 | targetPort: 8080 43 | selector: 44 | appName: httptogrpc 45 | --- 46 | apiVersion: "extensions/v1beta1" 47 | kind: Ingress 48 | metadata: 49 | name: httptogrpc 50 | spec: 51 | rules: 52 | - host: "superservice.com" 53 | http: 54 | paths: 55 | - backend: 56 | serviceName: httptogrpc 57 | servicePort: 8080 58 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.4.7 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.lightbend.akka.grpc" % "sbt-akka-grpc" % "1.1.0") 2 | addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.8.0") 3 | -------------------------------------------------------------------------------- /scripts/setup-minikube-for-linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -exu 4 | 5 | # using Minikube v1.16.0 fails 6 | MINIKUBE_VERSION="v1.15.1" 7 | 8 | # From https://minikube.sigs.k8s.io/docs/tutorials/continuous_integration/ 9 | curl -Lo minikube https://storage.googleapis.com/minikube/releases/${MINIKUBE_VERSION}/minikube-linux-amd64 && chmod +x minikube && sudo cp minikube /usr/local/bin/ && rm minikube 10 | curl -Lo kubectl https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && chmod +x kubectl && sudo cp kubectl /usr/local/bin/ && rm kubectl 11 | 12 | export MINIKUBE_WANTUPDATENOTIFICATION=false 13 | export MINIKUBE_WANTREPORTERRORPROMPT=false 14 | export MINIKUBE_HOME=$HOME 15 | export CHANGE_MINIKUBE_NONE_USER=true 16 | mkdir -p $HOME/.kube 17 | touch $HOME/.kube/config 18 | 19 | export KUBECONFIG=$HOME/.kube/config 20 | minikube start --driver=docker 21 | minikube addons enable ingress 22 | #sudo -E chmod a+r ~/.minikube/client.key 23 | 24 | # this for loop waits until kubectl can access the api server that Minikube has created 25 | set +e 26 | for i in {1..150}; do # timeout for 5 minutes 27 | kubectl get po &> /dev/null 28 | if [ $? -ne 1 ]; then 29 | break 30 | fi 31 | sleep 2 32 | done 33 | 34 | if [ $i -eq 150 ] 35 | then 36 | echo "Kubectl is not ready" 37 | kubectl get po 38 | exit 1 39 | fi 40 | 41 | echo "Kubectl is now ready" 42 | kubectl get po 43 | minikube version 44 | minikube addons list 45 | sudo -E chmod a+r ~/.minikube/machines/minikube/config.json 46 | sudo -E chmod a+r /home/travis/.minikube/machines/minikube/config.json 47 | 48 | # kubectl commands are now able to interact with Minikube cluster 49 | -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -exu 4 | eval $(minikube -p minikube docker-env) 5 | sbt docker:publishLocal 6 | 7 | kubectl apply -f kubernetes/grpcservice.yml 8 | kubectl apply -f kubernetes/httptogrpc.yml 9 | 10 | for i in {1..10} 11 | do 12 | echo "Waiting for pods to get ready..." 13 | kubectl get pods 14 | [ `kubectl get pods | grep Running | wc -l` -eq 2 ] && break 15 | sleep 4 16 | done 17 | 18 | if [ $i -eq 10 ] 19 | then 20 | echo "Pods did not get ready" 21 | exit -1 22 | fi 23 | 24 | for i in {1..10} 25 | do 26 | REPLY=`curl --header 'Host: superservice.com' $(sudo -E minikube ip)/hello/donkey || true` 27 | [ "$REPLY" = 'Hello, donkey' ] && break 28 | sleep 4 29 | done 30 | 31 | if [ $i -eq 10 ] 32 | then 33 | echo "Got reply '$REPLY' instead of 'Hello, donkey'" 34 | kubectl get pods | tail -2 | cut -d " " -f 1 | while read line ; do echo "=== $line ==="; kubectl logs $line ; done 35 | exit -1 36 | fi 37 | --------------------------------------------------------------------------------