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
This commit is contained in:
Carl Bergquist 2018-04-27 13:41:58 +02:00 committed by Torkel Ödegaard
parent afce0feb05
commit 28f7b6dad1
30 changed files with 1678 additions and 105 deletions

View File

@ -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
.gitignore vendored
View File

@ -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

211
Gopkg.lock generated
View File

@ -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

View File

@ -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()
}

View File

@ -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)

View File

@ -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 {

View File

@ -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",

View File

@ -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{},

View File

@ -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)
})

View File

@ -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)

View File

@ -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)

View File

@ -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,30 +89,64 @@ 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) })
}
// cleanup service
cleanUpService := cleanup.NewCleanUpService()
g.childRoutines.Go(func() error { return cleanUpService.Run(g.context) })
if err = notifications.Init(); err != nil {
return fmt.Errorf("Notification service failed to initialize. error: %v", err)
}
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()
// Add all services to dependency graph
for _, service := range services {
serviceGraph.Provide(&inject.Object{Value: service})
}
serviceGraph.Provide(&inject.Object{Value: g})
// Inject dependencies to services
if err := serviceGraph.Populate(); err != nil {
return fmt.Errorf("Failed to populate service dependency: %v", err)
}
// 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 initSql() {
sqlstore.NewEngine()
sqlstore.EnsureAdminUser()
}
func (g *GrafanaServerImpl) initLogging() {
err := setting.NewConfigContext(&setting.CommandLineArgs{
Config: *configFile,
@ -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
pkg/extensions/main.go Normal file
View File

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

View File

@ -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
pkg/registry/registry.go Normal file
View File

@ -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()
}

View File

@ -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) IsDisabled() bool {
return !setting.AlertingEnabled || !setting.ExecuteAlerts
}
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 {
e.log.Info("Initializing Alerting")
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
}

View File

@ -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) Init() error {
service.log = log.New("cleanup")
return nil
}
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) start(ctx context.Context) error {
service.cleanUpTmpFiles()
ticker := time.NewTicker(time.Minute * 10)

View File

@ -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,

View File

@ -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() {

View File

@ -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) {

View File

@ -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)

View File

@ -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
vendor/github.com/facebookgo/inject/inject.go generated vendored Normal file
View File

@ -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
vendor/github.com/facebookgo/inject/license generated vendored Normal file
View File

@ -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
vendor/github.com/facebookgo/inject/patents generated vendored Normal file
View File

@ -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
Facebooks 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
vendor/github.com/facebookgo/structtag/license generated vendored Normal file
View File

@ -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
vendor/github.com/facebookgo/structtag/structtag.go generated vendored Normal file
View File

@ -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
vendor/github.com/pkg/errors/LICENSE generated vendored Normal file
View File

@ -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
vendor/github.com/pkg/errors/errors.go generated vendored Normal file
View File

@ -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
vendor/github.com/pkg/errors/stack.go generated vendored Normal file
View File

@ -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
}