Bläddra i källkod

Enable Grafana extensions at build time. (#11752)

* extensions: import and build

* bus: use predefined error

* enterprise: build script for enterprise packages

* poc: auto registering services and dependency injection

(cherry picked from commit b5b1ef875f905473af41e49f8071cb9028edc845)

* poc: backend services registry progress

(cherry picked from commit 97be69725881241bfbf1e7adf0e66801d6b0af3d)

* poc: minor update

(cherry picked from commit 03d7a6888b81403f458b94305792e075568f0794)

* ioc: introduce manuel ioc

* enterprise: adds setting for enterprise

* build: test and build specific ee commit

* cleanup: test testing code

* removes example hello service
Carl Bergquist 7 år sedan
förälder
incheckning
28f7b6dad1

+ 20 - 0
.circleci/config.yml

@@ -93,6 +93,22 @@ jobs:
             - scripts/*.sh
             - scripts/publish
 
+  build-enterprise:
+    docker:
+     - image: grafana/build-container:v0.1
+    working_directory: /go/src/github.com/grafana/grafana
+    steps:
+      - checkout
+      - run:
+          name: build and package grafana
+          command: './scripts/build/build_enterprise.sh'
+      - run:
+          name: sign packages
+          command: './scripts/build/sign_packages.sh'
+      - run:
+          name: sha-sum packages
+          command: 'go run build.go sha-dist'
+
   deploy-master:
     docker:
       - image: circleci/python:2.7-stretch
@@ -176,3 +192,7 @@ workflows:
               ignore: /.*/
             tags:
               only: /^v[0-9]+(\.[0-9]+){2}(-.+|[^-.]*)$/
+      - build-enterprise:
+          filters:
+            tags:
+              only: /.*/

+ 1 - 0
.gitignore

@@ -48,6 +48,7 @@ profile.cov
 /pkg/cmd/grafana-cli/grafana-cli
 /pkg/cmd/grafana-server/grafana-server
 /pkg/cmd/grafana-server/debug
+/pkg/extensions
 debug.test
 /examples/*/dist
 /packaging/**/*.rpm

+ 190 - 21
Gopkg.lock

@@ -27,7 +27,37 @@
 
 [[projects]]
   name = "github.com/aws/aws-sdk-go"
-  packages = ["aws","aws/awserr","aws/awsutil","aws/client","aws/client/metadata","aws/corehandlers","aws/credentials","aws/credentials/ec2rolecreds","aws/credentials/endpointcreds","aws/credentials/stscreds","aws/defaults","aws/ec2metadata","aws/endpoints","aws/request","aws/session","aws/signer/v4","internal/shareddefaults","private/protocol","private/protocol/ec2query","private/protocol/query","private/protocol/query/queryutil","private/protocol/rest","private/protocol/restxml","private/protocol/xml/xmlutil","service/cloudwatch","service/ec2","service/ec2/ec2iface","service/s3","service/sts"]
+  packages = [
+    "aws",
+    "aws/awserr",
+    "aws/awsutil",
+    "aws/client",
+    "aws/client/metadata",
+    "aws/corehandlers",
+    "aws/credentials",
+    "aws/credentials/ec2rolecreds",
+    "aws/credentials/endpointcreds",
+    "aws/credentials/stscreds",
+    "aws/defaults",
+    "aws/ec2metadata",
+    "aws/endpoints",
+    "aws/request",
+    "aws/session",
+    "aws/signer/v4",
+    "internal/shareddefaults",
+    "private/protocol",
+    "private/protocol/ec2query",
+    "private/protocol/query",
+    "private/protocol/query/queryutil",
+    "private/protocol/rest",
+    "private/protocol/restxml",
+    "private/protocol/xml/xmlutil",
+    "service/cloudwatch",
+    "service/ec2",
+    "service/ec2/ec2iface",
+    "service/s3",
+    "service/sts"
+  ]
   revision = "decd990ddc5dcdf2f73309cbcab90d06b996ca28"
   version = "v1.12.67"
 
@@ -75,7 +105,10 @@
 
 [[projects]]
   name = "github.com/denisenkom/go-mssqldb"
-  packages = [".","internal/cp"]
+  packages = [
+    ".",
+    "internal/cp"
+  ]
   revision = "270bc3860bb94dd3a3ffd047377d746c5e276726"
 
 [[projects]]
@@ -117,7 +150,12 @@
 [[projects]]
   branch = "master"
   name = "github.com/go-macaron/session"
-  packages = [".","memcache","postgres","redis"]
+  packages = [
+    ".",
+    "memcache",
+    "postgres",
+    "redis"
+  ]
   revision = "b8e286a0dba8f4999042d6b258daf51b31d08938"
 
 [[projects]]
@@ -152,7 +190,13 @@
 [[projects]]
   branch = "master"
   name = "github.com/golang/protobuf"
-  packages = ["proto","ptypes","ptypes/any","ptypes/duration","ptypes/timestamp"]
+  packages = [
+    "proto",
+    "ptypes",
+    "ptypes/any",
+    "ptypes/duration",
+    "ptypes/timestamp"
+  ]
   revision = "c65a0412e71e8b9b3bfd22925720d23c0f054237"
 
 [[projects]]
@@ -221,7 +265,10 @@
 
 [[projects]]
   name = "github.com/klauspost/compress"
-  packages = ["flate","gzip"]
+  packages = [
+    "flate",
+    "gzip"
+  ]
   revision = "6c8db69c4b49dd4df1fff66996cf556176d0b9bf"
   version = "v1.2.1"
 
@@ -252,7 +299,10 @@
 [[projects]]
   branch = "master"
   name = "github.com/lib/pq"
-  packages = [".","oid"]
+  packages = [
+    ".",
+    "oid"
+  ]
   revision = "61fe37aa2ee24fabcdbe5c4ac1d4ac566f88f345"
 
 [[projects]]
@@ -287,7 +337,11 @@
 
 [[projects]]
   name = "github.com/opentracing/opentracing-go"
-  packages = [".","ext","log"]
+  packages = [
+    ".",
+    "ext",
+    "log"
+  ]
   revision = "1949ddbfd147afd4d964a9f00b24eb291e0e7c38"
   version = "v1.0.2"
 
@@ -297,9 +351,20 @@
   revision = "a3647f8e31d79543b2d0f0ae2fe5c379d72cedc0"
   version = "v2.1.0"
 
+[[projects]]
+  name = "github.com/pkg/errors"
+  packages = ["."]
+  revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
+  version = "v0.8.0"
+
 [[projects]]
   name = "github.com/prometheus/client_golang"
-  packages = ["api","api/prometheus/v1","prometheus","prometheus/promhttp"]
+  packages = [
+    "api",
+    "api/prometheus/v1",
+    "prometheus",
+    "prometheus/promhttp"
+  ]
   revision = "967789050ba94deca04a5e84cce8ad472ce313c1"
   version = "v0.9.0-pre1"
 
@@ -312,13 +377,22 @@
 [[projects]]
   branch = "master"
   name = "github.com/prometheus/common"
-  packages = ["expfmt","internal/bitbucket.org/ww/goautoneg","model"]
+  packages = [
+    "expfmt",
+    "internal/bitbucket.org/ww/goautoneg",
+    "model"
+  ]
   revision = "89604d197083d4781071d3c65855d24ecfb0a563"
 
 [[projects]]
   branch = "master"
   name = "github.com/prometheus/procfs"
-  packages = [".","internal/util","nfsd","xfs"]
+  packages = [
+    ".",
+    "internal/util",
+    "nfsd",
+    "xfs"
+  ]
   revision = "85fadb6e89903ef7cca6f6a804474cd5ea85b6e1"
 
 [[projects]]
@@ -335,13 +409,21 @@
 
 [[projects]]
   name = "github.com/smartystreets/assertions"
-  packages = [".","internal/go-render/render","internal/oglematchers"]
+  packages = [
+    ".",
+    "internal/go-render/render",
+    "internal/oglematchers"
+  ]
   revision = "0b37b35ec7434b77e77a4bb29b79677cced992ea"
   version = "1.8.1"
 
 [[projects]]
   name = "github.com/smartystreets/goconvey"
-  packages = ["convey","convey/gotest","convey/reporting"]
+  packages = [
+    "convey",
+    "convey/gotest",
+    "convey/reporting"
+  ]
   revision = "9e8dc3f972df6c8fcc0375ef492c24d0bb204857"
   version = "1.6.3"
 
@@ -353,7 +435,21 @@
 
 [[projects]]
   name = "github.com/uber/jaeger-client-go"
-  packages = [".","config","internal/baggage","internal/baggage/remote","internal/spanlog","log","rpcmetrics","thrift-gen/agent","thrift-gen/baggage","thrift-gen/jaeger","thrift-gen/sampling","thrift-gen/zipkincore","utils"]
+  packages = [
+    ".",
+    "config",
+    "internal/baggage",
+    "internal/baggage/remote",
+    "internal/spanlog",
+    "log",
+    "rpcmetrics",
+    "thrift-gen/agent",
+    "thrift-gen/baggage",
+    "thrift-gen/jaeger",
+    "thrift-gen/sampling",
+    "thrift-gen/zipkincore",
+    "utils"
+  ]
   revision = "3ac96c6e679cb60a74589b0d0aa7c70a906183f7"
   version = "v2.11.2"
 
@@ -365,7 +461,10 @@
 
 [[projects]]
   name = "github.com/yudai/gojsondiff"
-  packages = [".","formatter"]
+  packages = [
+    ".",
+    "formatter"
+  ]
   revision = "7b1b7adf999dab73a6eb02669c3d82dbb27a3dd6"
   version = "1.0.0"
 
@@ -378,19 +477,37 @@
 [[projects]]
   branch = "master"
   name = "golang.org/x/crypto"
-  packages = ["md4","pbkdf2"]
+  packages = [
+    "md4",
+    "pbkdf2"
+  ]
   revision = "3d37316aaa6bd9929127ac9a527abf408178ea7b"
 
 [[projects]]
   branch = "master"
   name = "golang.org/x/net"
-  packages = ["context","context/ctxhttp","http2","http2/hpack","idna","internal/timeseries","lex/httplex","trace"]
+  packages = [
+    "context",
+    "context/ctxhttp",
+    "http2",
+    "http2/hpack",
+    "idna",
+    "internal/timeseries",
+    "lex/httplex",
+    "trace"
+  ]
   revision = "5ccada7d0a7ba9aeb5d3aca8d3501b4c2a509fec"
 
 [[projects]]
   branch = "master"
   name = "golang.org/x/oauth2"
-  packages = [".","google","internal","jws","jwt"]
+  packages = [
+    ".",
+    "google",
+    "internal",
+    "jws",
+    "jwt"
+  ]
   revision = "b28fcf2b08a19742b43084fb40ab78ac6c3d8067"
 
 [[projects]]
@@ -408,12 +525,39 @@
 [[projects]]
   branch = "master"
   name = "golang.org/x/text"
-  packages = ["collate","collate/build","internal/colltab","internal/gen","internal/tag","internal/triegen","internal/ucd","language","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"]
+  packages = [
+    "collate",
+    "collate/build",
+    "internal/colltab",
+    "internal/gen",
+    "internal/tag",
+    "internal/triegen",
+    "internal/ucd",
+    "language",
+    "secure/bidirule",
+    "transform",
+    "unicode/bidi",
+    "unicode/cldr",
+    "unicode/norm",
+    "unicode/rangetable"
+  ]
   revision = "e19ae1496984b1c655b8044a65c0300a3c878dd3"
 
 [[projects]]
   name = "google.golang.org/appengine"
-  packages = [".","cloudsql","internal","internal/app_identity","internal/base","internal/datastore","internal/log","internal/modules","internal/remote_api","internal/urlfetch","urlfetch"]
+  packages = [
+    ".",
+    "cloudsql",
+    "internal",
+    "internal/app_identity",
+    "internal/base",
+    "internal/datastore",
+    "internal/log",
+    "internal/modules",
+    "internal/remote_api",
+    "internal/urlfetch",
+    "urlfetch"
+  ]
   revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a"
   version = "v1.0.0"
 
@@ -425,7 +569,32 @@
 
 [[projects]]
   name = "google.golang.org/grpc"
-  packages = [".","balancer","balancer/base","balancer/roundrobin","codes","connectivity","credentials","encoding","grpclb/grpc_lb_v1/messages","grpclog","health","health/grpc_health_v1","internal","keepalive","metadata","naming","peer","resolver","resolver/dns","resolver/passthrough","stats","status","tap","transport"]
+  packages = [
+    ".",
+    "balancer",
+    "balancer/base",
+    "balancer/roundrobin",
+    "codes",
+    "connectivity",
+    "credentials",
+    "encoding",
+    "grpclb/grpc_lb_v1/messages",
+    "grpclog",
+    "health",
+    "health/grpc_health_v1",
+    "internal",
+    "keepalive",
+    "metadata",
+    "naming",
+    "peer",
+    "resolver",
+    "resolver/dns",
+    "resolver/passthrough",
+    "stats",
+    "status",
+    "tap",
+    "transport"
+  ]
   revision = "6b51017f791ae1cfbec89c52efdf444b13b550ef"
   version = "v1.9.2"
 
@@ -480,6 +649,6 @@
 [solve-meta]
   analyzer-name = "dep"
   analyzer-version = 1
-  inputs-digest = "ad3c71fd3244369c313978e9e7464c7116faee764386439a17de0707a08103aa"
+  inputs-digest = "2bd5b309496d57e2189a1cc28f5c1c41398c19729ba0cf53c8cbb17ea3f706b5"
   solver-name = "gps-cdcl"
   solver-version = 1

+ 20 - 3
build.go

@@ -41,6 +41,7 @@ var (
 	buildNumber           int      = 0
 	binaries              []string = []string{"grafana-server", "grafana-cli"}
 	isDev                 bool     = false
+	enterprise            bool     = false
 )
 
 func main() {
@@ -58,6 +59,7 @@ func main() {
 	flag.StringVar(&phjsToRelease, "phjs", "", "PhantomJS binary")
 	flag.BoolVar(&race, "race", race, "Use race detector")
 	flag.BoolVar(&includeBuildNumber, "includeBuildNumber", includeBuildNumber, "IncludeBuildNumber in package name")
+	flag.BoolVar(&enterprise, "enterprise", enterprise, "Build enterprise version of Grafana")
 	flag.IntVar(&buildNumber, "buildNumber", 0, "Build number from CI system")
 	flag.BoolVar(&isDev, "dev", isDev, "optimal for development, skips certain steps")
 	flag.Parse()
@@ -283,19 +285,33 @@ func createPackage(options linuxPackageOptions) {
 		"-s", "dir",
 		"--description", "Grafana",
 		"-C", packageRoot,
-		"--vendor", "Grafana",
 		"--url", "https://grafana.com",
-		"--license", "\"Apache 2.0\"",
 		"--maintainer", "contact@grafana.com",
 		"--config-files", options.initdScriptFilePath,
 		"--config-files", options.etcDefaultFilePath,
 		"--config-files", options.systemdServiceFilePath,
 		"--after-install", options.postinstSrc,
-		"--name", "grafana",
+
 		"--version", linuxPackageVersion,
 		"-p", "./dist",
 	}
 
+	name := "grafana"
+	if enterprise {
+		name += "-enterprise"
+	}
+	args = append(args, "--name", name)
+
+	description := "Grafana"
+	if enterprise {
+		description += " Enterprise"
+	}
+	args = append(args, "--vendor", description)
+
+	if !enterprise {
+		args = append(args, "--license", "\"Apache 2.0\"")
+	}
+
 	if options.packageType == "rpm" {
 		args = append(args, "--rpm-posttrans", "packaging/rpm/control/posttrans")
 	}
@@ -412,6 +428,7 @@ func ldflags() string {
 	b.WriteString(fmt.Sprintf(" -X main.version=%s", version))
 	b.WriteString(fmt.Sprintf(" -X main.commit=%s", getGitSha()))
 	b.WriteString(fmt.Sprintf(" -X main.buildstamp=%d", buildStamp()))
+	b.WriteString(fmt.Sprintf(" -X main.enterprise=%t", enterprise))
 	return b.String()
 }
 

+ 1 - 1
pkg/api/api.go

@@ -23,7 +23,7 @@ func (hs *HTTPServer) registerRoutes() {
 	// automatically set HEAD for every GET
 	macaronR.SetAutoHead(true)
 
-	r := newRouteRegister(middleware.RequestMetrics, middleware.RequestTracing)
+	r := hs.RouteRegister
 
 	// not logged in views
 	r.Get("/", reqSignedIn, Index)

+ 4 - 5
pkg/api/http_server.go

@@ -35,15 +35,14 @@ type HTTPServer struct {
 	context       context.Context
 	streamManager *live.StreamManager
 	cache         *gocache.Cache
+	RouteRegister RouteRegister `inject:""`
 
 	httpSrv *http.Server
 }
 
-func NewHTTPServer() *HTTPServer {
-	return &HTTPServer{
-		log:   log.New("http.server"),
-		cache: gocache.New(5*time.Minute, 10*time.Minute),
-	}
+func (hs *HTTPServer) Init() {
+	hs.log = log.New("http.server")
+	hs.cache = gocache.New(5*time.Minute, 10*time.Minute)
 }
 
 func (hs *HTTPServer) Start(ctx context.Context) error {

+ 1 - 1
pkg/api/index.go

@@ -289,7 +289,7 @@ func setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, error) {
 
 	data.NavTree = append(data.NavTree, &dtos.NavLink{
 		Text:         "Help",
-		SubTitle:     fmt.Sprintf(`Grafana v%s (%s)`, setting.BuildVersion, setting.BuildCommit),
+		SubTitle:     fmt.Sprintf(`%s v%s (%s)`, setting.ApplicationName, setting.BuildVersion, setting.BuildCommit),
 		Id:           "help",
 		Url:          "#",
 		Icon:         "gicon gicon-question",

+ 4 - 1
pkg/api/route_register.go

@@ -11,6 +11,8 @@ type Router interface {
 	Get(pattern string, handlers ...macaron.Handler) *macaron.Route
 }
 
+// RouteRegister allows you to add routes and macaron.Handlers
+// that the web server should serve.
 type RouteRegister interface {
 	Get(string, ...macaron.Handler)
 	Post(string, ...macaron.Handler)
@@ -26,7 +28,8 @@ type RouteRegister interface {
 
 type RegisterNamedMiddleware func(name string) macaron.Handler
 
-func newRouteRegister(namedMiddleware ...RegisterNamedMiddleware) RouteRegister {
+// NewRouteRegister creates a new RouteRegister with all middlewares sent as params
+func NewRouteRegister(namedMiddleware ...RegisterNamedMiddleware) RouteRegister {
 	return &routeRegister{
 		prefix:          "",
 		routes:          []route{},

+ 3 - 3
pkg/api/route_register_test.go

@@ -51,7 +51,7 @@ func TestRouteSimpleRegister(t *testing.T) {
 	}
 
 	// Setup
-	rr := newRouteRegister(func(name string) macaron.Handler {
+	rr := NewRouteRegister(func(name string) macaron.Handler {
 		return emptyHandler(name)
 	})
 
@@ -96,7 +96,7 @@ func TestRouteGroupedRegister(t *testing.T) {
 	}
 
 	// Setup
-	rr := newRouteRegister()
+	rr := NewRouteRegister()
 
 	rr.Delete("/admin", emptyHandler("1"))
 	rr.Get("/down", emptyHandler("1"), emptyHandler("2"))
@@ -150,7 +150,7 @@ func TestNamedMiddlewareRouteRegister(t *testing.T) {
 	}
 
 	// Setup
-	rr := newRouteRegister(func(name string) macaron.Handler {
+	rr := NewRouteRegister(func(name string) macaron.Handler {
 		return emptyHandler(name)
 	})
 

+ 10 - 3
pkg/bus/bus.go

@@ -2,7 +2,7 @@ package bus
 
 import (
 	"context"
-	"fmt"
+	"errors"
 	"reflect"
 )
 
@@ -10,6 +10,8 @@ type HandlerFunc interface{}
 type CtxHandlerFunc func()
 type Msg interface{}
 
+var ErrHandlerNotFound = errors.New("handler not found")
+
 type Bus interface {
 	Dispatch(msg Msg) error
 	DispatchCtx(ctx context.Context, msg Msg) error
@@ -38,12 +40,17 @@ func New() Bus {
 	return bus
 }
 
+// Want to get rid of global bus
+func GetBus() Bus {
+	return globalBus
+}
+
 func (b *InProcBus) DispatchCtx(ctx context.Context, msg Msg) error {
 	var msgName = reflect.TypeOf(msg).Elem().Name()
 
 	var handler = b.handlers[msgName]
 	if handler == nil {
-		return fmt.Errorf("handler not found for %s", msgName)
+		return ErrHandlerNotFound
 	}
 
 	var params = make([]reflect.Value, 2)
@@ -64,7 +71,7 @@ func (b *InProcBus) Dispatch(msg Msg) error {
 
 	var handler = b.handlers[msgName]
 	if handler == nil {
-		return fmt.Errorf("handler not found for %s", msgName)
+		return ErrHandlerNotFound
 	}
 
 	var params = make([]reflect.Value, 1)

+ 3 - 0
pkg/cmd/grafana-server/main.go

@@ -18,6 +18,7 @@ import (
 	"github.com/grafana/grafana/pkg/metrics"
 	"github.com/grafana/grafana/pkg/setting"
 
+	_ "github.com/grafana/grafana/pkg/extensions"
 	_ "github.com/grafana/grafana/pkg/services/alerting/conditions"
 	_ "github.com/grafana/grafana/pkg/services/alerting/notifiers"
 	_ "github.com/grafana/grafana/pkg/tsdb/cloudwatch"
@@ -33,6 +34,7 @@ import (
 var version = "5.0.0"
 var commit = "NA"
 var buildstamp string
+var enterprise string
 
 var configFile = flag.String("config", "", "path to config file")
 var homePath = flag.String("homepath", "", "path to grafana install/home path, defaults to working directory")
@@ -76,6 +78,7 @@ func main() {
 	setting.BuildVersion = version
 	setting.BuildCommit = commit
 	setting.BuildStamp = buildstampInt64
+	setting.Enterprise, _ = strconv.ParseBool(enterprise)
 
 	metrics.M_Grafana_Version.WithLabelValues(version).Set(1)
 	shutdownCompleted := make(chan int)

+ 69 - 25
pkg/cmd/grafana-server/server.go

@@ -8,9 +8,15 @@ import (
 	"net"
 	"os"
 	"path/filepath"
+	"reflect"
 	"strconv"
 	"time"
 
+	"github.com/facebookgo/inject"
+	"github.com/grafana/grafana/pkg/bus"
+	"github.com/grafana/grafana/pkg/middleware"
+	"github.com/grafana/grafana/pkg/registry"
+	"github.com/grafana/grafana/pkg/services/dashboards"
 	"github.com/grafana/grafana/pkg/services/provisioning"
 
 	"golang.org/x/sync/errgroup"
@@ -20,15 +26,17 @@ import (
 	"github.com/grafana/grafana/pkg/login"
 	"github.com/grafana/grafana/pkg/metrics"
 	"github.com/grafana/grafana/pkg/plugins"
-	"github.com/grafana/grafana/pkg/services/alerting"
-	"github.com/grafana/grafana/pkg/services/cleanup"
 	"github.com/grafana/grafana/pkg/services/notifications"
-	"github.com/grafana/grafana/pkg/services/search"
 	"github.com/grafana/grafana/pkg/services/sqlstore"
 	"github.com/grafana/grafana/pkg/setting"
 
 	"github.com/grafana/grafana/pkg/social"
 	"github.com/grafana/grafana/pkg/tracing"
+
+	_ "github.com/grafana/grafana/pkg/extensions"
+	_ "github.com/grafana/grafana/pkg/services/alerting"
+	_ "github.com/grafana/grafana/pkg/services/cleanup"
+	_ "github.com/grafana/grafana/pkg/services/search"
 )
 
 func NewGrafanaServer() *GrafanaServerImpl {
@@ -48,18 +56,20 @@ type GrafanaServerImpl struct {
 	shutdownFn    context.CancelFunc
 	childRoutines *errgroup.Group
 	log           log.Logger
+	RouteRegister api.RouteRegister `inject:""`
 
-	httpServer *api.HTTPServer
+	HttpServer *api.HTTPServer `inject:""`
 }
 
 func (g *GrafanaServerImpl) Start() error {
 	g.initLogging()
 	g.writePIDFile()
 
-	initSql()
+	// initSql
+	sqlstore.NewEngine() // TODO: this should return an error
+	sqlstore.EnsureAdminUser()
 
 	metrics.Init(setting.Cfg)
-	search.Init()
 	login.Init()
 	social.NewOAuthService()
 
@@ -79,28 +89,62 @@ func (g *GrafanaServerImpl) Start() error {
 	}
 	defer tracingCloser.Close()
 
-	// init alerting
-	if setting.AlertingEnabled && setting.ExecuteAlerts {
-		engine := alerting.NewEngine()
-		g.childRoutines.Go(func() error { return engine.Run(g.context) })
+	if err = notifications.Init(); err != nil {
+		return fmt.Errorf("Notification service failed to initialize. error: %v", err)
 	}
 
-	// cleanup service
-	cleanUpService := cleanup.NewCleanUpService()
-	g.childRoutines.Go(func() error { return cleanUpService.Run(g.context) })
+	serviceGraph := inject.Graph{}
+	serviceGraph.Provide(&inject.Object{Value: bus.GetBus()})
+	serviceGraph.Provide(&inject.Object{Value: dashboards.NewProvisioningService()})
+	serviceGraph.Provide(&inject.Object{Value: api.NewRouteRegister(middleware.RequestMetrics, middleware.RequestTracing)})
+	serviceGraph.Provide(&inject.Object{Value: api.HTTPServer{}})
+	services := registry.GetServices()
 
-	if err = notifications.Init(); err != nil {
-		return fmt.Errorf("Notification service failed to initialize. error: %v", err)
+	// Add all services to dependency graph
+	for _, service := range services {
+		serviceGraph.Provide(&inject.Object{Value: service})
 	}
 
-	sendSystemdNotification("READY=1")
+	serviceGraph.Provide(&inject.Object{Value: g})
 
-	return g.startHttpServer()
-}
+	// Inject dependencies to services
+	if err := serviceGraph.Populate(); err != nil {
+		return fmt.Errorf("Failed to populate service dependency: %v", err)
+	}
 
-func initSql() {
-	sqlstore.NewEngine()
-	sqlstore.EnsureAdminUser()
+	// Init & start services
+	for _, service := range services {
+		if registry.IsDisabled(service) {
+			continue
+		}
+
+		g.log.Info("Initializing " + reflect.TypeOf(service).Elem().Name())
+
+		if err := service.Init(); err != nil {
+			return fmt.Errorf("Service init failed %v", err)
+		}
+	}
+
+	// Start background services
+	for index := range services {
+		service, ok := services[index].(registry.BackgroundService)
+		if !ok {
+			continue
+		}
+
+		if registry.IsDisabled(services[index]) {
+			continue
+		}
+
+		g.childRoutines.Go(func() error {
+			err := service.Run(g.context)
+			g.log.Info("Stopped "+reflect.TypeOf(service).Elem().Name(), "reason", err)
+			return err
+		})
+	}
+
+	sendSystemdNotification("READY=1")
+	return g.startHttpServer()
 }
 
 func (g *GrafanaServerImpl) initLogging() {
@@ -115,14 +159,14 @@ func (g *GrafanaServerImpl) initLogging() {
 		os.Exit(1)
 	}
 
-	g.log.Info("Starting Grafana", "version", version, "commit", commit, "compiled", time.Unix(setting.BuildStamp, 0))
+	g.log.Info("Starting "+setting.ApplicationName, "version", version, "commit", commit, "compiled", time.Unix(setting.BuildStamp, 0))
 	setting.LogConfigurationInfo()
 }
 
 func (g *GrafanaServerImpl) startHttpServer() error {
-	g.httpServer = api.NewHTTPServer()
+	g.HttpServer.Init()
 
-	err := g.httpServer.Start(g.context)
+	err := g.HttpServer.Start(g.context)
 
 	if err != nil {
 		return fmt.Errorf("Fail to start server. error: %v", err)
@@ -134,7 +178,7 @@ func (g *GrafanaServerImpl) startHttpServer() error {
 func (g *GrafanaServerImpl) Shutdown(code int, reason string) {
 	g.log.Info("Shutdown started", "code", code, "reason", reason)
 
-	err := g.httpServer.Shutdown(g.context)
+	err := g.HttpServer.Shutdown(g.context)
 	if err != nil {
 		g.log.Error("Failed to shutdown server", "error", err)
 	}

+ 3 - 0
pkg/extensions/main.go

@@ -0,0 +1,3 @@
+package extensions
+
+import _ "github.com/pkg/errors"

+ 1 - 1
pkg/plugins/plugins.go

@@ -58,7 +58,7 @@ func (p *PluginManager) Run(ctx context.Context) error {
 		p.Kill()
 	}
 
-	p.log.Info("Stopped Plugins", "error", ctx.Err())
+	p.log.Info("Stopped Plugins", "reason", ctx.Err())
 	return ctx.Err()
 }
 

+ 33 - 0
pkg/registry/registry.go

@@ -0,0 +1,33 @@
+package registry
+
+import (
+	"context"
+)
+
+var services = []Service{}
+
+func RegisterService(srv Service) {
+	services = append(services, srv)
+}
+
+func GetServices() []Service {
+	return services
+}
+
+type Service interface {
+	Init() error
+}
+
+// Useful for alerting service
+type CanBeDisabled interface {
+	IsDisabled() bool
+}
+
+type BackgroundService interface {
+	Run(ctx context.Context) error
+}
+
+func IsDisabled(srv Service) bool {
+	canBeDisabled, ok := srv.(CanBeDisabled)
+	return ok && canBeDisabled.IsDisabled()
+}

+ 23 - 15
pkg/services/alerting/engine.go

@@ -11,6 +11,8 @@ import (
 
 	"github.com/benbjohnson/clock"
 	"github.com/grafana/grafana/pkg/log"
+	"github.com/grafana/grafana/pkg/registry"
+	"github.com/grafana/grafana/pkg/setting"
 	"golang.org/x/sync/errgroup"
 )
 
@@ -25,31 +27,37 @@ type Engine struct {
 	resultHandler ResultHandler
 }
 
-func NewEngine() *Engine {
-	e := &Engine{
-		ticker:        NewTicker(time.Now(), time.Second*0, clock.New()),
-		execQueue:     make(chan *Job, 1000),
-		scheduler:     NewScheduler(),
-		evalHandler:   NewEvalHandler(),
-		ruleReader:    NewRuleReader(),
-		log:           log.New("alerting.engine"),
-		resultHandler: NewResultHandler(),
-	}
+func init() {
+	registry.RegisterService(&Engine{})
+}
 
+func NewEngine() *Engine {
+	e := &Engine{}
+	e.Init()
 	return e
 }
 
-func (e *Engine) Run(ctx context.Context) error {
-	e.log.Info("Initializing Alerting")
+func (e *Engine) IsDisabled() bool {
+	return !setting.AlertingEnabled || !setting.ExecuteAlerts
+}
 
-	alertGroup, ctx := errgroup.WithContext(ctx)
+func (e *Engine) Init() error {
+	e.ticker = NewTicker(time.Now(), time.Second*0, clock.New())
+	e.execQueue = make(chan *Job, 1000)
+	e.scheduler = NewScheduler()
+	e.evalHandler = NewEvalHandler()
+	e.ruleReader = NewRuleReader()
+	e.log = log.New("alerting.engine")
+	e.resultHandler = NewResultHandler()
+	return nil
+}
 
+func (e *Engine) Run(ctx context.Context) error {
+	alertGroup, ctx := errgroup.WithContext(ctx)
 	alertGroup.Go(func() error { return e.alertingTicker(ctx) })
 	alertGroup.Go(func() error { return e.runJobDispatcher(ctx) })
 
 	err := alertGroup.Wait()
-
-	e.log.Info("Stopped Alerting", "reason", err)
 	return err
 }
 

+ 7 - 16
pkg/services/cleanup/cleanup.go

@@ -7,11 +7,10 @@ import (
 	"path"
 	"time"
 
-	"golang.org/x/sync/errgroup"
-
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/log"
 	m "github.com/grafana/grafana/pkg/models"
+	"github.com/grafana/grafana/pkg/registry"
 	"github.com/grafana/grafana/pkg/setting"
 )
 
@@ -19,24 +18,16 @@ type CleanUpService struct {
 	log log.Logger
 }
 
-func NewCleanUpService() *CleanUpService {
-	return &CleanUpService{
-		log: log.New("cleanup"),
-	}
+func init() {
+	registry.RegisterService(&CleanUpService{})
 }
 
-func (service *CleanUpService) Run(ctx context.Context) error {
-	service.log.Info("Initializing CleanUpService")
-
-	g, _ := errgroup.WithContext(ctx)
-	g.Go(func() error { return service.start(ctx) })
-
-	err := g.Wait()
-	service.log.Info("Stopped CleanUpService", "reason", err)
-	return err
+func (service *CleanUpService) Init() error {
+	service.log = log.New("cleanup")
+	return nil
 }
 
-func (service *CleanUpService) start(ctx context.Context) error {
+func (service *CleanUpService) Run(ctx context.Context) error {
 	service.cleanUpTmpFiles()
 
 	ticker := time.NewTicker(time.Minute * 10)

+ 13 - 3
pkg/services/search/handlers.go

@@ -5,13 +5,23 @@ import (
 
 	"github.com/grafana/grafana/pkg/bus"
 	m "github.com/grafana/grafana/pkg/models"
+	"github.com/grafana/grafana/pkg/registry"
 )
 
-func Init() {
-	bus.AddHandler("search", searchHandler)
+func init() {
+	registry.RegisterService(&SearchService{})
 }
 
-func searchHandler(query *Query) error {
+type SearchService struct {
+	Bus bus.Bus `inject:""`
+}
+
+func (s *SearchService) Init() error {
+	s.Bus.AddHandler(s.searchHandler)
+	return nil
+}
+
+func (s *SearchService) searchHandler(query *Query) error {
 	dashQuery := FindPersistedDashboardsQuery{
 		Title:        query.Title,
 		SignedInUser: query.SignedInUser,

+ 2 - 1
pkg/services/search/handlers_test.go

@@ -12,6 +12,7 @@ func TestSearch(t *testing.T) {
 
 	Convey("Given search query", t, func() {
 		query := Query{Limit: 2000, SignedInUser: &m.SignedInUser{IsGrafanaAdmin: true}}
+		ss := &SearchService{}
 
 		bus.AddHandler("test", func(query *FindPersistedDashboardsQuery) error {
 			query.Result = HitList{
@@ -35,7 +36,7 @@ func TestSearch(t *testing.T) {
 		})
 
 		Convey("That is empty", func() {
-			err := searchHandler(&query)
+			err := ss.searchHandler(&query)
 			So(err, ShouldBeNil)
 
 			Convey("should return sorted results", func() {

+ 3 - 1
pkg/services/sqlstore/sqlstore.go

@@ -77,7 +77,7 @@ func EnsureAdminUser() {
 	log.Info("Created default admin user: %v", setting.AdminUser)
 }
 
-func NewEngine() {
+func NewEngine() *xorm.Engine {
 	x, err := getEngine()
 
 	if err != nil {
@@ -91,6 +91,8 @@ func NewEngine() {
 		sqlog.Error("Fail to initialize orm engine", "error", err)
 		os.Exit(1)
 	}
+
+	return x
 }
 
 func SetEngine(engine *xorm.Engine) (err error) {

+ 10 - 3
pkg/setting/setting.go

@@ -45,9 +45,11 @@ var (
 	InstanceName string
 
 	// build
-	BuildVersion string
-	BuildCommit  string
-	BuildStamp   int64
+	BuildVersion    string
+	BuildCommit     string
+	BuildStamp      int64
+	Enterprise      bool
+	ApplicationName string
 
 	// Paths
 	LogsPath         string
@@ -486,6 +488,11 @@ func NewConfigContext(args *CommandLineArgs) error {
 		return err
 	}
 
+	ApplicationName = "Grafana"
+	if Enterprise {
+		ApplicationName += " Enterprise"
+	}
+
 	Env = Cfg.Section("").Key("app_mode").MustString("development")
 	InstanceName = Cfg.Section("").Key("instance_name").MustString("unknown_instance_name")
 	PluginsPath = makeAbsolute(Cfg.Section("paths").Key("plugins").String(), HomePath)

+ 58 - 0
scripts/build/build_enterprise.sh

@@ -0,0 +1,58 @@
+#!/bin/bash
+
+#
+#   This script is executed from within the container.
+#
+
+echo "building enterprise version"
+
+GOPATH=/go
+REPO_PATH=$GOPATH/src/github.com/grafana/grafana
+
+
+cd /go/src/github.com/grafana/grafana
+echo "current dir: $(pwd)"
+
+cd ..
+git clone -b ee_build --single-branch git@github.com:grafana/grafana-enterprise.git --depth 10
+cd grafana-enterprise
+git checkout 7fbae9c1be3467c4a39cf6ad85278a6896ceb49f
+./build.sh
+
+cd ../grafana
+
+function exit_if_fail {
+    command=$@
+    echo "Executing '$command'"
+    eval $command
+    rc=$?
+    if [ $rc -ne 0 ]; then
+        echo "'$command' returned $rc."
+        exit $rc
+    fi
+}
+
+exit_if_fail go test ./pkg/extensions/...
+
+
+if [ "$CIRCLE_TAG" != "" ]; then
+  echo "Building a release from tag $ls"
+  go run build.go -buildNumber=${CIRCLE_BUILD_NUM} -enterprise=true -includeBuildNumber=false build
+else
+  echo "Building incremental build for $CIRCLE_BRANCH"
+  go run build.go -buildNumber=${CIRCLE_BUILD_NUM} -enterprise=true build
+fi
+
+yarn install --pure-lockfile --no-progress
+
+source /etc/profile.d/rvm.sh
+
+echo "current dir: $(pwd)"
+
+if [ "$CIRCLE_TAG" != "" ]; then
+  echo "Packaging a release from tag $CIRCLE_TAG"
+  go run build.go -buildNumber=${CIRCLE_BUILD_NUM} -enterprise=true -includeBuildNumber=false package latest
+else
+  echo "Packaging incremental build for $CIRCLE_BRANCH"
+  go run build.go -buildNumber=${CIRCLE_BUILD_NUM} -enterprise=true package latest
+fi

+ 576 - 0
vendor/github.com/facebookgo/inject/inject.go

@@ -0,0 +1,576 @@
+// Package inject provides a reflect based injector. A large application built
+// with dependency injection in mind will typically involve the boring work of
+// setting up the object graph. This library attempts to take care of this
+// boring work by creating and connecting the various objects. Its use involves
+// you seeding the object graph with some (possibly incomplete) objects, where
+// the underlying types have been tagged for injection. Given this, the
+// library will populate the objects creating new ones as necessary. It uses
+// singletons by default, supports optional private instances as well as named
+// instances.
+//
+// It works using Go's reflection package and is inherently limited in what it
+// can do as opposed to a code-gen system with respect to private fields.
+//
+// The usage pattern for the library involves struct tags. It requires the tag
+// format used by the various standard libraries, like json, xml etc. It
+// involves tags in one of the three forms below:
+//
+//     `inject:""`
+//     `inject:"private"`
+//     `inject:"dev logger"`
+//
+// The first no value syntax is for the common case of a singleton dependency
+// of the associated type. The second triggers creation of a private instance
+// for the associated type. Finally the last form is asking for a named
+// dependency called "dev logger".
+package inject
+
+import (
+	"bytes"
+	"fmt"
+	"math/rand"
+	"reflect"
+
+	"github.com/facebookgo/structtag"
+)
+
+// Logger allows for simple logging as inject traverses and populates the
+// object graph.
+type Logger interface {
+	Debugf(format string, v ...interface{})
+}
+
+// Populate is a short-hand for populating a graph with the given incomplete
+// object values.
+func Populate(values ...interface{}) error {
+	var g Graph
+	for _, v := range values {
+		if err := g.Provide(&Object{Value: v}); err != nil {
+			return err
+		}
+	}
+	return g.Populate()
+}
+
+// An Object in the Graph.
+type Object struct {
+	Value        interface{}
+	Name         string             // Optional
+	Complete     bool               // If true, the Value will be considered complete
+	Fields       map[string]*Object // Populated with the field names that were injected and their corresponding *Object.
+	reflectType  reflect.Type
+	reflectValue reflect.Value
+	private      bool // If true, the Value will not be used and will only be populated
+	created      bool // If true, the Object was created by us
+	embedded     bool // If true, the Object is an embedded struct provided internally
+}
+
+// String representation suitable for human consumption.
+func (o *Object) String() string {
+	var buf bytes.Buffer
+	fmt.Fprint(&buf, o.reflectType)
+	if o.Name != "" {
+		fmt.Fprintf(&buf, " named %s", o.Name)
+	}
+	return buf.String()
+}
+
+func (o *Object) addDep(field string, dep *Object) {
+	if o.Fields == nil {
+		o.Fields = make(map[string]*Object)
+	}
+	o.Fields[field] = dep
+}
+
+// The Graph of Objects.
+type Graph struct {
+	Logger      Logger // Optional, will trigger debug logging.
+	unnamed     []*Object
+	unnamedType map[reflect.Type]bool
+	named       map[string]*Object
+}
+
+// Provide objects to the Graph. The Object documentation describes
+// the impact of various fields.
+func (g *Graph) Provide(objects ...*Object) error {
+	for _, o := range objects {
+		o.reflectType = reflect.TypeOf(o.Value)
+		o.reflectValue = reflect.ValueOf(o.Value)
+
+		if o.Fields != nil {
+			return fmt.Errorf(
+				"fields were specified on object %s when it was provided",
+				o,
+			)
+		}
+
+		if o.Name == "" {
+			if !isStructPtr(o.reflectType) {
+				return fmt.Errorf(
+					"expected unnamed object value to be a pointer to a struct but got type %s "+
+						"with value %v",
+					o.reflectType,
+					o.Value,
+				)
+			}
+
+			if !o.private {
+				if g.unnamedType == nil {
+					g.unnamedType = make(map[reflect.Type]bool)
+				}
+
+				if g.unnamedType[o.reflectType] {
+					return fmt.Errorf(
+						"provided two unnamed instances of type *%s.%s",
+						o.reflectType.Elem().PkgPath(), o.reflectType.Elem().Name(),
+					)
+				}
+				g.unnamedType[o.reflectType] = true
+			}
+			g.unnamed = append(g.unnamed, o)
+		} else {
+			if g.named == nil {
+				g.named = make(map[string]*Object)
+			}
+
+			if g.named[o.Name] != nil {
+				return fmt.Errorf("provided two instances named %s", o.Name)
+			}
+			g.named[o.Name] = o
+		}
+
+		if g.Logger != nil {
+			if o.created {
+				g.Logger.Debugf("created %s", o)
+			} else if o.embedded {
+				g.Logger.Debugf("provided embedded %s", o)
+			} else {
+				g.Logger.Debugf("provided %s", o)
+			}
+		}
+	}
+	return nil
+}
+
+// Populate the incomplete Objects.
+func (g *Graph) Populate() error {
+	for _, o := range g.named {
+		if o.Complete {
+			continue
+		}
+
+		if err := g.populateExplicit(o); err != nil {
+			return err
+		}
+	}
+
+	// We append and modify our slice as we go along, so we don't use a standard
+	// range loop, and do a single pass thru each object in our graph.
+	i := 0
+	for {
+		if i == len(g.unnamed) {
+			break
+		}
+
+		o := g.unnamed[i]
+		i++
+
+		if o.Complete {
+			continue
+		}
+
+		if err := g.populateExplicit(o); err != nil {
+			return err
+		}
+	}
+
+	// A Second pass handles injecting Interface values to ensure we have created
+	// all concrete types first.
+	for _, o := range g.unnamed {
+		if o.Complete {
+			continue
+		}
+
+		if err := g.populateUnnamedInterface(o); err != nil {
+			return err
+		}
+	}
+
+	for _, o := range g.named {
+		if o.Complete {
+			continue
+		}
+
+		if err := g.populateUnnamedInterface(o); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (g *Graph) populateExplicit(o *Object) error {
+	// Ignore named value types.
+	if o.Name != "" && !isStructPtr(o.reflectType) {
+		return nil
+	}
+
+StructLoop:
+	for i := 0; i < o.reflectValue.Elem().NumField(); i++ {
+		field := o.reflectValue.Elem().Field(i)
+		fieldType := field.Type()
+		fieldTag := o.reflectType.Elem().Field(i).Tag
+		fieldName := o.reflectType.Elem().Field(i).Name
+		tag, err := parseTag(string(fieldTag))
+		if err != nil {
+			return fmt.Errorf(
+				"unexpected tag format `%s` for field %s in type %s",
+				string(fieldTag),
+				o.reflectType.Elem().Field(i).Name,
+				o.reflectType,
+			)
+		}
+
+		// Skip fields without a tag.
+		if tag == nil {
+			continue
+		}
+
+		// Cannot be used with unexported fields.
+		if !field.CanSet() {
+			return fmt.Errorf(
+				"inject requested on unexported field %s in type %s",
+				o.reflectType.Elem().Field(i).Name,
+				o.reflectType,
+			)
+		}
+
+		// Inline tag on anything besides a struct is considered invalid.
+		if tag.Inline && fieldType.Kind() != reflect.Struct {
+			return fmt.Errorf(
+				"inline requested on non inlined field %s in type %s",
+				o.reflectType.Elem().Field(i).Name,
+				o.reflectType,
+			)
+		}
+
+		// Don't overwrite existing values.
+		if !isNilOrZero(field, fieldType) {
+			continue
+		}
+
+		// Named injects must have been explicitly provided.
+		if tag.Name != "" {
+			existing := g.named[tag.Name]
+			if existing == nil {
+				return fmt.Errorf(
+					"did not find object named %s required by field %s in type %s",
+					tag.Name,
+					o.reflectType.Elem().Field(i).Name,
+					o.reflectType,
+				)
+			}
+
+			if !existing.reflectType.AssignableTo(fieldType) {
+				return fmt.Errorf(
+					"object named %s of type %s is not assignable to field %s (%s) in type %s",
+					tag.Name,
+					fieldType,
+					o.reflectType.Elem().Field(i).Name,
+					existing.reflectType,
+					o.reflectType,
+				)
+			}
+
+			field.Set(reflect.ValueOf(existing.Value))
+			if g.Logger != nil {
+				g.Logger.Debugf(
+					"assigned %s to field %s in %s",
+					existing,
+					o.reflectType.Elem().Field(i).Name,
+					o,
+				)
+			}
+			o.addDep(fieldName, existing)
+			continue StructLoop
+		}
+
+		// Inline struct values indicate we want to traverse into it, but not
+		// inject itself. We require an explicit "inline" tag for this to work.
+		if fieldType.Kind() == reflect.Struct {
+			if tag.Private {
+				return fmt.Errorf(
+					"cannot use private inject on inline struct on field %s in type %s",
+					o.reflectType.Elem().Field(i).Name,
+					o.reflectType,
+				)
+			}
+
+			if !tag.Inline {
+				return fmt.Errorf(
+					"inline struct on field %s in type %s requires an explicit \"inline\" tag",
+					o.reflectType.Elem().Field(i).Name,
+					o.reflectType,
+				)
+			}
+
+			err := g.Provide(&Object{
+				Value:    field.Addr().Interface(),
+				private:  true,
+				embedded: o.reflectType.Elem().Field(i).Anonymous,
+			})
+			if err != nil {
+				return err
+			}
+			continue
+		}
+
+		// Interface injection is handled in a second pass.
+		if fieldType.Kind() == reflect.Interface {
+			continue
+		}
+
+		// Maps are created and required to be private.
+		if fieldType.Kind() == reflect.Map {
+			if !tag.Private {
+				return fmt.Errorf(
+					"inject on map field %s in type %s must be named or private",
+					o.reflectType.Elem().Field(i).Name,
+					o.reflectType,
+				)
+			}
+
+			field.Set(reflect.MakeMap(fieldType))
+			if g.Logger != nil {
+				g.Logger.Debugf(
+					"made map for field %s in %s",
+					o.reflectType.Elem().Field(i).Name,
+					o,
+				)
+			}
+			continue
+		}
+
+		// Can only inject Pointers from here on.
+		if !isStructPtr(fieldType) {
+			return fmt.Errorf(
+				"found inject tag on unsupported field %s in type %s",
+				o.reflectType.Elem().Field(i).Name,
+				o.reflectType,
+			)
+		}
+
+		// Unless it's a private inject, we'll look for an existing instance of the
+		// same type.
+		if !tag.Private {
+			for _, existing := range g.unnamed {
+				if existing.private {
+					continue
+				}
+				if existing.reflectType.AssignableTo(fieldType) {
+					field.Set(reflect.ValueOf(existing.Value))
+					if g.Logger != nil {
+						g.Logger.Debugf(
+							"assigned existing %s to field %s in %s",
+							existing,
+							o.reflectType.Elem().Field(i).Name,
+							o,
+						)
+					}
+					o.addDep(fieldName, existing)
+					continue StructLoop
+				}
+			}
+		}
+
+		newValue := reflect.New(fieldType.Elem())
+		newObject := &Object{
+			Value:   newValue.Interface(),
+			private: tag.Private,
+			created: true,
+		}
+
+		// Add the newly ceated object to the known set of objects.
+		err = g.Provide(newObject)
+		if err != nil {
+			return err
+		}
+
+		// Finally assign the newly created object to our field.
+		field.Set(newValue)
+		if g.Logger != nil {
+			g.Logger.Debugf(
+				"assigned newly created %s to field %s in %s",
+				newObject,
+				o.reflectType.Elem().Field(i).Name,
+				o,
+			)
+		}
+		o.addDep(fieldName, newObject)
+	}
+	return nil
+}
+
+func (g *Graph) populateUnnamedInterface(o *Object) error {
+	// Ignore named value types.
+	if o.Name != "" && !isStructPtr(o.reflectType) {
+		return nil
+	}
+
+	for i := 0; i < o.reflectValue.Elem().NumField(); i++ {
+		field := o.reflectValue.Elem().Field(i)
+		fieldType := field.Type()
+		fieldTag := o.reflectType.Elem().Field(i).Tag
+		fieldName := o.reflectType.Elem().Field(i).Name
+		tag, err := parseTag(string(fieldTag))
+		if err != nil {
+			return fmt.Errorf(
+				"unexpected tag format `%s` for field %s in type %s",
+				string(fieldTag),
+				o.reflectType.Elem().Field(i).Name,
+				o.reflectType,
+			)
+		}
+
+		// Skip fields without a tag.
+		if tag == nil {
+			continue
+		}
+
+		// We only handle interface injection here. Other cases including errors
+		// are handled in the first pass when we inject pointers.
+		if fieldType.Kind() != reflect.Interface {
+			continue
+		}
+
+		// Interface injection can't be private because we can't instantiate new
+		// instances of an interface.
+		if tag.Private {
+			return fmt.Errorf(
+				"found private inject tag on interface field %s in type %s",
+				o.reflectType.Elem().Field(i).Name,
+				o.reflectType,
+			)
+		}
+
+		// Don't overwrite existing values.
+		if !isNilOrZero(field, fieldType) {
+			continue
+		}
+
+		// Named injects must have already been handled in populateExplicit.
+		if tag.Name != "" {
+			panic(fmt.Sprintf("unhandled named instance with name %s", tag.Name))
+		}
+
+		// Find one, and only one assignable value for the field.
+		var found *Object
+		for _, existing := range g.unnamed {
+			if existing.private {
+				continue
+			}
+			if existing.reflectType.AssignableTo(fieldType) {
+				if found != nil {
+					return fmt.Errorf(
+						"found two assignable values for field %s in type %s. one type "+
+							"%s with value %v and another type %s with value %v",
+						o.reflectType.Elem().Field(i).Name,
+						o.reflectType,
+						found.reflectType,
+						found.Value,
+						existing.reflectType,
+						existing.reflectValue,
+					)
+				}
+				found = existing
+				field.Set(reflect.ValueOf(existing.Value))
+				if g.Logger != nil {
+					g.Logger.Debugf(
+						"assigned existing %s to interface field %s in %s",
+						existing,
+						o.reflectType.Elem().Field(i).Name,
+						o,
+					)
+				}
+				o.addDep(fieldName, existing)
+			}
+		}
+
+		// If we didn't find an assignable value, we're missing something.
+		if found == nil {
+			return fmt.Errorf(
+				"found no assignable value for field %s in type %s",
+				o.reflectType.Elem().Field(i).Name,
+				o.reflectType,
+			)
+		}
+	}
+	return nil
+}
+
+// Objects returns all known objects, named as well as unnamed. The returned
+// elements are not in a stable order.
+func (g *Graph) Objects() []*Object {
+	objects := make([]*Object, 0, len(g.unnamed)+len(g.named))
+	for _, o := range g.unnamed {
+		if !o.embedded {
+			objects = append(objects, o)
+		}
+	}
+	for _, o := range g.named {
+		if !o.embedded {
+			objects = append(objects, o)
+		}
+	}
+	// randomize to prevent callers from relying on ordering
+	for i := 0; i < len(objects); i++ {
+		j := rand.Intn(i + 1)
+		objects[i], objects[j] = objects[j], objects[i]
+	}
+	return objects
+}
+
+var (
+	injectOnly    = &tag{}
+	injectPrivate = &tag{Private: true}
+	injectInline  = &tag{Inline: true}
+)
+
+type tag struct {
+	Name    string
+	Inline  bool
+	Private bool
+}
+
+func parseTag(t string) (*tag, error) {
+	found, value, err := structtag.Extract("inject", t)
+	if err != nil {
+		return nil, err
+	}
+	if !found {
+		return nil, nil
+	}
+	if value == "" {
+		return injectOnly, nil
+	}
+	if value == "inline" {
+		return injectInline, nil
+	}
+	if value == "private" {
+		return injectPrivate, nil
+	}
+	return &tag{Name: value}, nil
+}
+
+func isStructPtr(t reflect.Type) bool {
+	return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
+}
+
+func isNilOrZero(v reflect.Value, t reflect.Type) bool {
+	switch v.Kind() {
+	default:
+		return reflect.DeepEqual(v.Interface(), reflect.Zero(t).Interface())
+	case reflect.Interface, reflect.Ptr:
+		return v.IsNil()
+	}
+}

+ 30 - 0
vendor/github.com/facebookgo/inject/license

@@ -0,0 +1,30 @@
+BSD License
+
+For inject software
+
+Copyright (c) 2015, Facebook, Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+ * Neither the name Facebook nor the names of its contributors may be used to
+   endorse or promote products derived from this software without specific
+   prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 33 - 0
vendor/github.com/facebookgo/inject/patents

@@ -0,0 +1,33 @@
+Additional Grant of Patent Rights Version 2
+
+"Software" means the inject software distributed by Facebook, Inc.
+
+Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software
+("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable
+(subject to the termination provision below) license under any Necessary
+Claims, to make, have made, use, sell, offer to sell, import, and otherwise
+transfer the Software. For avoidance of doubt, no license is granted under
+Facebook’s rights in any patent claims that are infringed by (i) modifications
+to the Software made by you or any third party or (ii) the Software in
+combination with any software or other technology.
+
+The license granted hereunder will terminate, automatically and without notice,
+if you (or any of your subsidiaries, corporate affiliates or agents) initiate
+directly or indirectly, or take a direct financial interest in, any Patent
+Assertion: (i) against Facebook or any of its subsidiaries or corporate
+affiliates, (ii) against any party if such Patent Assertion arises in whole or
+in part from any software, technology, product or service of Facebook or any of
+its subsidiaries or corporate affiliates, or (iii) against any party relating
+to the Software. Notwithstanding the foregoing, if Facebook or any of its
+subsidiaries or corporate affiliates files a lawsuit alleging patent
+infringement against you in the first instance, and you respond by filing a
+patent infringement counterclaim in that lawsuit against that party that is
+unrelated to the Software, the license granted hereunder will not terminate
+under section (i) of this paragraph due to such counterclaim.
+
+A "Necessary Claim" is a claim of a patent owned by Facebook that is
+necessarily infringed by the Software standing alone.
+
+A "Patent Assertion" is any lawsuit or other action alleging direct, indirect,
+or contributory infringement or inducement to infringe any patent, including a
+cross-claim or counterclaim.

+ 27 - 0
vendor/github.com/facebookgo/structtag/license

@@ -0,0 +1,27 @@
+Copyright (c) 2012 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 61 - 0
vendor/github.com/facebookgo/structtag/structtag.go

@@ -0,0 +1,61 @@
+// Package structtag provides parsing of the defacto struct tag style.
+package structtag
+
+import (
+	"errors"
+	"strconv"
+)
+
+var errInvalidTag = errors.New("invalid tag")
+
+// Extract the quoted value for the given name returning it if it is found. The
+// found boolean helps differentiate between the "empty and found" vs "empty
+// and not found" nature of default empty strings.
+func Extract(name, tag string) (found bool, value string, err error) {
+	for tag != "" {
+		// skip leading space
+		i := 0
+		for i < len(tag) && tag[i] == ' ' {
+			i++
+		}
+		tag = tag[i:]
+		if tag == "" {
+			break
+		}
+
+		// scan to colon.
+		// a space or a quote is a syntax error
+		i = 0
+		for i < len(tag) && tag[i] != ' ' && tag[i] != ':' && tag[i] != '"' {
+			i++
+		}
+		if i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
+			return false, "", errInvalidTag
+		}
+		foundName := string(tag[:i])
+		tag = tag[i+1:]
+
+		// scan quoted string to find value
+		i = 1
+		for i < len(tag) && tag[i] != '"' {
+			if tag[i] == '\\' {
+				i++
+			}
+			i++
+		}
+		if i >= len(tag) {
+			return false, "", errInvalidTag
+		}
+		qvalue := string(tag[:i+1])
+		tag = tag[i+1:]
+
+		if foundName == name {
+			value, err := strconv.Unquote(qvalue)
+			if err != nil {
+				return false, "", err
+			}
+			return true, value, nil
+		}
+	}
+	return false, "", nil
+}

+ 23 - 0
vendor/github.com/pkg/errors/LICENSE

@@ -0,0 +1,23 @@
+Copyright (c) 2015, Dave Cheney <dave@cheney.net>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 269 - 0
vendor/github.com/pkg/errors/errors.go

@@ -0,0 +1,269 @@
+// Package errors provides simple error handling primitives.
+//
+// The traditional error handling idiom in Go is roughly akin to
+//
+//     if err != nil {
+//             return err
+//     }
+//
+// which applied recursively up the call stack results in error reports
+// without context or debugging information. The errors package allows
+// programmers to add context to the failure path in their code in a way
+// that does not destroy the original value of the error.
+//
+// Adding context to an error
+//
+// The errors.Wrap function returns a new error that adds context to the
+// original error by recording a stack trace at the point Wrap is called,
+// and the supplied message. For example
+//
+//     _, err := ioutil.ReadAll(r)
+//     if err != nil {
+//             return errors.Wrap(err, "read failed")
+//     }
+//
+// If additional control is required the errors.WithStack and errors.WithMessage
+// functions destructure errors.Wrap into its component operations of annotating
+// an error with a stack trace and an a message, respectively.
+//
+// Retrieving the cause of an error
+//
+// Using errors.Wrap constructs a stack of errors, adding context to the
+// preceding error. Depending on the nature of the error it may be necessary
+// to reverse the operation of errors.Wrap to retrieve the original error
+// for inspection. Any error value which implements this interface
+//
+//     type causer interface {
+//             Cause() error
+//     }
+//
+// can be inspected by errors.Cause. errors.Cause will recursively retrieve
+// the topmost error which does not implement causer, which is assumed to be
+// the original cause. For example:
+//
+//     switch err := errors.Cause(err).(type) {
+//     case *MyError:
+//             // handle specifically
+//     default:
+//             // unknown error
+//     }
+//
+// causer interface is not exported by this package, but is considered a part
+// of stable public API.
+//
+// Formatted printing of errors
+//
+// All error values returned from this package implement fmt.Formatter and can
+// be formatted by the fmt package. The following verbs are supported
+//
+//     %s    print the error. If the error has a Cause it will be
+//           printed recursively
+//     %v    see %s
+//     %+v   extended format. Each Frame of the error's StackTrace will
+//           be printed in detail.
+//
+// Retrieving the stack trace of an error or wrapper
+//
+// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
+// invoked. This information can be retrieved with the following interface.
+//
+//     type stackTracer interface {
+//             StackTrace() errors.StackTrace
+//     }
+//
+// Where errors.StackTrace is defined as
+//
+//     type StackTrace []Frame
+//
+// The Frame type represents a call site in the stack trace. Frame supports
+// the fmt.Formatter interface that can be used for printing information about
+// the stack trace of this error. For example:
+//
+//     if err, ok := err.(stackTracer); ok {
+//             for _, f := range err.StackTrace() {
+//                     fmt.Printf("%+s:%d", f)
+//             }
+//     }
+//
+// stackTracer interface is not exported by this package, but is considered a part
+// of stable public API.
+//
+// See the documentation for Frame.Format for more details.
+package errors
+
+import (
+	"fmt"
+	"io"
+)
+
+// New returns an error with the supplied message.
+// New also records the stack trace at the point it was called.
+func New(message string) error {
+	return &fundamental{
+		msg:   message,
+		stack: callers(),
+	}
+}
+
+// Errorf formats according to a format specifier and returns the string
+// as a value that satisfies error.
+// Errorf also records the stack trace at the point it was called.
+func Errorf(format string, args ...interface{}) error {
+	return &fundamental{
+		msg:   fmt.Sprintf(format, args...),
+		stack: callers(),
+	}
+}
+
+// fundamental is an error that has a message and a stack, but no caller.
+type fundamental struct {
+	msg string
+	*stack
+}
+
+func (f *fundamental) Error() string { return f.msg }
+
+func (f *fundamental) Format(s fmt.State, verb rune) {
+	switch verb {
+	case 'v':
+		if s.Flag('+') {
+			io.WriteString(s, f.msg)
+			f.stack.Format(s, verb)
+			return
+		}
+		fallthrough
+	case 's':
+		io.WriteString(s, f.msg)
+	case 'q':
+		fmt.Fprintf(s, "%q", f.msg)
+	}
+}
+
+// WithStack annotates err with a stack trace at the point WithStack was called.
+// If err is nil, WithStack returns nil.
+func WithStack(err error) error {
+	if err == nil {
+		return nil
+	}
+	return &withStack{
+		err,
+		callers(),
+	}
+}
+
+type withStack struct {
+	error
+	*stack
+}
+
+func (w *withStack) Cause() error { return w.error }
+
+func (w *withStack) Format(s fmt.State, verb rune) {
+	switch verb {
+	case 'v':
+		if s.Flag('+') {
+			fmt.Fprintf(s, "%+v", w.Cause())
+			w.stack.Format(s, verb)
+			return
+		}
+		fallthrough
+	case 's':
+		io.WriteString(s, w.Error())
+	case 'q':
+		fmt.Fprintf(s, "%q", w.Error())
+	}
+}
+
+// Wrap returns an error annotating err with a stack trace
+// at the point Wrap is called, and the supplied message.
+// If err is nil, Wrap returns nil.
+func Wrap(err error, message string) error {
+	if err == nil {
+		return nil
+	}
+	err = &withMessage{
+		cause: err,
+		msg:   message,
+	}
+	return &withStack{
+		err,
+		callers(),
+	}
+}
+
+// Wrapf returns an error annotating err with a stack trace
+// at the point Wrapf is call, and the format specifier.
+// If err is nil, Wrapf returns nil.
+func Wrapf(err error, format string, args ...interface{}) error {
+	if err == nil {
+		return nil
+	}
+	err = &withMessage{
+		cause: err,
+		msg:   fmt.Sprintf(format, args...),
+	}
+	return &withStack{
+		err,
+		callers(),
+	}
+}
+
+// WithMessage annotates err with a new message.
+// If err is nil, WithMessage returns nil.
+func WithMessage(err error, message string) error {
+	if err == nil {
+		return nil
+	}
+	return &withMessage{
+		cause: err,
+		msg:   message,
+	}
+}
+
+type withMessage struct {
+	cause error
+	msg   string
+}
+
+func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
+func (w *withMessage) Cause() error  { return w.cause }
+
+func (w *withMessage) Format(s fmt.State, verb rune) {
+	switch verb {
+	case 'v':
+		if s.Flag('+') {
+			fmt.Fprintf(s, "%+v\n", w.Cause())
+			io.WriteString(s, w.msg)
+			return
+		}
+		fallthrough
+	case 's', 'q':
+		io.WriteString(s, w.Error())
+	}
+}
+
+// Cause returns the underlying cause of the error, if possible.
+// An error value has a cause if it implements the following
+// interface:
+//
+//     type causer interface {
+//            Cause() error
+//     }
+//
+// If the error does not implement Cause, the original error will
+// be returned. If the error is nil, nil will be returned without further
+// investigation.
+func Cause(err error) error {
+	type causer interface {
+		Cause() error
+	}
+
+	for err != nil {
+		cause, ok := err.(causer)
+		if !ok {
+			break
+		}
+		err = cause.Cause()
+	}
+	return err
+}

+ 178 - 0
vendor/github.com/pkg/errors/stack.go

@@ -0,0 +1,178 @@
+package errors
+
+import (
+	"fmt"
+	"io"
+	"path"
+	"runtime"
+	"strings"
+)
+
+// Frame represents a program counter inside a stack frame.
+type Frame uintptr
+
+// pc returns the program counter for this frame;
+// multiple frames may have the same PC value.
+func (f Frame) pc() uintptr { return uintptr(f) - 1 }
+
+// file returns the full path to the file that contains the
+// function for this Frame's pc.
+func (f Frame) file() string {
+	fn := runtime.FuncForPC(f.pc())
+	if fn == nil {
+		return "unknown"
+	}
+	file, _ := fn.FileLine(f.pc())
+	return file
+}
+
+// line returns the line number of source code of the
+// function for this Frame's pc.
+func (f Frame) line() int {
+	fn := runtime.FuncForPC(f.pc())
+	if fn == nil {
+		return 0
+	}
+	_, line := fn.FileLine(f.pc())
+	return line
+}
+
+// Format formats the frame according to the fmt.Formatter interface.
+//
+//    %s    source file
+//    %d    source line
+//    %n    function name
+//    %v    equivalent to %s:%d
+//
+// Format accepts flags that alter the printing of some verbs, as follows:
+//
+//    %+s   path of source file relative to the compile time GOPATH
+//    %+v   equivalent to %+s:%d
+func (f Frame) Format(s fmt.State, verb rune) {
+	switch verb {
+	case 's':
+		switch {
+		case s.Flag('+'):
+			pc := f.pc()
+			fn := runtime.FuncForPC(pc)
+			if fn == nil {
+				io.WriteString(s, "unknown")
+			} else {
+				file, _ := fn.FileLine(pc)
+				fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
+			}
+		default:
+			io.WriteString(s, path.Base(f.file()))
+		}
+	case 'd':
+		fmt.Fprintf(s, "%d", f.line())
+	case 'n':
+		name := runtime.FuncForPC(f.pc()).Name()
+		io.WriteString(s, funcname(name))
+	case 'v':
+		f.Format(s, 's')
+		io.WriteString(s, ":")
+		f.Format(s, 'd')
+	}
+}
+
+// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
+type StackTrace []Frame
+
+func (st StackTrace) Format(s fmt.State, verb rune) {
+	switch verb {
+	case 'v':
+		switch {
+		case s.Flag('+'):
+			for _, f := range st {
+				fmt.Fprintf(s, "\n%+v", f)
+			}
+		case s.Flag('#'):
+			fmt.Fprintf(s, "%#v", []Frame(st))
+		default:
+			fmt.Fprintf(s, "%v", []Frame(st))
+		}
+	case 's':
+		fmt.Fprintf(s, "%s", []Frame(st))
+	}
+}
+
+// stack represents a stack of program counters.
+type stack []uintptr
+
+func (s *stack) Format(st fmt.State, verb rune) {
+	switch verb {
+	case 'v':
+		switch {
+		case st.Flag('+'):
+			for _, pc := range *s {
+				f := Frame(pc)
+				fmt.Fprintf(st, "\n%+v", f)
+			}
+		}
+	}
+}
+
+func (s *stack) StackTrace() StackTrace {
+	f := make([]Frame, len(*s))
+	for i := 0; i < len(f); i++ {
+		f[i] = Frame((*s)[i])
+	}
+	return f
+}
+
+func callers() *stack {
+	const depth = 32
+	var pcs [depth]uintptr
+	n := runtime.Callers(3, pcs[:])
+	var st stack = pcs[0:n]
+	return &st
+}
+
+// funcname removes the path prefix component of a function's name reported by func.Name().
+func funcname(name string) string {
+	i := strings.LastIndex(name, "/")
+	name = name[i+1:]
+	i = strings.Index(name, ".")
+	return name[i+1:]
+}
+
+func trimGOPATH(name, file string) string {
+	// Here we want to get the source file path relative to the compile time
+	// GOPATH. As of Go 1.6.x there is no direct way to know the compiled
+	// GOPATH at runtime, but we can infer the number of path segments in the
+	// GOPATH. We note that fn.Name() returns the function name qualified by
+	// the import path, which does not include the GOPATH. Thus we can trim
+	// segments from the beginning of the file path until the number of path
+	// separators remaining is one more than the number of path separators in
+	// the function name. For example, given:
+	//
+	//    GOPATH     /home/user
+	//    file       /home/user/src/pkg/sub/file.go
+	//    fn.Name()  pkg/sub.Type.Method
+	//
+	// We want to produce:
+	//
+	//    pkg/sub/file.go
+	//
+	// From this we can easily see that fn.Name() has one less path separator
+	// than our desired output. We count separators from the end of the file
+	// path until it finds two more than in the function name and then move
+	// one character forward to preserve the initial path segment without a
+	// leading separator.
+	const sep = "/"
+	goal := strings.Count(name, sep) + 2
+	i := len(file)
+	for n := 0; n < goal; n++ {
+		i = strings.LastIndex(file[:i], sep)
+		if i == -1 {
+			// not enough separators found, set i so that the slice expression
+			// below leaves file unmodified
+			i = -len(sep)
+			break
+		}
+	}
+	// get back to 0 or trim the leading separator
+	file = file[i+len(sep):]
+	return file
+}