Skip to content

Commit

Permalink
added stack module
Browse files Browse the repository at this point in the history
  • Loading branch information
daviddenton committed Jun 24, 2016
1 parent 2a99a9d commit e41c3d5
Show file tree
Hide file tree
Showing 101 changed files with 1,902 additions and 142 deletions.
23 changes: 19 additions & 4 deletions build.sbt
@@ -1,3 +1,5 @@
val jCenter = "JCenter" at "https://jcenter.bintray.com"

lazy val baseSettings = Seq(
name := "fintrospect",
organization := "io.fintrospect",
Expand Down Expand Up @@ -112,11 +114,24 @@ lazy val mustache = project
.settings(libraryDependencies ++= Seq("com.github.spullara.mustache.java" % "compiler" % "0.9.2",
"com.github.spullara.mustache.java" % "scala-extensions-2.11" % "0.9.2"))

// misc
lazy val stack = project
.settings(baseSettings)
.settings(moduleName := "fintrospect-stack")
.settings(description := "Classes for a fintrospect-based HTTP service stack providing built metrics, events and circuit breakers")
.dependsOn(core)
.dependsOn(json4s % "compile->test")
.settings(libraryDependencies ++= Seq(
"io.github.daviddenton" %% "finagle-circuit" % "4.0.1",
"io.github.daviddenton" % "metrique" % "3.1.0",
"me.moocar" % "logback-gelf" % "0.12",
"com.newrelic.agent.java" % "newrelic-api" % "3.28.0")
)

lazy val examples = project.in(file("."))
.settings(baseSettings)
.settings(moduleName := "fintrospect-examples")
.aggregate(core, argonaut, circe, gson, json4s, play, spray, handlebars, mustache)
.dependsOn(core, argonaut, circe, gson, json4s, play, spray, handlebars, mustache)
.settings(libraryDependencies += "com.github.finagle" %% "finagle-oauth2" % "0.1.6")
.settings(libraryDependencies += "com.google.code.gson" % "gson" % "2.5")
.aggregate(core, argonaut, circe, gson, json4s, play, spray, handlebars, mustache, stack)
.dependsOn(core, argonaut, circe, gson, json4s, play, spray, handlebars, mustache, stack)
.settings(libraryDependencies ++= Seq("com.github.finagle" %% "finagle-oauth2" % "0.1.6", "com.google.code.gson" % "gson" % "2.7"))

2 changes: 2 additions & 0 deletions core/src/main/scala/io/fintrospect/configuration/Port.scala
Expand Up @@ -5,6 +5,8 @@ case class Port(value: Int) extends AnyVal {
}

object Port {
val random = Port(0)

def apply(value: String): Port = Port(value.toInt)
}

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
138 changes: 0 additions & 138 deletions src/test/scala/io/fintrospect/parameters/BodyTest.scala

This file was deleted.

18 changes: 18 additions & 0 deletions stack/src/main/scala/io/fintrospect/configuration/BaseUri.scala
@@ -0,0 +1,18 @@
package io.fintrospect.configuration

import java.net.URI

case class BaseUri(value: URI) extends AnyVal {
override def toString: String = pathTo("").toString

def pathTo(relative: String) = value.resolve(relative)
}

object BaseUri {
def apply(scheme: Scheme, authority: Authority): BaseUri = scheme.baseUri(authority.host, authority.port)
def apply(scheme: Scheme, host: Host, port: Port): BaseUri = scheme.baseUri(host, port)

def apply(base: String): BaseUri = BaseUri(new URI(base))
}


@@ -0,0 +1,5 @@
package io.fintrospect.configuration

case class Environment(name: String) extends AnyVal {
override def toString = name
}
17 changes: 17 additions & 0 deletions stack/src/main/scala/io/fintrospect/configuration/Scheme.scala
@@ -0,0 +1,17 @@
package io.fintrospect.configuration

import java.net.URI

case class Scheme(value: String) extends AnyVal {
def baseUri(authority: Authority): BaseUri = BaseUri(new URI(s"$value://$authority"))

def baseUri(host: Host, port: Port): BaseUri = baseUri(Authority(host, port))
}

object Scheme {
def http = Scheme("http")

def https = Scheme("https")

def ftp = Scheme("ftp")
}
13 changes: 13 additions & 0 deletions stack/src/main/scala/io/fintrospect/configuration/SystemName.scala
@@ -0,0 +1,13 @@
package io.fintrospect.configuration

import io.github.finaglecircuit.CircuitName

import scala.language.implicitConversions

case class SystemName(value: String) extends AnyVal {
override def toString = value
}

object SystemName {
implicit def toCircuitName(systemName: SystemName): CircuitName = CircuitName(systemName.value)
}
@@ -0,0 +1,21 @@
package io.fintrospect.stack.anatomy

import com.twitter.finagle.http.path._
import io.fintrospect.ModuleSpec
import io.fintrospect.renderers.simplejson.SimpleJson
import io.fintrospect.stack.cloudfoundry.VcapApplication
import io.fintrospect.stack.util.Emails

object AnatomyModule {
def apply(config: Map[String, String], versionContents: String, checkers: DependencyChecker*) = {

ModuleSpec(Root / "internal", SimpleJson())
.withRoute(Version(versionContents).route)
.withRoute(Dependencies(checkers: _*).route)
.withRoute(DescribeConfiguration(config).route)
.withRoute(StatusEndpoint(config.get("SUPPORT_EMAILS").map(Emails.parseEmails).getOrElse(Nil)).route)
.withRoute(HeapDump(VcapApplication(config.getOrElse("VCAP_APPLICATION", VcapApplication.EMPTY.toString))).route)
// .withRoute(RouteSpec().at(Get) / "dump" / "memory" bindTo memoryDump)
// .withRoute(RouteSpec().at(Get) / "dump" / "threads" bindTo threadDump)
}
}
@@ -0,0 +1,27 @@
package io.fintrospect.stack.anatomy

import argo.jdom.JsonRootNode
import com.twitter.finagle.Service
import com.twitter.finagle.http.Method._
import com.twitter.finagle.http.Request
import com.twitter.util.Future.collect
import io.fintrospect.RouteSpec
import io.fintrospect.formats.json.Argo.JsonFormat._
import io.fintrospect.formats.json.Argo.ResponseBuilder._

case class Dependencies(checkers: DependencyChecker*) {

private def resultToJson: (DependencyCheckResult) => JsonRootNode = {
result =>
obj("code" -> number(result.code.code), "url" -> string(result.uri.toString))
}

val route = RouteSpec("dependencies").at(Get) / "dependencies" bindTo Service.mk {
rq: Request =>
collect(checkers.map(_.check))
.map(results => results.map(resultToJson))
.map { j =>
HttpResponse().withCode(com.twitter.finagle.http.Status.Ok).withContent(obj("results" -> array(j: _*))).build
}
}
}
@@ -0,0 +1,12 @@
package io.fintrospect.stack.anatomy

import java.net.URI

import com.twitter.finagle.http.Status
import com.twitter.util.Future

trait DependencyChecker {
def check: Future[DependencyCheckResult]
}

case class DependencyCheckResult(code: Status, uri: URI)
@@ -0,0 +1,27 @@
package io.fintrospect.stack.anatomy

import argo.jdom.JsonRootNode
import com.twitter.finagle.Service
import com.twitter.finagle.http.Method._
import com.twitter.finagle.http.Request
import com.twitter.finagle.http.Status._
import io.fintrospect.RouteSpec
import io.fintrospect.formats.json.Argo.JsonFormat._
import io.fintrospect.formats.json.Argo.ResponseBuilder.implicits._

case class DescribeConfiguration(config: Map[String, String]) {

private def toJson(fields: Map[String, String]): JsonRootNode = {
obj(fields.map(f => f._1 -> string(f._2)).toSeq: _*)
}

private val configAsJson: JsonRootNode = obj(
"settings" -> toJson(config),
"env" -> toJson(sys.env.filterNot(kv => config.contains(kv._1))),
"jvm" -> toJson(sys.props.toMap.filterNot(kv => config.contains(kv._1)))
)

val route = RouteSpec("config").at(Get) / "config" bindTo Service.mk { rq: Request => Ok(configAsJson) }

}

37 changes: 37 additions & 0 deletions stack/src/main/scala/io/fintrospect/stack/anatomy/HeapDump.scala
@@ -0,0 +1,37 @@
package io.fintrospect.stack.anatomy

import java.io.File
import java.lang.management.ManagementFactory.getPlatformMXBeans
import java.time.ZonedDateTime.now
import java.time.format.DateTimeFormatter._

import com.sun.management.HotSpotDiagnosticMXBean
import com.twitter.finagle.Service
import com.twitter.finagle.http.Method._
import com.twitter.finagle.http.{Request, Response}
import com.twitter.io.Readers
import com.twitter.util.Future
import io.fintrospect.formats.ResponseBuilder.HttpResponse
import io.fintrospect.stack.cloudfoundry.VcapApplication
import io.fintrospect.{ContentType, RouteSpec}

case class HeapDump(vcap: VcapApplication) {

val route = RouteSpec("heap").at(Get) / "heap" bindTo Service.mk[Request, Response] {
rq => {

val dumpFileName = s"heapdump-${vcap.appName.name}-${vcap.appInstanceIndex.index}-${now().format(ISO_DATE_TIME)}"

val dumpFile = File.createTempFile(dumpFileName, ".hprof")
dumpFile.delete()

getPlatformMXBeans(classOf[HotSpotDiagnosticMXBean]).get(0).dumpHeap(dumpFile.getAbsolutePath, true)

val response = HttpResponse(ContentType("application/x-heap-dump"))
.withHeaders("Content-disposition" -> ("inline; filename=\"" + dumpFileName + ".hprof\""))
.withContent(Readers.newFileReader(dumpFile)).build()

Future.value(response).ensure(dumpFile.delete())
}
}
}
@@ -0,0 +1,20 @@
package io.fintrospect.stack.anatomy

import java.net.URI

import com.twitter.finagle.http.{Request, Status}
import com.twitter.util.Future
import io.fintrospect.configuration.{Authority, BaseUri, Scheme}
import io.fintrospect.stack.util.BasicHttp

class StatusCheckingDependencyChecker(authority: Authority) extends DependencyChecker {
private val client = BasicHttp.newService(authority)

override def check: Future[DependencyCheckResult] = {
val request = Request(BaseUri(Scheme.http, authority).pathTo("/internal/status").toString)
client(request).map(_.status)
.rescue {
case _ => Future.value(Status.ServiceUnavailable)
}.map(DependencyCheckResult(_, new URI(request.uri)))
}
}

0 comments on commit e41c3d5

Please sign in to comment.