Migrate to Wire for dependency injection (#32289)

Fixes #30144

Co-authored-by: dsotirakis <sotirakis.dim@gmail.com>
Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
Co-authored-by: Ida Furjesova <ida.furjesova@grafana.com>
Co-authored-by: Jack Westbrook <jack.westbrook@gmail.com>
Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com>
Co-authored-by: spinillos <selenepinillos@gmail.com>
Co-authored-by: Karl Persson <kalle.persson@grafana.com>
Co-authored-by: Leonard Gram <leo@xlson.com>
This commit is contained in:
Arve Knudsen 2021-08-25 15:11:22 +02:00 committed by GitHub
parent e61bc33163
commit 78596a6756
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
180 changed files with 2384 additions and 2401 deletions

12
.bingo/.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
# Ignore everything
*
# But not these files:
!.gitignore
!*.mod
!README.md
!Variables.mk
!variables.env
*tmp.mod

14
.bingo/README.md Normal file
View File

@ -0,0 +1,14 @@
# Project Development Dependencies.
This is directory which stores Go modules with pinned buildable package that is used within this repository, managed by https://github.com/bwplotka/bingo.
* Run `bingo get` to install all tools having each own module file in this directory.
* Run `bingo get <tool>` to install <tool> that have own module file in this directory.
* For Makefile: Make sure to put `include .bingo/Variables.mk` in your Makefile, then use $(<upper case tool name>) variable where <tool> is the .bingo/<tool>.mod.
* For shell: Run `source .bingo/variables.env` to source all environment variable for each tool.
* For go: Import `.bingo/variables.go` to for variable names.
* See https://github.com/bwplotka/bingo or -h on how to add, remove or change binaries dependencies.
## Requirements
* Go 1.14+

25
.bingo/Variables.mk Normal file
View File

@ -0,0 +1,25 @@
# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.4.3. DO NOT EDIT.
# All tools are designed to be build inside $GOBIN.
BINGO_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
GOPATH ?= $(shell go env GOPATH)
GOBIN ?= $(firstword $(subst :, ,${GOPATH}))/bin
GO ?= $(shell which go)
# Below generated variables ensure that every time a tool under each variable is invoked, the correct version
# will be used; reinstalling only if needed.
# For example for wire variable:
#
# In your main Makefile (for non array binaries):
#
#include .bingo/Variables.mk # Assuming -dir was set to .bingo .
#
#command: $(WIRE)
# @echo "Running wire"
# @$(WIRE) <flags/args..>
#
WIRE := $(GOBIN)/wire-v0.5.0
$(WIRE): $(BINGO_DIR)/wire.mod
@# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies.
@echo "(re)installing $(GOBIN)/wire-v0.5.0"
@cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=wire.mod -o=$(GOBIN)/wire-v0.5.0 "github.com/google/wire/cmd/wire"

1
.bingo/go.mod Normal file
View File

@ -0,0 +1 @@
module _ // Fake go.mod auto-created by 'bingo' for go -moddir compatibility with non-Go projects. Commit this file, together with other .mod files.

12
.bingo/variables.env Normal file
View File

@ -0,0 +1,12 @@
# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.4.3. DO NOT EDIT.
# All tools are designed to be build inside $GOBIN.
# Those variables will work only until 'bingo get' was invoked, or if tools were installed via Makefile's Variables.mk.
GOBIN=${GOBIN:=$(go env GOBIN)}
if [ -z "$GOBIN" ]; then
GOBIN="$(go env GOPATH)/bin"
fi
WIRE="${GOBIN}/wire-v0.5.0"

5
.bingo/wire.mod Normal file
View File

@ -0,0 +1,5 @@
module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT
go 1.16
require github.com/google/wire v0.5.0 // cmd/wire

View File

@ -1,5 +1,6 @@
[run]
init_cmds = [
["make", "gen-go"],
["go", "run", "build.go", "-dev", "build-cli"],
["go", "run", "build.go", "-dev", "build-server"],
["./bin/grafana-server", "-packaging=dev", "cfg:app_mode=development"]

View File

@ -17,7 +17,7 @@ steps:
image: grafana/build-container:1.4.1
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.3/grabpl
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.4.0/grabpl
- chmod +x bin/grabpl
- ./bin/grabpl verify-drone
- curl -fLO https://github.com/jwilder/dockerize/releases/download/v$${DOCKERIZE_VERSION}/dockerize-linux-amd64-v$${DOCKERIZE_VERSION}.tar.gz
@ -45,6 +45,7 @@ steps:
- name: lint-backend
image: grafana/build-container:1.4.1
commands:
- make gen-go
- ./bin/grabpl lint-backend --edition oss
environment:
CGO_ENABLED: 1
@ -253,7 +254,7 @@ steps:
image: grafana/build-container:1.4.1
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.3/grabpl
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.4.0/grabpl
- chmod +x bin/grabpl
- ./bin/grabpl verify-drone
- curl -fLO https://github.com/jwilder/dockerize/releases/download/v$${DOCKERIZE_VERSION}/dockerize-linux-amd64-v$${DOCKERIZE_VERSION}.tar.gz
@ -293,6 +294,7 @@ steps:
- name: lint-backend
image: grafana/build-container:1.4.1
commands:
- make gen-go
- ./bin/grabpl lint-backend --edition oss
environment:
CGO_ENABLED: 1
@ -596,7 +598,7 @@ steps:
image: grafana/ci-wix:0.1.1
commands:
- $$ProgressPreference = "SilentlyContinue"
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.3/windows/grabpl.exe -OutFile grabpl.exe
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.4.0/windows/grabpl.exe -OutFile grabpl.exe
- name: build-windows-installer
image: grafana/ci-wix:0.1.1
@ -645,7 +647,7 @@ steps:
image: grafana/build-container:1.4.1
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.3/grabpl
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.4.0/grabpl
- chmod +x bin/grabpl
- ./bin/grabpl verify-drone
environment:
@ -733,7 +735,7 @@ steps:
image: grafana/build-container:1.4.1
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.3/grabpl
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.4.0/grabpl
- chmod +x bin/grabpl
- ./bin/grabpl verify-drone
- ./bin/grabpl verify-version ${DRONE_TAG}
@ -762,6 +764,7 @@ steps:
- name: lint-backend
image: grafana/build-container:1.4.1
commands:
- make gen-go
- ./bin/grabpl lint-backend --edition oss
environment:
CGO_ENABLED: 1
@ -1036,7 +1039,7 @@ steps:
image: grafana/ci-wix:0.1.1
commands:
- $$ProgressPreference = "SilentlyContinue"
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.3/windows/grabpl.exe -OutFile grabpl.exe
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.4.0/windows/grabpl.exe -OutFile grabpl.exe
- name: build-windows-installer
image: grafana/ci-wix:0.1.1
@ -1086,7 +1089,7 @@ steps:
image: grafana/build-container:1.4.1
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.3/grabpl
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.4.0/grabpl
- chmod +x bin/grabpl
- git clone "https://$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git"
- cd grafana-enterprise
@ -1134,6 +1137,7 @@ steps:
- name: lint-backend
image: grafana/build-container:1.4.1
commands:
- make gen-go
- ./bin/grabpl lint-backend --edition enterprise
environment:
CGO_ENABLED: 1
@ -1195,6 +1199,7 @@ steps:
- name: lint-backend-enterprise2
image: grafana/build-container:1.4.1
commands:
- make gen-go
- ./bin/grabpl lint-backend --edition enterprise2
environment:
CGO_ENABLED: 1
@ -1506,7 +1511,7 @@ steps:
image: grafana/ci-wix:0.1.1
commands:
- $$ProgressPreference = "SilentlyContinue"
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.3/windows/grabpl.exe -OutFile grabpl.exe
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.4.0/windows/grabpl.exe -OutFile grabpl.exe
- git clone "https://$$env:GITHUB_TOKEN@github.com/grafana/grafana-enterprise.git"
- cd grafana-enterprise
- git checkout ${DRONE_TAG}
@ -1574,7 +1579,7 @@ steps:
image: grafana/build-container:1.4.1
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.3/grabpl
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.4.0/grabpl
- chmod +x bin/grabpl
- ./bin/grabpl verify-drone
- ./bin/grabpl verify-version ${DRONE_TAG}
@ -1682,7 +1687,7 @@ steps:
image: grafana/build-container:1.4.1
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.3/grabpl
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.4.0/grabpl
- chmod +x bin/grabpl
- ./bin/grabpl verify-drone
- ./bin/grabpl verify-version v7.3.0-test
@ -1711,6 +1716,7 @@ steps:
- name: lint-backend
image: grafana/build-container:1.4.1
commands:
- make gen-go
- ./bin/grabpl lint-backend --edition oss
environment:
CGO_ENABLED: 1
@ -1974,7 +1980,7 @@ steps:
image: grafana/ci-wix:0.1.1
commands:
- $$ProgressPreference = "SilentlyContinue"
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.3/windows/grabpl.exe -OutFile grabpl.exe
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.4.0/windows/grabpl.exe -OutFile grabpl.exe
- name: build-windows-installer
image: grafana/ci-wix:0.1.1
@ -2024,7 +2030,7 @@ steps:
image: grafana/build-container:1.4.1
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.3/grabpl
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.4.0/grabpl
- chmod +x bin/grabpl
- git clone "https://$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git"
- cd grafana-enterprise
@ -2072,6 +2078,7 @@ steps:
- name: lint-backend
image: grafana/build-container:1.4.1
commands:
- make gen-go
- ./bin/grabpl lint-backend --edition enterprise
environment:
CGO_ENABLED: 1
@ -2133,6 +2140,7 @@ steps:
- name: lint-backend-enterprise2
image: grafana/build-container:1.4.1
commands:
- make gen-go
- ./bin/grabpl lint-backend --edition enterprise2
environment:
CGO_ENABLED: 1
@ -2438,7 +2446,7 @@ steps:
image: grafana/ci-wix:0.1.1
commands:
- $$ProgressPreference = "SilentlyContinue"
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.3/windows/grabpl.exe -OutFile grabpl.exe
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.4.0/windows/grabpl.exe -OutFile grabpl.exe
- git clone "https://$$env:GITHUB_TOKEN@github.com/grafana/grafana-enterprise.git"
- cd grafana-enterprise
- git checkout main
@ -2506,7 +2514,7 @@ steps:
image: grafana/build-container:1.4.1
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.3/grabpl
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.4.0/grabpl
- chmod +x bin/grabpl
- ./bin/grabpl verify-drone
- ./bin/grabpl verify-version v7.3.0-test
@ -2614,7 +2622,7 @@ steps:
image: grafana/build-container:1.4.1
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.3/grabpl
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.4.0/grabpl
- chmod +x bin/grabpl
- ./bin/grabpl verify-drone
- curl -fLO https://github.com/jwilder/dockerize/releases/download/v$${DOCKERIZE_VERSION}/dockerize-linux-amd64-v$${DOCKERIZE_VERSION}.tar.gz
@ -2642,6 +2650,7 @@ steps:
- name: lint-backend
image: grafana/build-container:1.4.1
commands:
- make gen-go
- ./bin/grabpl lint-backend --edition oss
environment:
CGO_ENABLED: 1
@ -2881,7 +2890,7 @@ steps:
image: grafana/ci-wix:0.1.1
commands:
- $$ProgressPreference = "SilentlyContinue"
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.3/windows/grabpl.exe -OutFile grabpl.exe
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.4.0/windows/grabpl.exe -OutFile grabpl.exe
- name: build-windows-installer
image: grafana/ci-wix:0.1.1
@ -2927,7 +2936,7 @@ steps:
image: grafana/build-container:1.4.1
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.3/grabpl
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.4.0/grabpl
- chmod +x bin/grabpl
- git clone "https://$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git"
- cd grafana-enterprise
@ -2974,6 +2983,7 @@ steps:
- name: lint-backend
image: grafana/build-container:1.4.1
commands:
- make gen-go
- ./bin/grabpl lint-backend --edition enterprise
environment:
CGO_ENABLED: 1
@ -3032,6 +3042,7 @@ steps:
- name: lint-backend-enterprise2
image: grafana/build-container:1.4.1
commands:
- make gen-go
- ./bin/grabpl lint-backend --edition enterprise2
environment:
CGO_ENABLED: 1
@ -3344,7 +3355,7 @@ steps:
image: grafana/ci-wix:0.1.1
commands:
- $$ProgressPreference = "SilentlyContinue"
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.3/windows/grabpl.exe -OutFile grabpl.exe
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.4.0/windows/grabpl.exe -OutFile grabpl.exe
- git clone "https://$$env:GITHUB_TOKEN@github.com/grafana/grafana-enterprise.git"
- cd grafana-enterprise
- git checkout $$env:DRONE_BRANCH
@ -3488,6 +3499,6 @@ get:
---
kind: signature
hmac: 136823e64c71ad2b4b447ef869ba3c896800f7df8111083665753a9db243dcb6
hmac: 56e9f30fa716aff37a2d3db8534dc0ad489270905f740f3772499a837beaa85c
...

4
.gitignore vendored
View File

@ -75,6 +75,7 @@ profile.cov
/pkg/cmd/grafana-server/grafana-server
/pkg/cmd/grafana-server/debug
/pkg/extensions/*
/pkg/server/wireexts_enterprise.go
!/pkg/extensions/main.go
/public/app/extensions
debug.test
@ -127,3 +128,6 @@ compilation-stats.json
# auto generated frontend docs
/docs/sources/packages_api
# auto generated Go files
*_gen.go

View File

@ -2,7 +2,10 @@
##
## For more information, refer to https://suva.sh/posts/well-documented-makefiles/
WIRE_TAGS = "oss"
-include local/Makefile
include .bingo/Variables.mk
.PHONY: all deps-go deps-js deps build-go build-server build-cli build-js build build-docker-dev build-docker-full lint-go golangci-lint test-go test-js test run run-frontend clean devenv devenv-down protobuf drone help
@ -27,7 +30,11 @@ node_modules: package.json yarn.lock ## Install node modules.
##@ Building
build-go: ## Build all Go binaries.
gen-go: $(WIRE)
@echo "generate go files"
$(WIRE) gen -tags $(WIRE_TAGS) ./pkg/server
build-go: gen-go ## Build all Go binaries.
@echo "build go files"
$(GO) run build.go build

View File

@ -2,71 +2,136 @@
A Grafana _service_ encapsulates and exposes application logic to the rest of the application, through a set of related operations.
Before a service can start communicating with the rest of Grafana, it needs to be registered in the _service registry_.
The service registry keeps track of all available services during runtime. On start-up, Grafana uses the registry to build a dependency graph of services, a _service graph_.
Grafana uses [Wire](https://github.com/google/wire), which is a code generation tool that automates connecting components using [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection). Dependencies between components are represented in Wire as function parameters, encouraging explicit initialization instead of global variables.
Even though the services in Grafana do different things, they share a number of patterns. To better understand how a service works, let's build one from scratch!
## Create a service
Before a service can start communicating with the rest of Grafana, it needs to be registered with Wire, see `ProvideService` factory function/method in the service example below and how it's being referenced in the wire.go example below.
To start building a service:
When Wire is run it will inspect the parameters of `ProvideService` and make sure that all it's dependencies has been wired up and initialized properly.
- Create a new Go package `mysvc` in the [pkg/services](/pkg/services) directory.
- Create a `service.go` file inside your new directory.
All services need to implement the [Service](https://godoc.org/github.com/grafana/grafana/pkg/registry#Service) interface:
**Service example:**
```go
type MyService struct {
package example
// Service service is the service responsible for X, Y and Z.
type Service struct {
logger log.Logger
cfg *setting.Cfg
sqlStore *sqlstore.SQLStore
}
func (s *MyService) Init() error {
// ProvideService provides Service as dependency for other services.
func ProvideService(cfg *setting.Cfg, sqlStore *sqlstore.SQLStore) (*Service, error) {
s := &Service{
logger: log.New("service"),
cfg: cfg,
sqlStore: sqlStore,
}
if s.IsDisabled() {
// skip certain initialization logic
return s, nil
}
if err := s.init(); err != nil {
return nil, err
}
return s, nil
}
func (s *Service) init() error {
// additional initialization logic...
return nil
}
```
The `Init` method is used to initialize and configure the service to make it ready to use. Services that return an error halt Grafana's startup process and cause the error to be logged as it exits.
// IsDisabled returns true if the service is disabled.
//
// Satisfies the registry.CanBeDisabled interface which will guarantee
// that Run() is not called if the service is disabled.
func (s *Service) IsDisabled() bool {
return !s.cfg.IsServiceEnabled()
}
## Register a service
Every service needs to be registered with the application for it to be included in the service graph.
To register a service, call the `registry.RegisterService` function in an `init` function within your package.
```go
func init() {
registry.RegisterService(&MyService{})
// Run runs the service in the background.
//
// Satisfies the registry.BackgroundService interface which will
// guarantee that the service can be registered as a background service.
func (s *Service) Run(ctx context.Context) error {
// background service logic...
<-ctx.Done()
return ctx.Err()
}
```
`init` functions are only run whenever a package is imported, so we also need to import the package in the application. In the `server.go` file under `pkg/server`, import the package we just created:
[wire.go](/pkg/server/wire.go)
```go
import _ "github.com/grafana/grafana/pkg/services/mysvc"
```
// +build wireinject
## Dependencies
package server
Grafana uses the [inject](https://github.com/facebookgo/inject) package to inject dependencies during runtime.
import (
"github.com/google/wire"
"github.com/grafana/grafana/pkg/example"
"github.com/grafana/grafana/pkg/services/sqlstore"
)
For example, to access the [bus](communication.md), add it to the `MyService` struct:
var wireBasicSet = wire.NewSet(
example.ProvideService,
```go
type MyService struct {
Bus bus.Bus `inject:""`
)
var wireSet = wire.NewSet(
wireBasicSet,
sqlstore.ProvideService,
)
var wireTestSet = wire.NewSet(
wireBasicSet,
)
func Initialize(cla setting.CommandLineArgs, opts Options, apiOpts api.ServerOptions) (*Server, error) {
wire.Build(wireExtsSet)
return &Server{}, nil
}
```
You can also inject other services in the same way:
```go
type MyService struct {
Service other.Service `inject:""`
func InitializeForTest(cla setting.CommandLineArgs, opts Options, apiOpts api.ServerOptions, sqlStore *sqlstore.SQLStore) (*Server, error) {
wire.Build(wireExtsTestSet)
return &Server{}, nil
}
```
> **Note:** Any injected dependency needs to be an exported field. Any unexported fields result in a runtime error.
## Background services
A background service is a service that runs in the background of the lifecycle between Grafana starts up and shutdown. If you want a service to be run in the background your Service should satisfy the `registry.BackgroundService` interface and add it as argument to the [ProvideBackgroundServiceRegistry](/pkg/server/backgroundsvcs/background_services.go) function and add it as argument to `NewBackgroundServiceRegistry` to register it as a background service.
You can see an example implementation above of the Run method.
## Disabled services
If you want to guarantee that a background service is not run by Grafana when certain criteria is met/service is disabled your service should satisfy the `registry.CanBeDisabled` interface. When the service.IsDisabled method return false Grafana would not call the service.Run method.
If you want to run certain initialization code if service is disabled or not, you need to handle this in the service factory method.
You can see an example implementation above of the IsDisabled method and custom initialization code when service is disabled.
## Run Wire / generate code
When running `make run` it will call `make gen-go` on the first run. `gen-go` in turn will call the wire binary and generate the code in [wire_gen.go](/pkg/server/wire_gen.go). The wire binary is installed using [bingo](https://github.com/bwplotka/bingo) which will make sure to download and install all the tools needed, including the Wire binary at using a specific version.
## OSS vs Enterprise
Grafana OSS and Grafana Enterprise shares code and dependencies. Grafana Enterprise might need to override/extend certain OSS services.
There's a [wireexts_oss.go](/pkg/server/wireexts_oss.go) that has the `wireinject` and `oss` build tags as requirements. Here services that might have other implementations, e.g. Grafana Enterprise, can be registered.
Similarly, there's a wireexts_enterprise.go file in the Enterprise source code repository where other service implementations can be overridden/be registered.
To extend oss background service create a specific background interface for that type and inject that type to [ProvideBackgroundServiceRegistry](/pkg/server/backgroundsvcs/background_services.go) instead of the concrete type. Then add a wire binding for that interface in [wireexts_oss.go](/pkg/server/wireexts_oss.go) and in the enterprise wireexts file.
## Methods

View File

@ -4,13 +4,13 @@ This style guide applies to all documentation created for Grafana products.
For information about how to write technical documentation, refer to the following resources:
* [Google Technical Writing courses](https://developers.google.com/tech-writing)
* [Divio documentation system](https://documentation.divio.com/)
* [Vue writing principles](https://v3.vuejs.org/guide/contributing/writing-guide.html#principles)
- [Google Technical Writing courses](https://developers.google.com/tech-writing)
- [Divio documentation system](https://documentation.divio.com/)
- [Vue writing principles](https://v3.vuejs.org/guide/contributing/writing-guide.html#principles)
## Contributing
The *Documentation style guide* is a living document. Add to it whenever a style decision is made or a question is answered regarding style, grammar, or word choice.
The _Documentation style guide_ is a living document. Add to it whenever a style decision is made or a question is answered regarding style, grammar, or word choice.
## Published guides
@ -46,7 +46,7 @@ Avoid _master_ or _slave_.
## Grafana-specific style
The following guidelines are specific to Grafana documentation. For the most part, these are *guidelines* are not rigid rules. If you have questions, then please ask in the #docs channel of Grafana Slack.
The following guidelines are specific to Grafana documentation. For the most part, these are _guidelines_ are not rigid rules. If you have questions, then please ask in the #docs channel of Grafana Slack.
### General
@ -108,6 +108,7 @@ However, sometimes we need to use headings as numbered steps. This is mostly in
If that is the case, then use the following format for headings:
##### Step 1. Install the software
##### Step 2. Run the software
### Images
@ -153,6 +154,7 @@ In general, "integration" is not capitalized. Only capitalize it if it is capita
The first letter of the name of an integration is always capitalized, even if the original named source is lowercase.
**Examples:**
- MySQL Integration
- CockroachDB Integration
- Etcd Integration
@ -216,16 +218,20 @@ Warnings tell the user not to do something. For example:
- Do not assume everyone is using Linux. Make sure instructions include enough information for Windows and Mac users to successfully complete procedures.
- Do not add `$` before commands. Make it easy for users to copy and paste commands.
- **Right:** `sudo yum install grafana`
- **Wrong:** `$ sudo yum install grafana`
- Include `sudo` before commands that require `sudo` to work.
For terminal examples and Grafana configuration, use a `bash` code block:
```bash
sudo yum install grafana
```
For HTTP request/response, use an `http` code block:
```http
GET /api/dashboards/id/1/permissions HTTP/1.1
Accept: application/json
@ -263,6 +269,7 @@ Two words if used as a verb, one word if used as a noun.
Two words, not one.
**Exceptions:**
- "datasource" used as an identifier
- "datasource" in a URL
- Use "data source" instead of "datasource" unless used as an identifier, in code, or as part of a URL.
@ -271,7 +278,8 @@ Two words, not one.
#### display (verb)
*Display* is a transitive verb, which means it always needs a direct object.
_Display_ is a transitive verb, which means it always needs a direct object.
- Correct, active voice: Grafana displays your list of active alarms.
- Correct, but passive voice: Your list of active alarms is displayed.
- Incorrect: The list of active alarms displays.
@ -331,6 +339,7 @@ Two words, not one.
**Incorrect:** webserver
### MS SQL Server
Always use "MS SQL" when referring to MS SQL Server application.
Incorrect UI spellings will be corrected in a later version of Grafana.

1
go.mod
View File

@ -46,6 +46,7 @@ require (
github.com/gobwas/glob v0.2.3
github.com/gofrs/uuid v4.0.0+incompatible
github.com/golang/mock v1.5.0
github.com/google/wire v0.5.0
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.6
github.com/google/uuid v1.3.0

4
go.sum
View File

@ -935,12 +935,15 @@ github.com/google/pprof v0.0.0-20210323184331-8eee2492667d/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go v2.0.2+incompatible h1:silFMLAnr330+NRuag/VjIGF7TLp/LBrV2CJKFLWEww=
@ -2504,6 +2507,7 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190425222832-ad9eeb80039a/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=

View File

@ -275,8 +275,7 @@ In a nutshell, the two most important changes are:
- register method is no longer passed as a `ref`, but instead its result is spread onto the input component:
```jsx
- <input ref={register({ required: true })} name="test" />
+ <input {...register('test', { required: true })} />;
-(<input ref={register({ required: true })} name="test" />) + <input {...register('test', { required: true })} />;
```
- `InputControl`'s `as` prop has been replaced with `render`, which has `field` and `fieldState` objects as arguments. `onChange`, `onBlur`, `value`, `name`, and `ref` are parts of `field`.

View File

@ -0,0 +1,44 @@
package apierrors
import (
"errors"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/util"
)
// ToFolderErrorResponse returns a different response status according to the folder error type
func ToFolderErrorResponse(err error) response.Response {
var dashboardErr models.DashboardErr
if ok := errors.As(err, &dashboardErr); ok {
return response.Error(dashboardErr.StatusCode, err.Error(), err)
}
if errors.Is(err, models.ErrFolderTitleEmpty) ||
errors.Is(err, models.ErrDashboardTypeMismatch) ||
errors.Is(err, models.ErrDashboardInvalidUid) ||
errors.Is(err, models.ErrDashboardUidTooLong) ||
errors.Is(err, models.ErrFolderContainsAlertRules) {
return response.Error(400, err.Error(), nil)
}
if errors.Is(err, models.ErrFolderAccessDenied) {
return response.Error(403, "Access denied", err)
}
if errors.Is(err, models.ErrFolderNotFound) {
return response.JSON(404, util.DynMap{"status": "not-found", "message": models.ErrFolderNotFound.Error()})
}
if errors.Is(err, models.ErrFolderSameNameExists) ||
errors.Is(err, models.ErrFolderWithSameUIDExists) {
return response.Error(409, err.Error(), nil)
}
if errors.Is(err, models.ErrFolderVersionMismatch) {
return response.JSON(412, util.DynMap{"status": "version-mismatch", "message": models.ErrFolderVersionMismatch.Error()})
}
return response.Error(500, "Folder API error", err)
}

View File

@ -7,23 +7,23 @@ import (
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
"gopkg.in/macaron.v1"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/fs"
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/accesscontrol"
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/auth/jwt"
"github.com/grafana/grafana/pkg/services/contexthandler"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/rendering"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/require"
"gopkg.in/macaron.v1"
)
func loggedInUserScenario(t *testing.T, desc string, url string, fn scenarioFunc) {
@ -168,35 +168,7 @@ func getContextHandler(t *testing.T, cfg *setting.Cfg) *contexthandler.ContextHa
userAuthTokenSvc := auth.NewFakeUserAuthTokenService()
renderSvc := &fakeRenderService{}
authJWTSvc := models.NewFakeJWTService()
ctxHdlr := &contexthandler.ContextHandler{}
err := registry.BuildServiceGraph([]interface{}{cfg}, []*registry.Descriptor{
{
Name: sqlstore.ServiceName,
Instance: sqlStore,
},
{
Name: remotecache.ServiceName,
Instance: remoteCacheSvc,
},
{
Name: auth.ServiceName,
Instance: userAuthTokenSvc,
},
{
Name: rendering.ServiceName,
Instance: renderSvc,
},
{
Name: jwt.ServiceName,
Instance: authJWTSvc,
},
{
Name: contexthandler.ServiceName,
Instance: ctxHdlr,
},
})
require.NoError(t, err)
ctxHdlr := contexthandler.ProvideService(cfg, userAuthTokenSvc, authJWTSvc, remoteCacheSvc, renderSvc, sqlStore)
return ctxHdlr
}
@ -236,6 +208,9 @@ func setupAccessControlScenarioContext(t *testing.T, cfg *setting.Cfg, url strin
hs := &HTTPServer{
Cfg: cfg,
RouteRegister: routing.NewRouteRegister(),
QuotaService: &quota.QuotaService{
Cfg: cfg,
},
AccessControl: accesscontrolmock.New().WithPermissions(permissions),
}

View File

@ -23,6 +23,7 @@ import (
"github.com/grafana/grafana/pkg/services/live"
"github.com/grafana/grafana/pkg/services/provisioning"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -86,10 +87,8 @@ type testState struct {
}
func newTestLive(t *testing.T) *live.GrafanaLive {
gLive := live.NewGrafanaLive()
gLive.RouteRegister = routing.NewRouteRegister()
gLive.Cfg = &setting.Cfg{AppURL: "http://localhost:3000/"}
err := gLive.Init()
cfg := &setting.Cfg{AppURL: "http://localhost:3000/"}
gLive, err := live.ProvideService(nil, cfg, routing.NewRouteRegister(), nil, nil, nil, nil, sqlstore.InitTestDB(t))
require.NoError(t, err)
return gLive
}

View File

@ -363,7 +363,7 @@ func GetDataSourceIdByName(c *models.ReqContext) response.Response {
// /api/datasources/:id/resources/*
func (hs *HTTPServer) CallDatasourceResource(c *models.ReqContext) {
datasourceID := c.ParamsInt64(":id")
ds, err := hs.DatasourceCache.GetDatasource(datasourceID, c.SignedInUser, c.SkipCache)
ds, err := hs.DataSourceCache.GetDatasource(datasourceID, c.SignedInUser, c.SkipCache)
if err != nil {
if errors.Is(err, models.ErrDataSourceAccessDenied) {
c.JsonApiErr(403, "Access denied to datasource", err)
@ -431,7 +431,7 @@ func convertModelToDtos(ds *models.DataSource) dtos.DataSource {
func (hs *HTTPServer) CheckDatasourceHealth(c *models.ReqContext) response.Response {
datasourceID := c.ParamsInt64("id")
ds, err := hs.DatasourceCache.GetDatasource(datasourceID, c.SignedInUser, c.SkipCache)
ds, err := hs.DataSourceCache.GetDatasource(datasourceID, c.SignedInUser, c.SkipCache)
if err != nil {
if errors.Is(err, models.ErrDataSourceAccessDenied) {
return response.Error(403, "Access denied to datasource", err)

View File

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"github.com/grafana/grafana/pkg/api/apierrors"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/models"
@ -19,7 +20,7 @@ func (hs *HTTPServer) GetFolders(c *models.ReqContext) response.Response {
folders, err := s.GetFolders(c.QueryInt64("limit"), c.QueryInt64("page"))
if err != nil {
return ToFolderErrorResponse(err)
return apierrors.ToFolderErrorResponse(err)
}
result := make([]dtos.FolderSearchHit, 0)
@ -39,7 +40,7 @@ func (hs *HTTPServer) GetFolderByUID(c *models.ReqContext) response.Response {
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser, hs.SQLStore)
folder, err := s.GetFolderByUID(c.Params(":uid"))
if err != nil {
return ToFolderErrorResponse(err)
return apierrors.ToFolderErrorResponse(err)
}
g := guardian.New(folder.Id, c.OrgId, c.SignedInUser)
@ -50,7 +51,7 @@ func (hs *HTTPServer) GetFolderByID(c *models.ReqContext) response.Response {
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser, hs.SQLStore)
folder, err := s.GetFolderByID(c.ParamsInt64(":id"))
if err != nil {
return ToFolderErrorResponse(err)
return apierrors.ToFolderErrorResponse(err)
}
g := guardian.New(folder.Id, c.OrgId, c.SignedInUser)
@ -61,7 +62,7 @@ func (hs *HTTPServer) CreateFolder(c *models.ReqContext, cmd models.CreateFolder
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser, hs.SQLStore)
folder, err := s.CreateFolder(cmd.Title, cmd.Uid)
if err != nil {
return ToFolderErrorResponse(err)
return apierrors.ToFolderErrorResponse(err)
}
if hs.Cfg.EditorsCanAdmin {
@ -79,7 +80,7 @@ func (hs *HTTPServer) UpdateFolder(c *models.ReqContext, cmd models.UpdateFolder
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser, hs.SQLStore)
err := s.UpdateFolder(c.Params(":uid"), &cmd)
if err != nil {
return ToFolderErrorResponse(err)
return apierrors.ToFolderErrorResponse(err)
}
g := guardian.New(cmd.Result.Id, c.OrgId, c.SignedInUser)
@ -93,12 +94,12 @@ func (hs *HTTPServer) DeleteFolder(c *models.ReqContext) response.Response { //
if errors.Is(err, libraryelements.ErrFolderHasConnectedLibraryElements) {
return response.Error(403, "Folder could not be deleted because it contains library elements in use", err)
}
return ToFolderErrorResponse(err)
return apierrors.ToFolderErrorResponse(err)
}
f, err := s.DeleteFolder(c.Params(":uid"), c.QueryBool("forceDeleteRules"))
if err != nil {
return ToFolderErrorResponse(err)
return apierrors.ToFolderErrorResponse(err)
}
return response.JSON(200, util.DynMap{
@ -138,38 +139,3 @@ func toFolderDto(ctx context.Context, g guardian.DashboardGuardian, folder *mode
Version: folder.Version,
}
}
// ToFolderErrorResponse returns a different response status according to the folder error type
func ToFolderErrorResponse(err error) response.Response {
var dashboardErr models.DashboardErr
if ok := errors.As(err, &dashboardErr); ok {
return response.Error(dashboardErr.StatusCode, err.Error(), err)
}
if errors.Is(err, models.ErrFolderTitleEmpty) ||
errors.Is(err, models.ErrDashboardTypeMismatch) ||
errors.Is(err, models.ErrDashboardInvalidUid) ||
errors.Is(err, models.ErrDashboardUidTooLong) ||
errors.Is(err, models.ErrFolderContainsAlertRules) {
return response.Error(400, err.Error(), nil)
}
if errors.Is(err, models.ErrFolderAccessDenied) {
return response.Error(403, "Access denied", err)
}
if errors.Is(err, models.ErrFolderNotFound) {
return response.JSON(404, util.DynMap{"status": "not-found", "message": models.ErrFolderNotFound.Error()})
}
if errors.Is(err, models.ErrFolderSameNameExists) ||
errors.Is(err, models.ErrFolderWithSameUIDExists) {
return response.Error(409, err.Error(), nil)
}
if errors.Is(err, models.ErrFolderVersionMismatch) {
return response.JSON(412, util.DynMap{"status": "version-mismatch", "message": models.ErrFolderVersionMismatch.Error()})
}
return response.Error(500, "Folder API error", err)
}

View File

@ -4,6 +4,7 @@ import (
"errors"
"time"
"github.com/grafana/grafana/pkg/api/apierrors"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/models"
@ -17,13 +18,13 @@ func (hs *HTTPServer) GetFolderPermissionList(c *models.ReqContext) response.Res
folder, err := s.GetFolderByUID(c.Params(":uid"))
if err != nil {
return ToFolderErrorResponse(err)
return apierrors.ToFolderErrorResponse(err)
}
g := guardian.New(folder.Id, c.OrgId, c.SignedInUser)
if canAdmin, err := g.CanAdmin(); err != nil || !canAdmin {
return ToFolderErrorResponse(models.ErrFolderAccessDenied)
return apierrors.ToFolderErrorResponse(models.ErrFolderAccessDenied)
}
acl, err := g.GetAcl()
@ -64,17 +65,17 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext, apiCmd dtos.
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser, hs.SQLStore)
folder, err := s.GetFolderByUID(c.Params(":uid"))
if err != nil {
return ToFolderErrorResponse(err)
return apierrors.ToFolderErrorResponse(err)
}
g := guardian.New(folder.Id, c.OrgId, c.SignedInUser)
canAdmin, err := g.CanAdmin()
if err != nil {
return ToFolderErrorResponse(err)
return apierrors.ToFolderErrorResponse(err)
}
if !canAdmin {
return ToFolderErrorResponse(models.ErrFolderAccessDenied)
return apierrors.ToFolderErrorResponse(models.ErrFolderAccessDenied)
}
var items []*models.DashboardAcl

View File

@ -14,6 +14,10 @@ import (
"sync"
"github.com/grafana/grafana/pkg/login/social"
"github.com/grafana/grafana/pkg/services/cleanup"
"github.com/grafana/grafana/pkg/services/ngalert"
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/grafana/grafana/pkg/services/libraryelements"
"github.com/grafana/grafana/pkg/services/librarypanels"
"github.com/grafana/grafana/pkg/services/oauthtoken"
@ -24,15 +28,16 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/localcache"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/infra/usagestats"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin"
_ "github.com/grafana/grafana/pkg/plugins/backendplugin/manager"
"github.com/grafana/grafana/pkg/plugins/plugincontext"
"github.com/grafana/grafana/pkg/plugins/plugindashboards"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/services/contexthandler"
@ -58,14 +63,6 @@ import (
"gopkg.in/macaron.v1"
)
func init() {
registry.Register(&registry.Descriptor{
Name: "HTTPServer",
Instance: &HTTPServer{},
InitPriority: registry.High,
})
}
type HTTPServer struct {
log log.Logger
macaron *macaron.Macaron
@ -73,49 +70,128 @@ type HTTPServer struct {
httpSrv *http.Server
middlewares []macaron.Handler
PluginContextProvider *plugincontext.Provider `inject:""`
RouteRegister routing.RouteRegister `inject:""`
Bus bus.Bus `inject:""`
RenderService rendering.Service `inject:""`
Cfg *setting.Cfg `inject:""`
SettingsProvider setting.Provider `inject:""`
HooksService *hooks.HooksService `inject:""`
CacheService *localcache.CacheService `inject:""`
DatasourceCache datasources.CacheService `inject:""`
AuthTokenService models.UserTokenService `inject:""`
QuotaService *quota.QuotaService `inject:""`
RemoteCacheService *remotecache.RemoteCache `inject:""`
ProvisioningService provisioning.ProvisioningService `inject:""`
Login login.Service `inject:""`
License models.Licensing `inject:""`
AccessControl accesscontrol.AccessControl `inject:""`
BackendPluginManager backendplugin.Manager `inject:""`
DataProxy *datasourceproxy.DatasourceProxyService `inject:""`
PluginRequestValidator models.PluginRequestValidator `inject:""`
PluginManager plugins.Manager `inject:""`
SearchService *search.SearchService `inject:""`
ShortURLService shorturls.Service `inject:""`
Live *live.GrafanaLive `inject:""`
LivePushGateway *pushhttp.Gateway `inject:""`
ContextHandler *contexthandler.ContextHandler `inject:""`
SQLStore *sqlstore.SQLStore `inject:""`
DataService *tsdb.Service `inject:""`
PluginDashboardService *plugindashboards.Service `inject:""`
AlertEngine *alerting.AlertEngine `inject:""`
LoadSchemaService *schemaloader.SchemaLoaderService `inject:""`
LibraryPanelService librarypanels.Service `inject:""`
LibraryElementService libraryelements.Service `inject:""`
SocialService social.Service `inject:""`
OAuthTokenService *oauthtoken.Service `inject:""`
UsageStatsService usagestats.UsageStats
PluginContextProvider *plugincontext.Provider
RouteRegister routing.RouteRegister
Bus bus.Bus
RenderService rendering.Service
Cfg *setting.Cfg
SettingsProvider setting.Provider
HooksService *hooks.HooksService
CacheService *localcache.CacheService
DataSourceCache datasources.CacheService
AuthTokenService models.UserTokenService
QuotaService *quota.QuotaService
RemoteCacheService *remotecache.RemoteCache
ProvisioningService provisioning.ProvisioningService
Login login.Service
License models.Licensing
AccessControl accesscontrol.AccessControl
BackendPluginManager backendplugin.Manager
DataProxy *datasourceproxy.DataSourceProxyService
PluginRequestValidator models.PluginRequestValidator
PluginManager plugins.Manager
SearchService *search.SearchService
ShortURLService shorturls.Service
Live *live.GrafanaLive
LivePushGateway *pushhttp.Gateway
ContextHandler *contexthandler.ContextHandler
SQLStore *sqlstore.SQLStore
DataService *tsdb.Service
AlertEngine *alerting.AlertEngine
LoadSchemaService *schemaloader.SchemaLoaderService
AlertNG *ngalert.AlertNG
LibraryPanelService librarypanels.Service
LibraryElementService libraryelements.Service
notificationService *notifications.NotificationService
SocialService social.Service
OAuthTokenService oauthtoken.OAuthTokenService
Listener net.Listener
cleanUpService *cleanup.CleanUpService
tracingService *tracing.TracingService
internalMetricsSvc *metrics.InternalMetricsService
}
type ServerOptions struct {
Listener net.Listener
}
func (hs *HTTPServer) Init() error {
hs.log = log.New("http.server")
func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routing.RouteRegister, bus bus.Bus,
renderService rendering.Service, licensing models.Licensing, hooksService *hooks.HooksService,
cacheService *localcache.CacheService, sqlStore *sqlstore.SQLStore,
dataService *tsdb.Service, alertEngine *alerting.AlertEngine,
usageStatsService *usagestats.UsageStatsService, pluginRequestValidator models.PluginRequestValidator,
pluginManager plugins.Manager, backendPM backendplugin.Manager, settingsProvider setting.Provider,
dataSourceCache datasources.CacheService, userTokenService models.UserTokenService,
cleanUpService *cleanup.CleanUpService, shortURLService shorturls.Service,
remoteCache *remotecache.RemoteCache, provisioningService provisioning.ProvisioningService,
loginService login.Service, accessControl accesscontrol.AccessControl,
dataSourceProxy *datasourceproxy.DataSourceProxyService, searchService *search.SearchService,
live *live.GrafanaLive, livePushGateway *pushhttp.Gateway, plugCtxProvider *plugincontext.Provider,
contextHandler *contexthandler.ContextHandler,
schemaService *schemaloader.SchemaLoaderService, alertNG *ngalert.AlertNG,
libraryPanelService librarypanels.Service, libraryElementService libraryelements.Service,
notificationService *notifications.NotificationService, tracingService *tracing.TracingService,
internalMetricsSvc *metrics.InternalMetricsService, quotaService *quota.QuotaService,
socialService social.Service, oauthTokenService oauthtoken.OAuthTokenService) (*HTTPServer, error) {
macaron.Env = cfg.Env
m := macaron.New()
// automatically set HEAD for every GET
m.SetAutoHead(true)
hs.macaron = hs.newMacaron()
hs := &HTTPServer{
Cfg: cfg,
RouteRegister: routeRegister,
Bus: bus,
RenderService: renderService,
License: licensing,
HooksService: hooksService,
CacheService: cacheService,
SQLStore: sqlStore,
DataService: dataService,
AlertEngine: alertEngine,
UsageStatsService: usageStatsService,
PluginRequestValidator: pluginRequestValidator,
PluginManager: pluginManager,
BackendPluginManager: backendPM,
SettingsProvider: settingsProvider,
DataSourceCache: dataSourceCache,
AuthTokenService: userTokenService,
cleanUpService: cleanUpService,
ShortURLService: shortURLService,
RemoteCacheService: remoteCache,
ProvisioningService: provisioningService,
Login: loginService,
AccessControl: accessControl,
DataProxy: dataSourceProxy,
SearchService: searchService,
Live: live,
LivePushGateway: livePushGateway,
PluginContextProvider: plugCtxProvider,
ContextHandler: contextHandler,
LoadSchemaService: schemaService,
AlertNG: alertNG,
LibraryPanelService: libraryPanelService,
LibraryElementService: libraryElementService,
QuotaService: quotaService,
notificationService: notificationService,
tracingService: tracingService,
internalMetricsSvc: internalMetricsSvc,
log: log.New("http.server"),
macaron: m,
Listener: opts.Listener,
SocialService: socialService,
OAuthTokenService: oauthTokenService,
}
if hs.Listener != nil {
hs.log.Debug("Using provided listener")
}
hs.registerRoutes()
return hs.declareFixedRoles()
if err := hs.declareFixedRoles(); err != nil {
return nil, err
}
return hs, nil
}
func (hs *HTTPServer) AddMiddleware(middleware macaron.Handler) {
@ -304,16 +380,6 @@ func (hs *HTTPServer) configureHttp2() error {
return nil
}
func (hs *HTTPServer) newMacaron() *macaron.Macaron {
macaron.Env = hs.Cfg.Env
m := macaron.New()
// automatically set HEAD for every GET
m.SetAutoHead(true)
return m
}
func (hs *HTTPServer) applyRoutes() {
// start with middlewares & static routes
hs.addMiddlewaresAndStaticRoutes()

View File

@ -52,7 +52,7 @@ func (hs *HTTPServer) QueryMetricsV2(c *models.ReqContext, reqDTO dtos.MetricReq
// So only the datasource from the first query is needed. As all requests
// should be the same data source.
if i == 0 {
ds, err = hs.DatasourceCache.GetDatasource(datasourceID, c.SignedInUser, c.SkipCache)
ds, err = hs.DataSourceCache.GetDatasource(datasourceID, c.SignedInUser, c.SkipCache)
if err != nil {
return hs.handleGetDataSourceError(err, datasourceID)
}
@ -122,7 +122,7 @@ func (hs *HTTPServer) handleExpressions(c *models.ReqContext, reqDTO dtos.Metric
if name != expr.DatasourceName {
// Expression requests have everything in one request, so need to check
// all data source queries for possible permission / not found issues.
if _, err = hs.DatasourceCache.GetDatasource(datasourceID, c.SignedInUser, c.SkipCache); err != nil {
if _, err = hs.DataSourceCache.GetDatasource(datasourceID, c.SignedInUser, c.SkipCache); err != nil {
return hs.handleGetDataSourceError(err, datasourceID)
}
}
@ -170,7 +170,7 @@ func (hs *HTTPServer) QueryMetrics(c *models.ReqContext, reqDto dtos.MetricReque
return response.Error(http.StatusBadRequest, "Query missing datasourceId", nil)
}
ds, err := hs.DatasourceCache.GetDatasource(datasourceId, c.SignedInUser, c.SkipCache)
ds, err := hs.DataSourceCache.GetDatasource(datasourceId, c.SignedInUser, c.SkipCache)
if err != nil {
return hs.handleGetDataSourceError(err, datasourceId)
}

View File

@ -4,6 +4,8 @@ import (
"net/http"
"strings"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/setting"
"gopkg.in/macaron.v1"
)
@ -50,13 +52,17 @@ type RouteRegister interface {
type RegisterNamedMiddleware func(name string) macaron.Handler
func ProvideRegister(cfg *setting.Cfg) *RouteRegisterImpl {
return NewRouteRegister(middleware.ProvideRouteOperationName, middleware.RequestMetrics(cfg))
}
// NewRouteRegister creates a new RouteRegister with all middlewares sent as params
func NewRouteRegister(namedMiddleware ...RegisterNamedMiddleware) RouteRegister {
return &routeRegister{
func NewRouteRegister(namedMiddlewares ...RegisterNamedMiddleware) *RouteRegisterImpl {
return &RouteRegisterImpl{
prefix: "",
routes: []route{},
subfixHandlers: []macaron.Handler{},
namedMiddleware: namedMiddleware,
namedMiddlewares: namedMiddlewares,
}
}
@ -66,15 +72,15 @@ type route struct {
handlers []macaron.Handler
}
type routeRegister struct {
type RouteRegisterImpl struct {
prefix string
subfixHandlers []macaron.Handler
namedMiddleware []RegisterNamedMiddleware
namedMiddlewares []RegisterNamedMiddleware
routes []route
groups []*routeRegister
groups []*RouteRegisterImpl
}
func (rr *routeRegister) Reset() {
func (rr *RouteRegisterImpl) Reset() {
if rr == nil {
return
}
@ -84,7 +90,7 @@ func (rr *routeRegister) Reset() {
rr.subfixHandlers = nil
}
func (rr *routeRegister) Insert(pattern string, fn func(RouteRegister), handlers ...macaron.Handler) {
func (rr *RouteRegisterImpl) Insert(pattern string, fn func(RouteRegister), handlers ...macaron.Handler) {
// loop over all groups at current level
for _, g := range rr.groups {
// apply routes if the prefix matches the pattern
@ -100,19 +106,19 @@ func (rr *routeRegister) Insert(pattern string, fn func(RouteRegister), handlers
}
}
func (rr *routeRegister) Group(pattern string, fn func(rr RouteRegister), handlers ...macaron.Handler) {
group := &routeRegister{
func (rr *RouteRegisterImpl) Group(pattern string, fn func(rr RouteRegister), handlers ...macaron.Handler) {
group := &RouteRegisterImpl{
prefix: rr.prefix + pattern,
subfixHandlers: append(rr.subfixHandlers, handlers...),
routes: []route{},
namedMiddleware: rr.namedMiddleware,
namedMiddlewares: rr.namedMiddlewares,
}
fn(group)
rr.groups = append(rr.groups, group)
}
func (rr *routeRegister) Register(router Router) {
func (rr *RouteRegisterImpl) Register(router Router) {
for _, r := range rr.routes {
// GET requests have to be added to macaron routing using Get()
// Otherwise HEAD requests will not be allowed.
@ -129,11 +135,11 @@ func (rr *routeRegister) Register(router Router) {
}
}
func (rr *routeRegister) route(pattern, method string, handlers ...macaron.Handler) {
func (rr *RouteRegisterImpl) route(pattern, method string, handlers ...macaron.Handler) {
h := make([]macaron.Handler, 0)
fullPattern := rr.prefix + pattern
for _, fn := range rr.namedMiddleware {
for _, fn := range rr.namedMiddlewares {
h = append(h, fn(fullPattern))
}
@ -153,26 +159,26 @@ func (rr *routeRegister) route(pattern, method string, handlers ...macaron.Handl
})
}
func (rr *routeRegister) Get(pattern string, handlers ...macaron.Handler) {
func (rr *RouteRegisterImpl) Get(pattern string, handlers ...macaron.Handler) {
rr.route(pattern, http.MethodGet, handlers...)
}
func (rr *routeRegister) Post(pattern string, handlers ...macaron.Handler) {
func (rr *RouteRegisterImpl) Post(pattern string, handlers ...macaron.Handler) {
rr.route(pattern, http.MethodPost, handlers...)
}
func (rr *routeRegister) Delete(pattern string, handlers ...macaron.Handler) {
func (rr *RouteRegisterImpl) Delete(pattern string, handlers ...macaron.Handler) {
rr.route(pattern, http.MethodDelete, handlers...)
}
func (rr *routeRegister) Put(pattern string, handlers ...macaron.Handler) {
func (rr *RouteRegisterImpl) Put(pattern string, handlers ...macaron.Handler) {
rr.route(pattern, http.MethodPut, handlers...)
}
func (rr *routeRegister) Patch(pattern string, handlers ...macaron.Handler) {
func (rr *RouteRegisterImpl) Patch(pattern string, handlers ...macaron.Handler) {
rr.route(pattern, http.MethodPatch, handlers...)
}
func (rr *routeRegister) Any(pattern string, handlers ...macaron.Handler) {
func (rr *RouteRegisterImpl) Any(pattern string, handlers ...macaron.Handler) {
rr.route(pattern, "*", handlers...)
}

View File

@ -47,11 +47,6 @@ type Bus interface {
SetTransactionManager(tm TransactionManager)
}
// InTransaction defines an in transaction function
func (b *InProcBus) InTransaction(ctx context.Context, fn func(ctx context.Context) error) error {
return b.txMng.InTransaction(ctx, fn)
}
// InProcBus defines the bus structure
type InProcBus struct {
logger log.Logger
@ -61,20 +56,27 @@ type InProcBus struct {
txMng TransactionManager
}
func ProvideBus() *InProcBus {
return globalBus
}
// InTransaction defines an in transaction function
func (b *InProcBus) InTransaction(ctx context.Context, fn func(ctx context.Context) error) error {
return b.txMng.InTransaction(ctx, fn)
}
// temp stuff, not sure how to handle bus instance, and init yet
var globalBus = New()
// New initialize the bus
func New() Bus {
bus := &InProcBus{
func New() *InProcBus {
return &InProcBus{
logger: log.New("bus"),
handlers: make(map[string]HandlerFunc),
handlersWithCtx: make(map[string]HandlerFunc),
listeners: make(map[string][]HandlerFunc),
txMng: &noopTransactionManager{},
}
bus.handlers = make(map[string]HandlerFunc)
bus.handlersWithCtx = make(map[string]HandlerFunc)
bus.listeners = make(map[string][]HandlerFunc)
bus.txMng = &noopTransactionManager{}
return bus
}
// Want to get rid of global bus
@ -234,7 +236,7 @@ func Publish(msg Msg) error {
}
func GetHandlerCtx(name string) HandlerFunc {
return globalBus.(*InProcBus).GetHandlerCtx(name)
return globalBus.GetHandlerCtx(name)
}
func ClearBusHandlers() {

View File

@ -4,13 +4,13 @@ import (
"strings"
"github.com/fatih/color"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/commands/datamigrations"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/logger"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/services"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/migrations"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util/errutil"
"github.com/urfave/cli/v2"
@ -21,14 +21,13 @@ func runDbCommand(command func(commandLine utils.CommandLine, sqlStore *sqlstore
cmd := &utils.ContextCommandLine{Context: context}
debug := cmd.Bool("debug")
cfg := setting.NewCfg()
configOptions := strings.Split(cmd.String("configOverrides"), " ")
if err := cfg.Load(&setting.CommandLineArgs{
cfg, err := setting.NewCfgFromArgs(setting.CommandLineArgs{
Config: cmd.ConfigFile(),
HomePath: cmd.HomePath(),
Args: append(configOptions, cmd.Args().Slice()...), // tailing arguments have precedence over the options string
}); err != nil {
})
if err != nil {
return errutil.Wrap("failed to load configuration", err)
}
@ -36,14 +35,12 @@ func runDbCommand(command func(commandLine utils.CommandLine, sqlStore *sqlstore
cfg.LogConfigSources()
}
engine := &sqlstore.SQLStore{}
engine.Cfg = cfg
engine.Bus = bus.GetBus()
if err := engine.Init(); err != nil {
return errutil.Wrap("failed to initialize SQL engine", err)
sqlStore, err := sqlstore.ProvideService(cfg, nil, bus.GetBus(), &migrations.OSSMigrations{})
if err != nil {
return errutil.Wrap("failed to initialize SQL store", err)
}
if err := command(cmd, engine); err != nil {
if err := command(cmd, sqlStore); err != nil {
return err
}

View File

@ -16,6 +16,7 @@ import (
"syscall"
"time"
"github.com/grafana/grafana/pkg/api"
"github.com/grafana/grafana/pkg/extensions"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics"
@ -23,19 +24,6 @@ import (
_ "github.com/grafana/grafana/pkg/services/alerting/conditions"
_ "github.com/grafana/grafana/pkg/services/alerting/notifiers"
"github.com/grafana/grafana/pkg/setting"
_ "github.com/grafana/grafana/pkg/tsdb/azuremonitor"
_ "github.com/grafana/grafana/pkg/tsdb/cloudmonitoring"
_ "github.com/grafana/grafana/pkg/tsdb/cloudwatch"
_ "github.com/grafana/grafana/pkg/tsdb/elasticsearch"
_ "github.com/grafana/grafana/pkg/tsdb/graphite"
_ "github.com/grafana/grafana/pkg/tsdb/influxdb"
_ "github.com/grafana/grafana/pkg/tsdb/loki"
_ "github.com/grafana/grafana/pkg/tsdb/mysql"
_ "github.com/grafana/grafana/pkg/tsdb/opentsdb"
_ "github.com/grafana/grafana/pkg/tsdb/postgres"
_ "github.com/grafana/grafana/pkg/tsdb/prometheus"
_ "github.com/grafana/grafana/pkg/tsdb/tempo"
_ "github.com/grafana/grafana/pkg/tsdb/testdatasource"
)
// The following variables cannot be constants, since they can be overridden through the -X link flag
@ -160,11 +148,13 @@ func executeServer(configFile, homePath, pidFile, packaging string, traceDiagnos
metrics.SetBuildInformation(version, commit, buildBranch)
s, err := server.New(server.Config{
ConfigFile: configFile, HomePath: homePath, PidFile: pidFile,
Version: version, Commit: commit, BuildBranch: buildBranch,
})
s, err := server.Initialize(setting.CommandLineArgs{
Config: configFile, HomePath: homePath, Args: flag.Args(),
}, server.Options{
PidFile: pidFile, Version: version, Commit: commit, BuildBranch: buildBranch,
}, api.ServerOptions{})
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to start grafana. error: %s\n", err.Error())
return err
}

View File

@ -12,7 +12,8 @@ func TestUploadToAzureBlob(t *testing.T) {
t.Run("[Integration test] for external_image_store.azure_blob", func(t *testing.T) {
t.Skip("Skipping testing for external_image_store.azure_blob")
cfg := setting.NewCfg()
err := cfg.Load(&setting.CommandLineArgs{
err := cfg.Load(setting.CommandLineArgs{
HomePath: "../../../",
})
require.NoError(t, err)

View File

@ -12,7 +12,7 @@ func TestImageUploaderFactory(t *testing.T) {
t.Run("Can create image uploader for ", func(t *testing.T) {
t.Run("S3ImageUploader config", func(t *testing.T) {
cfg := setting.NewCfg()
err := cfg.Load(&setting.CommandLineArgs{
err := cfg.Load(setting.CommandLineArgs{
HomePath: "../../../",
})
require.NoError(t, err)
@ -85,7 +85,7 @@ func TestImageUploaderFactory(t *testing.T) {
t.Run("Webdav uploader", func(t *testing.T) {
cfg := setting.NewCfg()
err := cfg.Load(&setting.CommandLineArgs{
err := cfg.Load(setting.CommandLineArgs{
HomePath: "../../../",
})
require.NoError(t, err)
@ -113,7 +113,7 @@ func TestImageUploaderFactory(t *testing.T) {
t.Run("GCS uploader", func(t *testing.T) {
cfg := setting.NewCfg()
err := cfg.Load(&setting.CommandLineArgs{
err := cfg.Load(setting.CommandLineArgs{
HomePath: "../../../",
})
require.NoError(t, err)
@ -138,7 +138,7 @@ func TestImageUploaderFactory(t *testing.T) {
t.Run("AzureBlobUploader config", func(t *testing.T) {
cfg := setting.NewCfg()
err := cfg.Load(&setting.CommandLineArgs{
err := cfg.Load(setting.CommandLineArgs{
HomePath: "../../../",
})
require.NoError(t, err)
@ -168,7 +168,7 @@ func TestImageUploaderFactory(t *testing.T) {
t.Run("Local uploader", func(t *testing.T) {
cfg := setting.NewCfg()
err := cfg.Load(&setting.CommandLineArgs{
err := cfg.Load(setting.CommandLineArgs{
HomePath: "../../../",
})
require.NoError(t, err)

View File

@ -12,7 +12,7 @@ func TestUploadToS3(t *testing.T) {
t.Run("[Integration test] for external_image_store.s3", func(t *testing.T) {
t.Skip("Skip test [Integration test] for external_image_store.s3")
cfg := setting.NewCfg()
err := cfg.Load(&setting.CommandLineArgs{
err := cfg.Load(setting.CommandLineArgs{
HomePath: "../../../",
})
require.NoError(t, err)

View File

@ -4,8 +4,8 @@ import (
"context"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/tsdb"
)
// DatasourceName is the string constant used as the datasource name in requests
@ -23,7 +23,7 @@ const DatasourceUID = "-100"
// Service is service representation for expression handling.
type Service struct {
Cfg *setting.Cfg
DataService *tsdb.Service
DataService plugins.DataRequestHandler
}
func (s *Service) isDisabled() bool {

View File

@ -13,9 +13,6 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin"
"github.com/grafana/grafana/pkg/plugins/manager"
"github.com/grafana/grafana/pkg/tsdb"
"github.com/stretchr/testify/require"
)
@ -25,17 +22,10 @@ func TestService(t *testing.T) {
data.NewField("time", nil, []time.Time{time.Unix(1, 0)}),
data.NewField("value", nil, []*float64{fp(2)}))
dataSvc := tsdb.NewService()
dataSvc.PluginManager = &manager.PluginManager{
BackendPluginManager: fakeBackendPM{},
}
s := Service{DataService: &dataSvc}
me := &mockEndpoint{
Frames: []*data.Frame{dsDF},
}
s.DataService.RegisterQueryHandler("test", func(*models.DataSource) (plugins.DataPlugin, error) {
return me, nil
})
s := Service{DataService: me}
bus.AddHandler("test", func(query *models.GetDataSourceQuery) error {
query.Result = &models.DataSource{Id: 1, OrgId: 1, Type: "test"}
return nil
@ -98,9 +88,8 @@ type mockEndpoint struct {
Frames data.Frames
}
// nolint:staticcheck // plugins.DataQueryResult deprecated
func (me *mockEndpoint) DataQuery(ctx context.Context, ds *models.DataSource, query plugins.DataQuery) (
plugins.DataResponse, error) {
// nolint:staticcheck // plugins.DataQueryResponse deprecated
func (me *mockEndpoint) DataQuery(ctx context.Context, ds *models.DataSource, query plugins.DataQuery) (plugins.DataResponse, error) {
return plugins.DataResponse{
Results: map[string]plugins.DataQueryResult{
"A": {
@ -110,6 +99,7 @@ func (me *mockEndpoint) DataQuery(ctx context.Context, ds *models.DataSource, qu
}, nil
}
type fakeBackendPM struct {
backendplugin.Manager
// nolint:staticcheck // plugins.DataQueryResponse deprecated
func (me *mockEndpoint) HandleRequest(ctx context.Context, ds *models.DataSource, query plugins.DataQuery) (plugins.DataResponse, error) {
return me.DataQuery(ctx, ds, query)
}

View File

@ -8,11 +8,6 @@ import (
_ "github.com/cortexproject/cortex/pkg/util"
_ "github.com/crewjam/saml"
_ "github.com/gobwas/glob"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol"
"github.com/grafana/grafana/pkg/services/licensing"
"github.com/grafana/grafana/pkg/services/validations"
"github.com/grafana/grafana/pkg/setting"
_ "github.com/grafana/loki/clients/pkg/promtail/client"
_ "github.com/grafana/loki/pkg/logproto"
_ "github.com/grpc-ecosystem/go-grpc-middleware"
@ -29,11 +24,4 @@ import (
_ "gopkg.in/square/go-jose.v2"
)
func init() {
registry.RegisterService(&licensing.OSSLicensingService{})
registry.RegisterService(&validations.OSSPluginRequestValidator{})
registry.RegisterService(&ossaccesscontrol.OSSAccessControlService{})
registry.RegisterService(&setting.OSSImpl{})
}
var IsEnterprise bool = false

View File

@ -6,7 +6,6 @@ import (
"time"
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics/metricutil"
"github.com/grafana/grafana/pkg/setting"
@ -16,7 +15,7 @@ import (
var newProviderFunc = sdkhttpclient.NewProvider
// New creates a new HTTP client provider with pre-configured middlewares.
func New(cfg *setting.Cfg) httpclient.Provider {
func New(cfg *setting.Cfg) *sdkhttpclient.Provider {
logger := log.New("httpclient")
userAgent := fmt.Sprintf("Grafana/%s", cfg.BuildVersion)

View File

@ -11,6 +11,10 @@ type CacheService struct {
*gocache.Cache
}
func ProvideService() *CacheService {
return New(5*time.Minute, 10*time.Minute)
}
// New returns a new CacheService
func New(defaultExpiration, cleanupInterval time.Duration) *CacheService {
return &CacheService{

View File

@ -5,7 +5,6 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics/graphitebridge"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/setting"
)
@ -20,22 +19,24 @@ func (lw *logWrapper) Println(v ...interface{}) {
}
func init() {
registry.RegisterService(&InternalMetricsService{})
initMetricVars()
initFrontendMetrics()
}
func ProvideService(cfg *setting.Cfg) (*InternalMetricsService, error) {
s := &InternalMetricsService{
Cfg: cfg,
}
return s, s.readSettings()
}
type InternalMetricsService struct {
Cfg *setting.Cfg `inject:""`
Cfg *setting.Cfg
intervalSeconds int64
graphiteCfg *graphitebridge.Config
}
func (im *InternalMetricsService) Init() error {
return im.readSettings()
}
func (im *InternalMetricsService) Run(ctx context.Context) error {
// Start Graphite Bridge
if im.graphiteCfg != nil {

View File

@ -27,13 +27,18 @@ const (
ServiceName = "RemoteCache"
)
func init() {
rc := &RemoteCache{}
registry.Register(&registry.Descriptor{
Name: ServiceName,
Instance: rc,
InitPriority: registry.Medium,
})
func ProvideService(cfg *setting.Cfg, sqlStore *sqlstore.SQLStore) (*RemoteCache, error) {
client, err := createClient(cfg.RemoteCacheOptions, sqlStore)
if err != nil {
return nil, err
}
s := &RemoteCache{
SQLStore: sqlStore,
Cfg: cfg,
log: log.New("cache.remote"),
client: client,
}
return s, nil
}
// CacheStorage allows the caller to set, get and delete items in the cache.
@ -55,8 +60,8 @@ type CacheStorage interface {
type RemoteCache struct {
log log.Logger
client CacheStorage
SQLStore *sqlstore.SQLStore `inject:""`
Cfg *setting.Cfg `inject:""`
SQLStore *sqlstore.SQLStore
Cfg *setting.Cfg
}
// Get reads object from Cache
@ -78,15 +83,7 @@ func (ds *RemoteCache) Delete(key string) error {
return ds.client.Delete(key)
}
// Init initializes the service
func (ds *RemoteCache) Init() error {
ds.log = log.New("cache.remote")
var err error
ds.client, err = createClient(ds.Cfg.RemoteCacheOptions, ds.SQLStore)
return err
}
// Run start the backend processes for cache clients
// Run starts the backend processes for cache clients.
func (ds *RemoteCache) Run(ctx context.Context) error {
// create new interface if more clients need GC jobs
backgroundjob, ok := ds.client.(registry.BackgroundService)

View File

@ -22,14 +22,10 @@ func init() {
func createTestClient(t *testing.T, opts *setting.RemoteCacheOptions, sqlstore *sqlstore.SQLStore) CacheStorage {
t.Helper()
dc := &RemoteCache{
SQLStore: sqlstore,
Cfg: &setting.Cfg{
cfg := &setting.Cfg{
RemoteCacheOptions: opts,
},
}
err := dc.Init()
dc, err := ProvideService(cfg, sqlstore)
require.Nil(t, err, "Failed to init client for test")
return dc
@ -37,7 +33,7 @@ func createTestClient(t *testing.T, opts *setting.RemoteCacheOptions, sqlstore *
func TestCachedBasedOnConfig(t *testing.T) {
cfg := setting.NewCfg()
err := cfg.Load(&setting.CommandLineArgs{
err := cfg.Load(setting.CommandLineArgs{
HomePath: "../../../",
})
require.Nil(t, err, "Failed to load config")

View File

@ -17,16 +17,11 @@ func NewFakeStore(t *testing.T) *RemoteCache {
ConnStr: "",
}
SQLStore := sqlstore.InitTestDB(t)
sqlStore := sqlstore.InitTestDB(t)
dc := &RemoteCache{
SQLStore: SQLStore,
Cfg: &setting.Cfg{
dc, err := ProvideService(&setting.Cfg{
RemoteCacheOptions: opts,
},
}
err := dc.Init()
}, sqlStore)
require.NoError(t, err, "Failed to init remote cache for test")
return dc

View File

@ -5,27 +5,23 @@ import (
"time"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/sqlstore"
)
func init() {
registry.RegisterService(&ServerLockService{})
func ProvideService(sqlStore *sqlstore.SQLStore) *ServerLockService {
return &ServerLockService{
SQLStore: sqlStore,
log: log.New("infra.lockservice"),
}
}
// ServerLockService allows servers in HA mode to claim a lock
// and execute an function if the server was granted the lock
type ServerLockService struct {
SQLStore *sqlstore.SQLStore `inject:""`
SQLStore *sqlstore.SQLStore
log log.Logger
}
// Init this service
func (sl *ServerLockService) Init() error {
sl.log = log.New("infra.lockservice")
return nil
}
// LockAndExecute try to create a lock for this server and only executes the
// `fn` function when successful. This should not be used at low internal. But services
// that needs to be run once every ex 10m.

View File

@ -8,7 +8,6 @@ import (
"strings"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/setting"
opentracing "github.com/opentracing/opentracing-go"
@ -21,8 +20,20 @@ const (
envJaegerAgentPort = "JAEGER_AGENT_PORT"
)
func init() {
registry.RegisterService(&TracingService{})
func ProvideService(cfg *setting.Cfg) (*TracingService, error) {
ts := &TracingService{
Cfg: cfg,
log: log.New("tracing"),
}
if err := ts.parseSettings(); err != nil {
return nil, err
}
if ts.enabled {
return ts, ts.initGlobalTracer()
}
return ts, nil
}
type TracingService struct {
@ -37,20 +48,7 @@ type TracingService struct {
zipkinPropagation bool
disableSharedZipkinSpans bool
Cfg *setting.Cfg `inject:""`
}
func (ts *TracingService) Init() error {
ts.log = log.New("tracing")
if err := ts.parseSettings(); err != nil {
return err
}
if ts.enabled {
return ts.initGlobalTracer()
}
return nil
Cfg *setting.Cfg
}
func (ts *TracingService) parseSettings() error {

View File

@ -107,15 +107,16 @@ func TestInitJaegerCfg_EnabledViaHost(t *testing.T) {
require.NoError(t, os.Unsetenv("JAEGER_AGENT_HOST"))
}()
ts := &TracingService{Cfg: setting.NewCfg()}
cfg := setting.NewCfg()
ts := &TracingService{Cfg: cfg}
_, err := ts.Cfg.Raw.NewSection("tracing.jaeger")
require.NoError(t, err)
require.NoError(t, ts.parseSettings())
cfg, err := ts.initJaegerCfg()
jaegerCfg, err := ts.initJaegerCfg()
require.NoError(t, err)
assert.False(t, cfg.Disabled)
assert.Equal(t, "example.com:6831", cfg.Reporter.LocalAgentHostPort)
assert.False(t, jaegerCfg.Disabled)
assert.Equal(t, "example.com:6831", jaegerCfg.Reporter.LocalAgentHostPort)
}
func TestInitJaegerCfg_EnabledViaHostPort(t *testing.T) {
@ -126,13 +127,14 @@ func TestInitJaegerCfg_EnabledViaHostPort(t *testing.T) {
require.NoError(t, os.Unsetenv("JAEGER_AGENT_PORT"))
}()
ts := &TracingService{Cfg: setting.NewCfg()}
cfg := setting.NewCfg()
ts := &TracingService{Cfg: cfg}
_, err := ts.Cfg.Raw.NewSection("tracing.jaeger")
require.NoError(t, err)
require.NoError(t, ts.parseSettings())
cfg, err := ts.initJaegerCfg()
jaegerCfg, err := ts.initJaegerCfg()
require.NoError(t, err)
assert.False(t, cfg.Disabled)
assert.Equal(t, "example.com:12345", cfg.Reporter.LocalAgentHostPort)
assert.False(t, jaegerCfg.Disabled)
assert.Equal(t, "example.com:12345", jaegerCfg.Reporter.LocalAgentHostPort)
}

View File

@ -9,20 +9,12 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/login/social"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
)
var metricsLogger = log.New("metrics")
func init() {
registry.RegisterService(&UsageStatsService{
log: log.New("infra.usagestats"),
externalMetrics: make([]MetricsFunc, 0),
})
}
var metricsLogger log.Logger = log.New("metrics")
type UsageStats interface {
GetUsageReport(context.Context) (UsageReport, error)
@ -33,12 +25,12 @@ type UsageStats interface {
type MetricsFunc func() (map[string]interface{}, error)
type UsageStatsService struct {
Cfg *setting.Cfg `inject:""`
Bus bus.Bus `inject:""`
SQLStore *sqlstore.SQLStore `inject:""`
AlertingUsageStats alerting.UsageStatsQuerier `inject:""`
PluginManager plugins.Manager `inject:""`
SocialService social.Service `inject:""`
Cfg *setting.Cfg
Bus bus.Bus
SQLStore *sqlstore.SQLStore
AlertingUsageStats alerting.UsageStatsQuerier
PluginManager plugins.Manager
SocialService social.Service
log log.Logger
@ -47,9 +39,19 @@ type UsageStatsService struct {
concurrentUserStatsCache memoConcurrentUserStats
}
func (uss *UsageStatsService) Init() error {
uss.oauthProviders = uss.SocialService.GetOAuthProviders()
return nil
func ProvideService(cfg *setting.Cfg, bus bus.Bus, sqlStore *sqlstore.SQLStore,
alertingStats alerting.UsageStatsQuerier, pluginManager plugins.Manager,
socialService social.Service) *UsageStatsService {
s := &UsageStatsService{
Cfg: cfg,
Bus: bus,
SQLStore: sqlStore,
AlertingUsageStats: alertingStats,
oauthProviders: socialService.GetOAuthProviders(),
PluginManager: pluginManager,
log: log.New("infra.usagestats"),
}
return s
}
func (uss *UsageStatsService) Run(ctx context.Context) error {

View File

@ -13,7 +13,6 @@ import (
"golang.org/x/oauth2"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
@ -22,12 +21,8 @@ var (
logger = log.New("social")
)
func init() {
registry.RegisterService(&SocialService{})
}
type SocialService struct {
Cfg *setting.Cfg `inject:""`
cfg *setting.Cfg
socialMap map[string]SocialConnector
oAuthProvider map[string]*OAuthInfo
@ -54,12 +49,15 @@ type OAuthInfo struct {
TlsSkipVerify bool
}
func (ss *SocialService) Init() error {
ss.oAuthProvider = make(map[string]*OAuthInfo)
ss.socialMap = make(map[string]SocialConnector)
func ProvideService(cfg *setting.Cfg) *SocialService {
ss := SocialService{
cfg: cfg,
oAuthProvider: make(map[string]*OAuthInfo),
socialMap: make(map[string]SocialConnector),
}
for _, name := range allOauthes {
sec := ss.Cfg.Raw.Section("auth." + name)
sec := cfg.Raw.Section("auth." + name)
info := &OAuthInfo{
ClientId: sec.Key("client_id").String(),
@ -107,7 +105,7 @@ func (ss *SocialService) Init() error {
TokenURL: info.TokenUrl,
AuthStyle: oauth2.AuthStyleAutoDetect,
},
RedirectURL: strings.TrimSuffix(ss.Cfg.AppURL, "/") + SocialBaseUrl + name,
RedirectURL: strings.TrimSuffix(cfg.AppURL, "/") + SocialBaseUrl + name,
Scopes: info.Scopes,
}
@ -144,7 +142,7 @@ func (ss *SocialService) Init() error {
ss.socialMap["azuread"] = &SocialAzureAD{
SocialBase: newSocialBase(name, &config, info),
allowedGroups: util.SplitString(sec.Key("allowed_groups").String()),
autoAssignOrgRole: ss.Cfg.AutoAssignOrgRole,
autoAssignOrgRole: cfg.AutoAssignOrgRole,
}
}
@ -182,22 +180,22 @@ func (ss *SocialService) Init() error {
ClientID: info.ClientId,
ClientSecret: info.ClientSecret,
Endpoint: oauth2.Endpoint{
AuthURL: ss.Cfg.GrafanaComURL + "/oauth2/authorize",
TokenURL: ss.Cfg.GrafanaComURL + "/api/oauth2/token",
AuthURL: cfg.GrafanaComURL + "/oauth2/authorize",
TokenURL: cfg.GrafanaComURL + "/api/oauth2/token",
AuthStyle: oauth2.AuthStyleInHeader,
},
RedirectURL: strings.TrimSuffix(ss.Cfg.AppURL, "/") + SocialBaseUrl + name,
RedirectURL: strings.TrimSuffix(cfg.AppURL, "/") + SocialBaseUrl + name,
Scopes: info.Scopes,
}
ss.socialMap[grafanaCom] = &SocialGrafanaCom{
SocialBase: newSocialBase(name, &config, info),
url: ss.Cfg.GrafanaComURL,
url: cfg.GrafanaComURL,
allowedOrganizations: util.SplitString(sec.Key("allowed_organizations").String()),
}
}
}
return nil
return &ss
}
type BasicUserInfo struct {
@ -270,7 +268,7 @@ func newSocialBase(name string, config *oauth2.Config, info *OAuthInfo) *SocialB
func (ss *SocialService) GetOAuthProviders() map[string]bool {
result := map[string]bool{}
if ss.Cfg == nil || ss.Cfg.Raw == nil {
if ss.cfg == nil || ss.cfg.Raw == nil {
return result
}
@ -279,7 +277,7 @@ func (ss *SocialService) GetOAuthProviders() map[string]bool {
name = grafanaCom
}
sec := ss.Cfg.Raw.Section("auth." + name)
sec := ss.cfg.Raw.Section("auth." + name)
if sec == nil {
continue
}

View File

@ -23,9 +23,7 @@ import (
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/login"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/auth/jwt"
"github.com/grafana/grafana/pkg/services/contexthandler"
"github.com/grafana/grafana/pkg/services/contexthandler/authproxy"
"github.com/grafana/grafana/pkg/services/rendering"
@ -669,47 +667,18 @@ func getContextHandler(t *testing.T, cfg *setting.Cfg) *contexthandler.ContextHa
t.Helper()
sqlStore := sqlstore.InitTestDB(t)
remoteCacheSvc := &remotecache.RemoteCache{}
if cfg == nil {
cfg = setting.NewCfg()
}
cfg.RemoteCacheOptions = &setting.RemoteCacheOptions{
Name: "database",
}
remoteCacheSvc, err := remotecache.ProvideService(cfg, sqlStore)
require.NoError(t, err)
userAuthTokenSvc := auth.NewFakeUserAuthTokenService()
renderSvc := &fakeRenderService{}
authJWTSvc := models.NewFakeJWTService()
ctxHdlr := &contexthandler.ContextHandler{}
err := registry.BuildServiceGraph([]interface{}{cfg}, []*registry.Descriptor{
{
Name: sqlstore.ServiceName,
Instance: sqlStore,
},
{
Name: remotecache.ServiceName,
Instance: remoteCacheSvc,
},
{
Name: auth.ServiceName,
Instance: userAuthTokenSvc,
},
{
Name: rendering.ServiceName,
Instance: renderSvc,
},
{
Name: jwt.ServiceName,
Instance: authJWTSvc,
},
{
Name: contexthandler.ServiceName,
Instance: ctxHdlr,
},
})
require.NoError(t, err)
return ctxHdlr
return contexthandler.ProvideService(cfg, userAuthTokenSvc, authJWTSvc, remoteCacheSvc, renderSvc, sqlStore)
}
type fakeRenderService struct {

View File

@ -11,6 +11,9 @@ import (
// Quota returns a function that returns a function used to call quotaservice based on target name
func Quota(quotaService *quota.QuotaService) func(string) macaron.Handler {
if quotaService == nil {
panic("quotaService is nil")
}
//https://open.spotify.com/track/7bZSoBEAEEUsGEuLOf94Jm?si=T1Tdju5qRSmmR0zph_6RBw fuuuuunky
return func(target string) macaron.Handler {
return func(c *models.ReqContext) {

View File

@ -44,7 +44,7 @@ func init() {
prometheus.MustRegister(httpRequestsInFlight, httpRequestDurationHistogram)
}
// RequestMetrics is a middleware handler that instruments the request
// RequestMetrics is a middleware handler that instruments the request.
func RequestMetrics(cfg *setting.Cfg) func(handler string) macaron.Handler {
return func(handler string) macaron.Handler {
return func(res http.ResponseWriter, req *http.Request, c *macaron.Context) {

View File

@ -18,10 +18,6 @@ func (s *FakeJWTService) Verify(ctx context.Context, token string) (JWTClaims, e
return s.VerifyProvider(ctx, token)
}
func (s *FakeJWTService) Init() error {
return nil
}
func NewFakeJWTService() *FakeJWTService {
return &FakeJWTService{
VerifyProvider: func(ctx context.Context, token string) (JWTClaims, error) {

View File

@ -4,6 +4,8 @@ import (
"context"
"errors"
"net"
"github.com/grafana/grafana/pkg/registry"
)
// Typed errors
@ -73,3 +75,7 @@ type UserTokenService interface {
GetUserTokens(ctx context.Context, userId int64) ([]*UserToken, error)
GetUserRevokedTokens(ctx context.Context, userId int64) ([]*UserToken, error)
}
type UserTokenBackgroundService interface {
registry.BackgroundService
}

View File

@ -27,7 +27,7 @@ type Manager interface {
// QueryData query data from a registered backend plugin.
QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error)
// CallResource calls a plugin resource.
CallResource(pluginConfig backend.PluginContext, ctx *models.ReqContext, path string)
CallResource(pCtx backend.PluginContext, reqCtx *models.ReqContext, path string)
// Get plugin by its ID.
Get(pluginID string) (Plugin, bool)
}

View File

@ -19,40 +19,40 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins/backendplugin"
"github.com/grafana/grafana/pkg/plugins/backendplugin/instrumentation"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util/errutil"
"github.com/grafana/grafana/pkg/util/proxyutil"
)
func init() {
registry.RegisterServiceWithPriority(&manager{
func ProvideService(cfg *setting.Cfg, licensing models.Licensing,
pluginRequestValidator models.PluginRequestValidator) *Manager {
s := &Manager{
Cfg: cfg,
License: licensing,
PluginRequestValidator: pluginRequestValidator,
logger: log.New("plugins.backend"),
plugins: map[string]backendplugin.Plugin{},
}, registry.MediumHigh)
}
return s
}
type manager struct {
Cfg *setting.Cfg `inject:""`
License models.Licensing `inject:""`
PluginRequestValidator models.PluginRequestValidator `inject:""`
type Manager struct {
Cfg *setting.Cfg
License models.Licensing
PluginRequestValidator models.PluginRequestValidator
pluginsMu sync.RWMutex
plugins map[string]backendplugin.Plugin
logger log.Logger
}
func (m *manager) Init() error {
return nil
}
func (m *manager) Run(ctx context.Context) error {
func (m *Manager) Run(ctx context.Context) error {
<-ctx.Done()
m.stop(ctx)
return ctx.Err()
}
// Register registers a backend plugin
func (m *manager) Register(pluginID string, factory backendplugin.PluginFactoryFunc) error {
func (m *Manager) Register(pluginID string, factory backendplugin.PluginFactoryFunc) error {
m.logger.Debug("Registering backend plugin", "pluginId", pluginID)
m.pluginsMu.Lock()
defer m.pluginsMu.Unlock()
@ -97,7 +97,7 @@ func (m *manager) Register(pluginID string, factory backendplugin.PluginFactoryF
}
// RegisterAndStart registers and starts a backend plugin
func (m *manager) RegisterAndStart(ctx context.Context, pluginID string, factory backendplugin.PluginFactoryFunc) error {
func (m *Manager) RegisterAndStart(ctx context.Context, pluginID string, factory backendplugin.PluginFactoryFunc) error {
err := m.Register(pluginID, factory)
if err != nil {
return err
@ -114,7 +114,7 @@ func (m *manager) RegisterAndStart(ctx context.Context, pluginID string, factory
}
// UnregisterAndStop unregisters and stops a backend plugin
func (m *manager) UnregisterAndStop(ctx context.Context, pluginID string) error {
func (m *Manager) UnregisterAndStop(ctx context.Context, pluginID string) error {
m.logger.Debug("Unregistering backend plugin", "pluginId", pluginID)
m.pluginsMu.Lock()
defer m.pluginsMu.Unlock()
@ -139,13 +139,13 @@ func (m *manager) UnregisterAndStop(ctx context.Context, pluginID string) error
return nil
}
func (m *manager) IsRegistered(pluginID string) bool {
func (m *Manager) IsRegistered(pluginID string) bool {
p, _ := m.Get(pluginID)
return p != nil && !p.IsDecommissioned()
}
func (m *manager) Get(pluginID string) (backendplugin.Plugin, bool) {
func (m *Manager) Get(pluginID string) (backendplugin.Plugin, bool) {
m.pluginsMu.RLock()
p, ok := m.plugins[pluginID]
m.pluginsMu.RUnlock()
@ -157,7 +157,7 @@ func (m *manager) Get(pluginID string) (backendplugin.Plugin, bool) {
return p, ok
}
func (m *manager) getAWSEnvironmentVariables() []string {
func (m *Manager) getAWSEnvironmentVariables() []string {
variables := []string{}
if m.Cfg.AWSAssumeRoleEnabled {
variables = append(variables, awsds.AssumeRoleEnabledEnvVarKeyName+"=true")
@ -169,7 +169,7 @@ func (m *manager) getAWSEnvironmentVariables() []string {
return variables
}
func (m *manager) getAzureEnvironmentVariables() []string {
func (m *Manager) getAzureEnvironmentVariables() []string {
variables := []string{}
if m.Cfg.Azure.Cloud != "" {
variables = append(variables, "AZURE_CLOUD="+m.Cfg.Azure.Cloud)
@ -185,7 +185,7 @@ func (m *manager) getAzureEnvironmentVariables() []string {
}
// start starts a managed backend plugin
func (m *manager) start(ctx context.Context, p backendplugin.Plugin) {
func (m *Manager) start(ctx context.Context, p backendplugin.Plugin) {
if !p.IsManaged() {
return
}
@ -196,7 +196,7 @@ func (m *manager) start(ctx context.Context, p backendplugin.Plugin) {
}
// StartPlugin starts a non-managed backend plugin
func (m *manager) StartPlugin(ctx context.Context, pluginID string) error {
func (m *Manager) StartPlugin(ctx context.Context, pluginID string) error {
m.pluginsMu.RLock()
p, registered := m.plugins[pluginID]
m.pluginsMu.RUnlock()
@ -212,7 +212,7 @@ func (m *manager) StartPlugin(ctx context.Context, pluginID string) error {
}
// stop stops all managed backend plugins
func (m *manager) stop(ctx context.Context) {
func (m *Manager) stop(ctx context.Context) {
m.pluginsMu.RLock()
defer m.pluginsMu.RUnlock()
var wg sync.WaitGroup
@ -231,7 +231,7 @@ func (m *manager) stop(ctx context.Context) {
}
// CollectMetrics collects metrics from a registered backend plugin.
func (m *manager) CollectMetrics(ctx context.Context, pluginID string) (*backend.CollectMetricsResult, error) {
func (m *Manager) CollectMetrics(ctx context.Context, pluginID string) (*backend.CollectMetricsResult, error) {
p, registered := m.Get(pluginID)
if !registered {
return nil, backendplugin.ErrPluginNotRegistered
@ -250,7 +250,7 @@ func (m *manager) CollectMetrics(ctx context.Context, pluginID string) (*backend
}
// CheckHealth checks the health of a registered backend plugin.
func (m *manager) CheckHealth(ctx context.Context, pluginContext backend.PluginContext) (*backend.CheckHealthResult, error) {
func (m *Manager) CheckHealth(ctx context.Context, pluginContext backend.PluginContext) (*backend.CheckHealthResult, error) {
var dsURL string
if pluginContext.DataSourceInstanceSettings != nil {
dsURL = pluginContext.DataSourceInstanceSettings.URL
@ -290,7 +290,7 @@ func (m *manager) CheckHealth(ctx context.Context, pluginContext backend.PluginC
return resp, nil
}
func (m *manager) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
func (m *Manager) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
p, registered := m.Get(req.PluginContext.PluginID)
if !registered {
return nil, backendplugin.ErrPluginNotRegistered
@ -321,7 +321,7 @@ type keepCookiesJSONModel struct {
KeepCookies []string `json:"keepCookies"`
}
func (m *manager) callResourceInternal(w http.ResponseWriter, req *http.Request, pCtx backend.PluginContext) error {
func (m *Manager) callResourceInternal(w http.ResponseWriter, req *http.Request, pCtx backend.PluginContext) error {
p, registered := m.Get(pCtx.PluginID)
if !registered {
return backendplugin.ErrPluginNotRegistered
@ -382,7 +382,7 @@ func (m *manager) callResourceInternal(w http.ResponseWriter, req *http.Request,
}
// CallResource calls a plugin resource.
func (m *manager) CallResource(pCtx backend.PluginContext, reqCtx *models.ReqContext, path string) {
func (m *Manager) CallResource(pCtx backend.PluginContext, reqCtx *models.ReqContext, path string) {
var dsURL string
if pCtx.DataSourceInstanceSettings != nil {
dsURL = pCtx.DataSourceInstanceSettings.URL

View File

@ -310,7 +310,7 @@ func TestManager(t *testing.T) {
type managerScenarioCtx struct {
cfg *setting.Cfg
license *testLicensingService
manager *manager
manager *Manager
factory backendplugin.PluginFactoryFunc
plugin *testPlugin
env []string
@ -331,7 +331,7 @@ func newManagerScenario(t *testing.T, managed bool, fn func(t *testing.T, ctx *m
ctx := &managerScenarioCtx{
cfg: cfg,
license: license,
manager: &manager{
manager: &Manager{
Cfg: cfg,
License: license,
PluginRequestValidator: validator,
@ -340,9 +340,6 @@ func newManagerScenario(t *testing.T, managed bool, fn func(t *testing.T, ctx *m
},
}
err := ctx.manager.Init()
require.NoError(t, err)
ctx.factory = func(pluginID string, logger log.Logger, env []string) (backendplugin.Plugin, error) {
ctx.plugin = &testPlugin{
pluginID: pluginID,

View File

@ -4,12 +4,13 @@ import (
"testing"
"github.com/grafana/grafana/pkg/setting"
. "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/require"
)
func TestFrontendPlugin(t *testing.T) {
Convey("When setting paths based on App on Windows", t, func() {
setting.StaticRootPath = "c:\\grafana\\public"
t.Run("When setting paths based on App on Windows", func(t *testing.T) {
cfg := setting.NewCfg()
cfg.StaticRootPath = "c:\\grafana\\public"
fp := &FrontendPluginBase{
PluginBase: PluginBase{
@ -26,9 +27,8 @@ func TestFrontendPlugin(t *testing.T) {
},
},
}
cfg := setting.NewCfg()
fp.setPathsBasedOnApp(app, cfg)
So(fp.Module, ShouldEqual, "app/plugins/app/testdata/datasources/datasource/module")
fp.setPathsBasedOnApp(app, cfg)
require.Equal(t, "app/plugins/app/testdata/datasources/datasource/module", fp.Module)
})
}

View File

@ -28,7 +28,6 @@ type Manager interface {
// AppCount gets the number of apps.
AppCount() int
// GetEnabledPlugins gets enabled plugins.
// GetEnabledPlugins gets enabled plugins.
GetEnabledPlugins(orgID int64) (*EnabledPlugins, error)
// GrafanaLatestVersion gets the latest Grafana version.
GrafanaLatestVersion() string

View File

@ -8,6 +8,7 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/require"
)
@ -79,15 +80,16 @@ func pluginScenario(t *testing.T, desc string, fn func(*testing.T, *PluginManage
t.Helper()
t.Run("Given a plugin", func(t *testing.T) {
pm := newManager(&setting.Cfg{
cfg := &setting.Cfg{
FeatureToggles: map[string]bool{},
PluginSettings: setting.PluginSettings{
"test-app": map[string]string{
"path": "testdata/test-app",
},
},
})
err := pm.Init()
}
pm := newManager(cfg, &sqlstore.SQLStore{}, &fakeBackendPluginManager{})
err := pm.init()
require.NoError(t, err)
t.Run(desc, func(t *testing.T) {

View File

@ -3,24 +3,26 @@ package manager
import (
"testing"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGetPluginDashboards(t *testing.T) {
pm := newManager(&setting.Cfg{
cfg := &setting.Cfg{
FeatureToggles: map[string]bool{},
PluginSettings: setting.PluginSettings{
"test-app": map[string]string{
"path": "testdata/test-app",
},
},
})
err := pm.Init()
}
pm := newManager(cfg, &sqlstore.SQLStore{}, &fakeBackendPluginManager{})
err := pm.init()
require.NoError(t, err)
bus.AddHandler("test", func(query *models.GetDashboardQuery) error {
@ -48,12 +50,12 @@ func TestGetPluginDashboards(t *testing.T) {
dashboards, err := pm.GetPluginDashboards(1, "test-app")
require.NoError(t, err)
assert.Len(t, dashboards, 2)
assert.Equal(t, "Nginx Connections", dashboards[0].Title)
assert.Equal(t, int64(25), dashboards[0].Revision)
assert.Equal(t, int64(22), dashboards[0].ImportedRevision)
assert.Equal(t, "db/nginx-connections", dashboards[0].ImportedUri)
require.Len(t, dashboards, 2)
require.Equal(t, "Nginx Connections", dashboards[0].Title)
require.Equal(t, int64(25), dashboards[0].Revision)
require.Equal(t, int64(22), dashboards[0].ImportedRevision)
require.Equal(t, "db/nginx-connections", dashboards[0].ImportedUri)
assert.Equal(t, int64(2), dashboards[1].Revision)
assert.Equal(t, int64(0), dashboards[1].ImportedRevision)
require.Equal(t, int64(2), dashboards[1].Revision)
require.Equal(t, int64(0), dashboards[1].ImportedRevision)
}

View File

@ -22,7 +22,6 @@ import (
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin"
"github.com/grafana/grafana/pkg/plugins/manager/installer"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
@ -52,9 +51,9 @@ type PluginScanner struct {
}
type PluginManager struct {
BackendPluginManager backendplugin.Manager `inject:""`
Cfg *setting.Cfg `inject:""`
SQLStore *sqlstore.SQLStore `inject:""`
BackendPluginManager backendplugin.Manager
Cfg *setting.Cfg
SQLStore *sqlstore.SQLStore
pluginInstaller plugins.PluginInstaller
log log.Logger
scanningErrors []error
@ -75,28 +74,30 @@ type PluginManager struct {
pluginsMu sync.RWMutex
}
func init() {
registry.Register(&registry.Descriptor{
Name: "PluginManager",
Instance: newManager(nil),
InitPriority: registry.MediumHigh,
})
func ProvideService(cfg *setting.Cfg, sqlStore *sqlstore.SQLStore, backendPM backendplugin.Manager) (*PluginManager, error) {
pm := newManager(cfg, sqlStore, backendPM)
if err := pm.init(); err != nil {
return nil, err
}
return pm, nil
}
func newManager(cfg *setting.Cfg) *PluginManager {
func newManager(cfg *setting.Cfg, sqlStore *sqlstore.SQLStore, backendPM backendplugin.Manager) *PluginManager {
return &PluginManager{
Cfg: cfg,
SQLStore: sqlStore,
BackendPluginManager: backendPM,
dataSources: map[string]*plugins.DataSourcePlugin{},
plugins: map[string]*plugins.PluginBase{},
panels: map[string]*plugins.PanelPlugin{},
apps: map[string]*plugins.AppPlugin{},
pluginScanningErrors: map[string]plugins.PluginError{},
log: log.New("plugins"),
}
}
func (pm *PluginManager) Init() error {
pm.log = log.New("plugins")
func (pm *PluginManager) init() error {
plog = log.New("plugins")
pm.pluginScanningErrors = map[string]plugins.PluginError{}
pm.pluginInstaller = installer.New(false, pm.Cfg.BuildVersion, installerLog)
pm.log.Info("Starting plugin search")
@ -119,12 +120,7 @@ func (pm *PluginManager) Init() error {
}
}
err = pm.initExternalPlugins()
if err != nil {
return err
}
return nil
return pm.initExternalPlugins()
}
func (pm *PluginManager) initExternalPlugins() error {

View File

@ -15,6 +15,7 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -35,7 +36,7 @@ func TestPluginManager_Init(t *testing.T) {
pm.Cfg.BundledPluginsPath = bundledPluginsPath
pm.Cfg.StaticRootPath = staticRootPath
})
err = pm.Init()
err = pm.init()
require.NoError(t, err)
assert.Empty(t, pm.scanningErrors)
@ -51,7 +52,7 @@ func TestPluginManager_Init(t *testing.T) {
},
}
})
err := pm.Init()
err := pm.init()
require.NoError(t, err)
assert.Empty(t, pm.scanningErrors)
@ -68,7 +69,7 @@ func TestPluginManager_Init(t *testing.T) {
pm.Cfg.PluginsPath = "testdata/unsigned-datasource"
pm.Cfg.Env = setting.Prod
})
err := pm.Init()
err := pm.init()
require.NoError(t, err)
const pluginID = "test"
@ -82,7 +83,7 @@ func TestPluginManager_Init(t *testing.T) {
pm.Cfg.PluginsPath = "testdata/unsigned-datasource"
pm.Cfg.Env = setting.Dev
})
err := pm.Init()
err := pm.init()
require.NoError(t, err)
const pluginID = "test"
@ -99,7 +100,7 @@ func TestPluginManager_Init(t *testing.T) {
pm.Cfg.PluginsPath = "testdata/unsigned-panel"
pm.Cfg.Env = setting.Prod
})
err := pm.Init()
err := pm.init()
require.NoError(t, err)
const pluginID = "test-panel"
@ -113,7 +114,7 @@ func TestPluginManager_Init(t *testing.T) {
pm.Cfg.PluginsPath = "testdata/unsigned-panel"
pm.Cfg.Env = setting.Dev
})
err := pm.Init()
err := pm.init()
require.NoError(t, err)
pluginID := "test-panel"
@ -130,7 +131,7 @@ func TestPluginManager_Init(t *testing.T) {
pm.Cfg.PluginsPath = "testdata/unsigned-datasource"
pm.Cfg.PluginsAllowUnsigned = []string{"test"}
})
err := pm.Init()
err := pm.init()
require.NoError(t, err)
assert.Empty(t, pm.scanningErrors)
@ -140,7 +141,7 @@ func TestPluginManager_Init(t *testing.T) {
pm := createManager(t, func(pm *PluginManager) {
pm.Cfg.PluginsPath = "testdata/invalid-v1-signature"
})
err := pm.Init()
err := pm.init()
require.NoError(t, err)
const pluginID = "test"
@ -155,7 +156,7 @@ func TestPluginManager_Init(t *testing.T) {
pm.Cfg.PluginsPath = "testdata/lacking-files"
pm.BackendPluginManager = fm
})
err := pm.Init()
err := pm.init()
require.NoError(t, err)
assert.Equal(t, []error{fmt.Errorf(`plugin 'test' has a modified signature`)}, pm.scanningErrors)
@ -167,7 +168,7 @@ func TestPluginManager_Init(t *testing.T) {
pm.Cfg.PluginsPath = "testdata/behind-feature-flag"
pm.BackendPluginManager = &fm
})
err := pm.Init()
err := pm.init()
require.NoError(t, err)
assert.Empty(t, pm.scanningErrors)
@ -178,7 +179,7 @@ func TestPluginManager_Init(t *testing.T) {
pm := createManager(t, func(pm *PluginManager) {
pm.Cfg.PluginsPath = "testdata/duplicate-plugins"
})
err := pm.Init()
err := pm.init()
require.NoError(t, err)
assert.Len(t, pm.scanningErrors, 1)
@ -191,7 +192,7 @@ func TestPluginManager_Init(t *testing.T) {
pm := createManager(t, func(manager *PluginManager) {
manager.Cfg.PluginsPath = pluginsDir
})
err := pm.Init()
err := pm.init()
require.NoError(t, err)
require.Empty(t, pm.scanningErrors)
@ -277,7 +278,7 @@ func TestPluginManager_Init(t *testing.T) {
pm := createManager(t, func(pm *PluginManager) {
pm.Cfg.PluginsPath = "testdata/valid-v2-pvt-signature"
})
err := pm.Init()
err := pm.init()
require.NoError(t, err)
assert.Equal(t, []error{fmt.Errorf(`plugin 'test' has an invalid signature`)}, pm.scanningErrors)
@ -297,7 +298,7 @@ func TestPluginManager_Init(t *testing.T) {
pm := createManager(t, func(pm *PluginManager) {
pm.Cfg.PluginsPath = "testdata/valid-v2-pvt-signature-root-url-uri"
})
err := pm.Init()
err := pm.init()
require.NoError(t, err)
require.Empty(t, pm.scanningErrors)
@ -323,7 +324,7 @@ func TestPluginManager_Init(t *testing.T) {
pm := createManager(t, func(pm *PluginManager) {
pm.Cfg.PluginsPath = "testdata/valid-v2-pvt-signature"
})
err := pm.Init()
err := pm.init()
require.NoError(t, err)
require.Empty(t, pm.scanningErrors)
@ -349,7 +350,7 @@ func TestPluginManager_Init(t *testing.T) {
pm := createManager(t, func(pm *PluginManager) {
pm.Cfg.PluginsPath = "testdata/invalid-v2-signature"
})
err := pm.Init()
err := pm.init()
require.NoError(t, err)
assert.Equal(t, []error{fmt.Errorf(`plugin 'test' has a modified signature`)}, pm.scanningErrors)
assert.Nil(t, pm.plugins[("test")])
@ -365,7 +366,7 @@ func TestPluginManager_Init(t *testing.T) {
pm := createManager(t, func(pm *PluginManager) {
pm.Cfg.PluginsPath = "testdata/invalid-v2-signature-2"
})
err := pm.Init()
err := pm.init()
require.NoError(t, err)
assert.Equal(t, []error{fmt.Errorf(`plugin 'test' has a modified signature`)}, pm.scanningErrors)
assert.Nil(t, pm.plugins[("test")])
@ -381,7 +382,7 @@ func TestPluginManager_Init(t *testing.T) {
pm := createManager(t, func(pm *PluginManager) {
pm.Cfg.PluginsPath = "testdata/includes-symlinks"
})
err := pm.Init()
err := pm.init()
require.NoError(t, err)
require.Empty(t, pm.scanningErrors)
@ -410,7 +411,7 @@ func TestPluginManager_Init(t *testing.T) {
pm := createManager(t, func(pm *PluginManager) {
pm.Cfg.PluginsPath = "testdata/symbolic-plugin-dirs"
})
err := pm.Init()
err := pm.init()
require.NoError(t, err)
// This plugin should be properly registered, even though it is symlinked to plugins dir
require.Empty(t, pm.scanningErrors)
@ -446,7 +447,7 @@ func TestPluginManager_Installer(t *testing.T) {
pm.BackendPluginManager = fm
})
err := pm.Init()
err := pm.init()
require.NoError(t, err)
// mock installer
@ -712,12 +713,13 @@ func createManager(t *testing.T, cbs ...func(*PluginManager)) *PluginManager {
staticRootPath, err := filepath.Abs("../../../public/")
require.NoError(t, err)
pm := newManager(&setting.Cfg{
cfg := &setting.Cfg{
Raw: ini.Empty(),
Env: setting.Prod,
StaticRootPath: staticRootPath,
})
pm.BackendPluginManager = &fakeBackendPluginManager{}
}
pm := newManager(cfg, &sqlstore.SQLStore{}, &fakeBackendPluginManager{})
for _, cb := range cbs {
cb(pm)
}

View File

@ -12,34 +12,28 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/adapters"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/util/errutil"
)
func init() {
registry.Register(&registry.Descriptor{
Name: "PluginContextProvider",
Instance: newProvider(),
})
}
func newProvider() *Provider {
return &Provider{}
func ProvideService(bus bus.Bus, cacheService *localcache.CacheService, pluginManager plugins.Manager,
dataSourceCache datasources.CacheService) *Provider {
return &Provider{
Bus: bus,
CacheService: cacheService,
PluginManager: pluginManager,
DataSourceCache: dataSourceCache,
}
}
type Provider struct {
Bus bus.Bus `inject:""`
CacheService *localcache.CacheService `inject:""`
PluginManager plugins.Manager `inject:""`
DatasourceCache datasources.CacheService `inject:""`
Bus bus.Bus
CacheService *localcache.CacheService
PluginManager plugins.Manager
DataSourceCache datasources.CacheService
}
func (p *Provider) Init() error {
return nil
}
// Get allows getting plugin context by its id. If datasourceUID is not empty string
// Get allows getting plugin context by its ID. If datasourceUID is not empty string
// then PluginContext.DataSourceInstanceSettings will be resolved and appended to
// returned context.
func (p *Provider) Get(pluginID string, datasourceUID string, user *models.SignedInUser, skipCache bool) (backend.PluginContext, bool, error) {
@ -81,7 +75,7 @@ func (p *Provider) Get(pluginID string, datasourceUID string, user *models.Signe
}
if datasourceUID != "" {
ds, err := p.DatasourceCache.GetDatasourceByUID(datasourceUID, user, skipCache)
ds, err := p.DataSourceCache.GetDatasourceByUID(datasourceUID, user, skipCache)
if err != nil {
return pc, false, errutil.Wrap("Failed to get datasource", err)
}

View File

@ -1,43 +1,34 @@
package plugindashboards
import (
"context"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/tsdb"
)
func init() {
registry.Register(&registry.Descriptor{
Name: "PluginDashboardService",
Instance: &Service{},
})
func ProvideService(dataService *tsdb.Service, pluginManager plugins.Manager, sqlStore *sqlstore.SQLStore) *Service {
s := &Service{
DataService: dataService,
PluginManager: pluginManager,
SQLStore: sqlStore,
logger: log.New("plugindashboards"),
}
bus.AddEventListener(s.handlePluginStateChanged)
s.updateAppDashboards()
return s
}
type Service struct {
DataService *tsdb.Service `inject:""`
PluginManager plugins.Manager `inject:""`
SQLStore *sqlstore.SQLStore `inject:""`
DataService *tsdb.Service
PluginManager plugins.Manager
SQLStore *sqlstore.SQLStore
logger log.Logger
}
func (s *Service) Init() error {
bus.AddEventListener(s.handlePluginStateChanged)
s.logger = log.New("plugindashboards")
return nil
}
func (s *Service) Run(ctx context.Context) error {
s.updateAppDashboards()
return nil
}
func (s *Service) updateAppDashboards() {
s.logger.Debug("Looking for app dashboard updates")

View File

@ -1,45 +0,0 @@
package registry
import (
"fmt"
"github.com/facebookgo/inject"
)
// BuildServiceGraph builds a graph of services and their dependencies.
// The services are initialized after the graph is built.
func BuildServiceGraph(objs []interface{}, services []*Descriptor) error {
if services == nil {
services = GetServices()
}
for _, service := range services {
objs = append(objs, service.Instance)
}
serviceGraph := inject.Graph{}
// Provide services and their dependencies to the graph.
for _, obj := range objs {
if err := serviceGraph.Provide(&inject.Object{Value: obj}); err != nil {
return fmt.Errorf("failed to provide object to the graph: %w", err)
}
}
// Resolve services and their dependencies.
if err := serviceGraph.Populate(); err != nil {
return fmt.Errorf("failed to populate service dependencies: %w", err)
}
// Initialize services.
for _, service := range services {
if IsDisabled(service.Instance) {
continue
}
if err := service.Instance.Init(); err != nil {
return fmt.Errorf("service init failed: %w", err)
}
}
return nil
}

View File

@ -2,113 +2,13 @@ package registry
import (
"context"
"reflect"
"sort"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
)
type Descriptor struct {
Name string
Instance Service
InitPriority Priority
}
var services []*Descriptor
func RegisterServiceWithPriority(instance Service, priority Priority) {
Register(&Descriptor{
Name: reflect.TypeOf(instance).Elem().Name(),
Instance: instance,
InitPriority: priority,
})
}
func RegisterService(instance Service) {
Register(&Descriptor{
Name: reflect.TypeOf(instance).Elem().Name(),
Instance: instance,
InitPriority: Medium,
})
}
func Register(descriptor *Descriptor) {
if descriptor == nil {
return
}
// Overwrite any existing equivalent service
for i, svc := range services {
if svc.Name == descriptor.Name {
services[i] = descriptor
return
}
}
services = append(services, descriptor)
}
// GetService gets the registered service descriptor with a certain name.
// If none is found, nil is returned.
func GetService(name string) *Descriptor {
for _, svc := range services {
if svc.Name == name {
return svc
}
}
return nil
}
func GetServices() []*Descriptor {
slice := getServicesWithOverrides()
sort.Slice(slice, func(i, j int) bool {
return slice[i].InitPriority > slice[j].InitPriority
})
return slice
}
type OverrideServiceFunc func(descriptor Descriptor) (*Descriptor, bool)
var overrides []OverrideServiceFunc
func RegisterOverride(fn OverrideServiceFunc) {
overrides = append(overrides, fn)
}
func ClearOverrides() {
overrides = nil
}
func getServicesWithOverrides() []*Descriptor {
slice := []*Descriptor{}
for _, s := range services {
var descriptor *Descriptor
for _, fn := range overrides {
if newDescriptor, override := fn(*s); override {
descriptor = newDescriptor
break
}
}
if descriptor != nil {
slice = append(slice, descriptor)
} else {
slice = append(slice, s)
}
}
return slice
}
// Service interface is the lowest common shape that services
// are expected to fulfill to be started within Grafana.
type Service interface {
// Init is called by Grafana main process which gives the service
// the possibility do some initial work before its started. Things
// like adding routes, bus handlers should be done in the Init function
Init() error
// BackgroundServiceRegistry provides background services.
type BackgroundServiceRegistry interface {
GetServices() []BackgroundService
}
// CanBeDisabled allows the services to decide if it should
@ -137,17 +37,8 @@ type DatabaseMigrator interface {
AddMigration(mg *migrator.Migrator)
}
// IsDisabled returns whether a service is disabled.
func IsDisabled(srv Service) bool {
// IsDisabled returns whether a background service is disabled.
func IsDisabled(srv BackgroundService) bool {
canBeDisabled, ok := srv.(CanBeDisabled)
return ok && canBeDisabled.IsDisabled()
}
type Priority int
const (
High Priority = 100
MediumHigh Priority = 75
Medium Priority = 50
Low Priority = 0
)

View File

@ -0,0 +1,76 @@
package backgroundsvcs
import (
"github.com/grafana/grafana/pkg/api"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/infra/usagestats"
"github.com/grafana/grafana/pkg/models"
backendmanager "github.com/grafana/grafana/pkg/plugins/backendplugin/manager"
"github.com/grafana/grafana/pkg/plugins/manager"
"github.com/grafana/grafana/pkg/plugins/plugindashboards"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/services/cleanup"
"github.com/grafana/grafana/pkg/services/live"
"github.com/grafana/grafana/pkg/services/live/pushhttp"
"github.com/grafana/grafana/pkg/services/ngalert"
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/grafana/grafana/pkg/services/provisioning"
"github.com/grafana/grafana/pkg/services/rendering"
"github.com/grafana/grafana/pkg/tsdb/azuremonitor"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch"
"github.com/grafana/grafana/pkg/tsdb/elasticsearch"
"github.com/grafana/grafana/pkg/tsdb/graphite"
"github.com/grafana/grafana/pkg/tsdb/influxdb"
"github.com/grafana/grafana/pkg/tsdb/loki"
"github.com/grafana/grafana/pkg/tsdb/opentsdb"
"github.com/grafana/grafana/pkg/tsdb/prometheus"
"github.com/grafana/grafana/pkg/tsdb/tempo"
"github.com/grafana/grafana/pkg/tsdb/testdatasource"
)
func ProvideBackgroundServiceRegistry(
httpServer *api.HTTPServer, ng *ngalert.AlertNG, cleanup *cleanup.CleanUpService,
live *live.GrafanaLive, pushGateway *pushhttp.Gateway, notifications *notifications.NotificationService,
rendering *rendering.RenderingService, tokenService models.UserTokenBackgroundService,
provisioning *provisioning.ProvisioningServiceImpl, alerting *alerting.AlertEngine, pm *manager.PluginManager,
backendPM *backendmanager.Manager, metrics *metrics.InternalMetricsService,
usageStats *usagestats.UsageStatsService, tracing *tracing.TracingService, remoteCache *remotecache.RemoteCache,
// Need to make sure these are initialized, is there a better place to put them?
_ *azuremonitor.Service, _ *cloudwatch.CloudWatchService, _ *elasticsearch.Service, _ *graphite.Service, _ *influxdb.Service,
_ *loki.Service, _ *opentsdb.Service, _ *prometheus.Service, _ *tempo.Service, _ *testdatasource.TestDataPlugin, _ *plugindashboards.Service,
) *BackgroundServiceRegistry {
return NewBackgroundServiceRegistry(
httpServer,
ng,
cleanup,
live,
pushGateway,
notifications,
rendering,
tokenService,
provisioning,
alerting,
pm,
backendPM,
metrics,
usageStats,
tracing,
remoteCache)
}
// BackgroundServiceRegistry provides background services.
type BackgroundServiceRegistry struct {
Services []registry.BackgroundService
}
func NewBackgroundServiceRegistry(services ...registry.BackgroundService) *BackgroundServiceRegistry {
return &BackgroundServiceRegistry{services}
}
func (r *BackgroundServiceRegistry) GetServices() []registry.BackgroundService {
return r.Services
}

View File

@ -3,53 +3,32 @@ package server
import (
"context"
"errors"
"flag"
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
"reflect"
"strconv"
"sync"
"time"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/api"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/bus"
_ "github.com/grafana/grafana/pkg/extensions"
"github.com/grafana/grafana/pkg/infra/httpclient/httpclientprovider"
"github.com/grafana/grafana/pkg/infra/localcache"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics"
_ "github.com/grafana/grafana/pkg/infra/remotecache"
_ "github.com/grafana/grafana/pkg/infra/serverlock"
_ "github.com/grafana/grafana/pkg/infra/tracing"
_ "github.com/grafana/grafana/pkg/infra/usagestats"
"github.com/grafana/grafana/pkg/login"
_ "github.com/grafana/grafana/pkg/login/social"
"github.com/grafana/grafana/pkg/middleware"
_ "github.com/grafana/grafana/pkg/plugins/manager"
"github.com/grafana/grafana/pkg/login/social"
"github.com/grafana/grafana/pkg/registry"
_ "github.com/grafana/grafana/pkg/services/alerting"
_ "github.com/grafana/grafana/pkg/services/auth"
_ "github.com/grafana/grafana/pkg/services/auth/jwt"
_ "github.com/grafana/grafana/pkg/services/cleanup"
_ "github.com/grafana/grafana/pkg/services/librarypanels"
_ "github.com/grafana/grafana/pkg/services/login/authinfoservice"
_ "github.com/grafana/grafana/pkg/services/login/loginservice"
_ "github.com/grafana/grafana/pkg/services/ngalert"
_ "github.com/grafana/grafana/pkg/services/notifications"
"github.com/grafana/grafana/pkg/services/provisioning"
_ "github.com/grafana/grafana/pkg/services/rendering"
_ "github.com/grafana/grafana/pkg/services/search"
_ "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
"golang.org/x/sync/errgroup"
)
// Config contains parameters for the New function.
type Config struct {
ConfigFile string
// Options contains parameters for the New function.
type Options struct {
HomePath string
PidFile string
Version string
@ -58,59 +37,46 @@ type Config struct {
Listener net.Listener
}
type serviceRegistry interface {
IsDisabled(srv registry.Service) bool
GetServices() []*registry.Descriptor
}
type globalServiceRegistry struct{}
func (r *globalServiceRegistry) IsDisabled(srv registry.Service) bool {
return registry.IsDisabled(srv)
}
func (r *globalServiceRegistry) GetServices() []*registry.Descriptor {
return registry.GetServices()
}
type roleRegistry interface {
// RegisterFixedRoles registers all roles declared to AccessControl
RegisterFixedRoles() error
}
// New returns a new instance of Server.
func New(cfg Config) (*Server, error) {
s := newServer(cfg)
func New(opts Options, cfg *setting.Cfg, httpServer *api.HTTPServer, roleRegistry accesscontrol.RoleRegistry,
provisioningService provisioning.ProvisioningService, backgroundServiceProvider registry.BackgroundServiceRegistry,
) (*Server, error) {
s, err := newServer(opts, cfg, httpServer, roleRegistry, provisioningService, backgroundServiceProvider)
if err != nil {
return nil, err
}
if err := s.init(); err != nil {
return nil, err
}
return s, nil
}
func newServer(cfg Config) *Server {
func newServer(opts Options, cfg *setting.Cfg, httpServer *api.HTTPServer, roleRegistry accesscontrol.RoleRegistry,
provisioningService provisioning.ProvisioningService, backgroundServiceProvider registry.BackgroundServiceRegistry,
) (*Server, error) {
rootCtx, shutdownFn := context.WithCancel(context.Background())
childRoutines, childCtx := errgroup.WithContext(rootCtx)
return &Server{
s := &Server{
context: childCtx,
childRoutines: childRoutines,
HTTPServer: httpServer,
provisioningService: provisioningService,
roleRegistry: roleRegistry,
shutdownFn: shutdownFn,
shutdownFinished: make(chan struct{}),
childRoutines: childRoutines,
log: log.New("server"),
// Need to use the singleton setting.Cfg instance, to make sure we use the same as is injected in the DI
// graph
cfg: setting.GetCfg(),
configFile: cfg.ConfigFile,
homePath: cfg.HomePath,
pidFile: cfg.PidFile,
version: cfg.Version,
commit: cfg.Commit,
buildBranch: cfg.BuildBranch,
serviceRegistry: &globalServiceRegistry{},
listener: cfg.Listener,
cfg: cfg,
pidFile: opts.PidFile,
version: opts.Version,
commit: opts.Commit,
buildBranch: opts.BuildBranch,
backgroundServices: backgroundServiceProvider.GetServices(),
}
return s, nil
}
// Server is responsible for managing the lifecycle of services.
@ -124,20 +90,16 @@ type Server struct {
shutdownFinished chan struct{}
isInitialized bool
mtx sync.Mutex
listener net.Listener
configFile string
homePath string
pidFile string
version string
commit string
buildBranch string
backgroundServices []registry.BackgroundService
serviceRegistry serviceRegistry
HTTPServer *api.HTTPServer `inject:""`
AccessControl roleRegistry `inject:""`
ProvisioningService provisioning.ProvisioningService `inject:""`
HTTPServer *api.HTTPServer
roleRegistry accesscontrol.RoleRegistry
provisioningService provisioning.ProvisioningService
}
// init initializes the server and its services.
@ -150,36 +112,19 @@ func (s *Server) init() error {
}
s.isInitialized = true
s.loadConfiguration()
s.writePIDFile()
if err := metrics.SetEnvironmentInformation(s.cfg.MetricsGrafanaEnvironmentInfo); err != nil {
return err
}
login.Init()
social.ProvideService(s.cfg)
services := s.serviceRegistry.GetServices()
if err := s.buildServiceGraph(services); err != nil {
if err := s.roleRegistry.RegisterFixedRoles(); err != nil {
return err
}
if s.listener != nil {
for _, service := range services {
if httpS, ok := service.Instance.(*api.HTTPServer); ok {
// Configure the api.HTTPServer if necessary
// Hopefully we can find a better solution, maybe with a more advanced DI framework, f.ex. Dig?
s.log.Debug("Using provided listener for HTTP server")
httpS.Listener = s.listener
}
}
}
// Register all fixed roles
if err := s.AccessControl.RegisterFixedRoles(); err != nil {
return err
}
return s.ProvisioningService.RunInitProvisioners()
return s.provisioningService.RunInitProvisioners()
}
// Run initializes and starts services. This will block until all services have
@ -191,36 +136,32 @@ func (s *Server) Run() error {
return err
}
services := s.serviceRegistry.GetServices()
services := s.backgroundServices
// Start background services.
for _, svc := range services {
service, ok := svc.Instance.(registry.BackgroundService)
if !ok {
if registry.IsDisabled(svc) {
continue
}
if s.serviceRegistry.IsDisabled(svc.Instance) {
continue
}
// Variable is needed for accessing loop variable in callback
descriptor := svc
service := svc
serviceName := reflect.TypeOf(service).String()
s.childRoutines.Go(func() error {
select {
case <-s.context.Done():
return s.context.Err()
default:
}
s.log.Debug("Starting background service " + serviceName)
err := service.Run(s.context)
// Do not return context.Canceled error since errgroup.Group only
// returns the first error to the caller - thus we can miss a more
// interesting error.
if err != nil && !errors.Is(err, context.Canceled) {
s.log.Error("Stopped "+descriptor.Name, "reason", err)
return fmt.Errorf("%s run error: %w", descriptor.Name, err)
s.log.Error("Stopped background service "+serviceName, "reason", err)
return fmt.Errorf("%s run error: %w", serviceName, err)
}
s.log.Debug("Stopped "+descriptor.Name, "reason", err)
s.log.Debug("Stopped background service "+serviceName, "reason", err)
return nil
})
}
@ -285,43 +226,6 @@ func (s *Server) writePIDFile() {
s.log.Info("Writing PID file", "path", s.pidFile, "pid", pid)
}
// buildServiceGraph builds a graph of services and their dependencies.
func (s *Server) buildServiceGraph(services []*registry.Descriptor) error {
// Specify service dependencies.
objs := []interface{}{
bus.GetBus(),
s.cfg,
routing.NewRouteRegister(middleware.ProvideRouteOperationName, middleware.RequestMetrics(s.cfg)),
localcache.New(5*time.Minute, 10*time.Minute),
httpclientprovider.New(s.cfg),
s,
}
return registry.BuildServiceGraph(objs, services)
}
// loadConfiguration loads settings and configuration from config files.
func (s *Server) loadConfiguration() {
args := &setting.CommandLineArgs{
Config: s.configFile,
HomePath: s.homePath,
Args: flag.Args(),
}
if err := s.cfg.Load(args); err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Failed to start grafana. error: %s\n", err.Error())
os.Exit(1)
}
s.log.Info("Starting "+setting.ApplicationName,
"version", s.version,
"commit", s.commit,
"branch", s.buildBranch,
"compiled", time.Unix(setting.BuildStamp, 0),
)
s.cfg.LogConfigSources()
}
// notifySystemd sends state notifications to systemd.
func (s *Server) notifySystemd(state string) {
notifySocket := os.Getenv("NOTIFY_SOCKET")

View File

@ -3,45 +3,36 @@ package server
import (
"context"
"errors"
"fmt"
"testing"
"time"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/server/backgroundsvcs"
"github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol"
"github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/require"
)
type testServiceRegistry struct {
services []*registry.Descriptor
}
func (r *testServiceRegistry) GetServices() []*registry.Descriptor {
return r.services
}
func (r *testServiceRegistry) IsDisabled(_ registry.Service) bool {
return false
}
type testService struct {
started chan struct{}
initErr error
runErr error
isDisabled bool
}
func newTestService(initErr, runErr error) *testService {
func newTestService(runErr error, disabled bool) *testService {
return &testService{
started: make(chan struct{}),
initErr: initErr,
runErr: runErr,
isDisabled: disabled,
}
}
func (s *testService) Init() error {
return s.initErr
}
func (s *testService) Run(ctx context.Context) error {
if s.isDisabled {
return fmt.Errorf("Shouldn't run disabled service")
}
if s.runErr != nil {
return s.runErr
}
@ -50,8 +41,14 @@ func (s *testService) Run(ctx context.Context) error {
return ctx.Err()
}
func testServer() *Server {
s := newServer(Config{})
func (s *testService) IsDisabled() bool {
return s.isDisabled
}
func testServer(t *testing.T, services ...registry.BackgroundService) *Server {
t.Helper()
s, err := newServer(Options{}, setting.NewCfg(), nil, &ossaccesscontrol.OSSAccessControlService{}, nil, backgroundsvcs.NewBackgroundServiceRegistry(services...))
require.NoError(t, err)
// Required to skip configuration initialization that causes
// DI errors in this test.
s.isInitialized = true
@ -59,25 +56,8 @@ func testServer() *Server {
}
func TestServer_Run_Error(t *testing.T) {
s := testServer()
var testErr = errors.New("boom")
s.serviceRegistry = &testServiceRegistry{
services: []*registry.Descriptor{
{
Name: "TestService1",
Instance: newTestService(nil, nil),
InitPriority: registry.High,
},
{
Name: "TestService2",
Instance: newTestService(nil, testErr),
InitPriority: registry.High,
},
},
}
testErr := errors.New("boom")
s := testServer(t, newTestService(nil, false), newTestService(testErr, false))
err := s.Run()
require.ErrorIs(t, err, testErr)
require.NotZero(t, s.ExitCode(err))
@ -86,22 +66,7 @@ func TestServer_Run_Error(t *testing.T) {
func TestServer_Shutdown(t *testing.T) {
ctx := context.Background()
s := testServer()
services := []*registry.Descriptor{
{
Name: "TestService1",
Instance: newTestService(nil, nil),
InitPriority: registry.High,
},
{
Name: "TestService2",
Instance: newTestService(nil, nil),
InitPriority: registry.High,
},
}
s.serviceRegistry = &testServiceRegistry{
services: services,
}
s := testServer(t, newTestService(nil, false), newTestService(nil, true))
ch := make(chan error)
@ -109,8 +74,10 @@ func TestServer_Shutdown(t *testing.T) {
defer close(ch)
// Wait until all services launched.
for _, svc := range services {
<-svc.Instance.(*testService).started
for _, svc := range s.backgroundServices {
if !svc.(*testService).isDisabled {
<-svc.(*testService).started
}
}
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()

12
pkg/server/test_env.go Normal file
View File

@ -0,0 +1,12 @@
package server
import "github.com/grafana/grafana/pkg/services/sqlstore"
func ProvideTestEnv(server *Server, store *sqlstore.SQLStore) (*TestEnv, error) {
return &TestEnv{server, store}, nil
}
type TestEnv struct {
Server *Server
SQLStore *sqlstore.SQLStore
}

159
pkg/server/wire.go Normal file
View File

@ -0,0 +1,159 @@
// +build wireinject
package server
import (
"github.com/google/wire"
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana/pkg/api"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/infra/httpclient/httpclientprovider"
"github.com/grafana/grafana/pkg/infra/localcache"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/infra/serverlock"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/infra/usagestats"
"github.com/grafana/grafana/pkg/login/social"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin"
backendmanager "github.com/grafana/grafana/pkg/plugins/backendplugin/manager"
"github.com/grafana/grafana/pkg/plugins/manager"
"github.com/grafana/grafana/pkg/plugins/plugincontext"
"github.com/grafana/grafana/pkg/plugins/plugindashboards"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/services/auth/jwt"
"github.com/grafana/grafana/pkg/services/cleanup"
"github.com/grafana/grafana/pkg/services/contexthandler"
"github.com/grafana/grafana/pkg/services/datasourceproxy"
"github.com/grafana/grafana/pkg/services/hooks"
"github.com/grafana/grafana/pkg/services/libraryelements"
"github.com/grafana/grafana/pkg/services/librarypanels"
"github.com/grafana/grafana/pkg/services/live"
"github.com/grafana/grafana/pkg/services/live/pushhttp"
"github.com/grafana/grafana/pkg/services/login"
"github.com/grafana/grafana/pkg/services/login/authinfoservice"
"github.com/grafana/grafana/pkg/services/login/loginservice"
"github.com/grafana/grafana/pkg/services/ngalert"
ngmetrics "github.com/grafana/grafana/pkg/services/ngalert/metrics"
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/grafana/grafana/pkg/services/oauthtoken"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/rendering"
"github.com/grafana/grafana/pkg/services/schemaloader"
"github.com/grafana/grafana/pkg/services/search"
"github.com/grafana/grafana/pkg/services/shorturls"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/tsdb"
"github.com/grafana/grafana/pkg/tsdb/azuremonitor"
"github.com/grafana/grafana/pkg/tsdb/cloudmonitoring"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch"
"github.com/grafana/grafana/pkg/tsdb/elasticsearch"
"github.com/grafana/grafana/pkg/tsdb/graphite"
"github.com/grafana/grafana/pkg/tsdb/influxdb"
"github.com/grafana/grafana/pkg/tsdb/loki"
"github.com/grafana/grafana/pkg/tsdb/opentsdb"
"github.com/grafana/grafana/pkg/tsdb/postgres"
"github.com/grafana/grafana/pkg/tsdb/prometheus"
"github.com/grafana/grafana/pkg/tsdb/tempo"
"github.com/grafana/grafana/pkg/tsdb/testdatasource"
)
var wireBasicSet = wire.NewSet(
tsdb.NewService,
wire.Bind(new(plugins.DataRequestHandler), new(*tsdb.Service)),
alerting.ProvideAlertEngine,
wire.Bind(new(alerting.UsageStatsQuerier), new(*alerting.AlertEngine)),
setting.NewCfgFromArgs,
New,
api.ProvideHTTPServer,
bus.ProvideBus,
wire.Bind(new(bus.Bus), new(*bus.InProcBus)),
rendering.ProvideService,
wire.Bind(new(rendering.Service), new(*rendering.RenderingService)),
routing.ProvideRegister,
wire.Bind(new(routing.RouteRegister), new(*routing.RouteRegisterImpl)),
hooks.ProvideService,
localcache.ProvideService,
usagestats.ProvideService,
wire.Bind(new(usagestats.UsageStats), new(*usagestats.UsageStatsService)),
manager.ProvideService,
wire.Bind(new(plugins.Manager), new(*manager.PluginManager)),
backendmanager.ProvideService,
wire.Bind(new(backendplugin.Manager), new(*backendmanager.Manager)),
cloudwatch.ProvideService,
cloudwatch.ProvideLogsService,
cloudmonitoring.ProvideService,
azuremonitor.ProvideService,
postgres.ProvideService,
httpclientprovider.New,
wire.Bind(new(httpclient.Provider), new(*sdkhttpclient.Provider)),
serverlock.ProvideService,
cleanup.ProvideService,
shorturls.ProvideService,
wire.Bind(new(shorturls.Service), new(*shorturls.ShortURLService)),
quota.ProvideService,
remotecache.ProvideService,
loginservice.ProvideService,
wire.Bind(new(login.Service), new(*loginservice.Implementation)),
authinfoservice.ProvideAuthInfoService,
wire.Bind(new(login.AuthInfoService), new(*authinfoservice.Implementation)),
datasourceproxy.ProvideService,
search.ProvideService,
live.ProvideService,
pushhttp.ProvideService,
plugincontext.ProvideService,
contexthandler.ProvideService,
jwt.ProvideService,
wire.Bind(new(models.JWTService), new(*jwt.AuthService)),
plugindashboards.ProvideService,
schemaloader.ProvideService,
ngalert.ProvideService,
librarypanels.ProvideService,
wire.Bind(new(librarypanels.Service), new(*librarypanels.LibraryPanelService)),
libraryelements.ProvideService,
wire.Bind(new(libraryelements.Service), new(*libraryelements.LibraryElementService)),
notifications.ProvideService,
tracing.ProvideService,
metrics.ProvideService,
testdatasource.ProvideService,
opentsdb.ProvideService,
social.ProvideService,
influxdb.ProvideService,
wire.Bind(new(social.Service), new(*social.SocialService)),
oauthtoken.ProvideService,
wire.Bind(new(oauthtoken.OAuthTokenService), new(*oauthtoken.Service)),
tempo.ProvideService,
loki.ProvideService,
graphite.ProvideService,
prometheus.ProvideService,
elasticsearch.ProvideService,
)
var wireSet = wire.NewSet(
wireBasicSet,
sqlstore.ProvideService,
ngmetrics.ProvideService,
)
var wireTestSet = wire.NewSet(
wireBasicSet,
ProvideTestEnv,
sqlstore.ProvideServiceForTests,
ngmetrics.ProvideServiceForTest,
)
func Initialize(cla setting.CommandLineArgs, opts Options, apiOpts api.ServerOptions) (*Server, error) {
wire.Build(wireExtsSet)
return &Server{}, nil
}
func InitializeForTest(cla setting.CommandLineArgs, opts Options, apiOpts api.ServerOptions) (*TestEnv, error) {
wire.Build(wireExtsTestSet)
return &TestEnv{Server: &Server{}, SQLStore: &sqlstore.SQLStore{}}, nil
}

View File

@ -0,0 +1,56 @@
// +build wireinject,oss
package server
import (
"github.com/google/wire"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/server/backgroundsvcs"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol"
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/licensing"
"github.com/grafana/grafana/pkg/services/login"
"github.com/grafana/grafana/pkg/services/login/authinfoservice"
"github.com/grafana/grafana/pkg/services/provisioning"
"github.com/grafana/grafana/pkg/services/sqlstore/migrations"
"github.com/grafana/grafana/pkg/services/validations"
"github.com/grafana/grafana/pkg/setting"
)
var wireExtsBasicSet = wire.NewSet(
auth.ProvideUserAuthTokenService,
wire.Bind(new(models.UserTokenService), new(*auth.UserAuthTokenService)),
wire.Bind(new(models.UserTokenBackgroundService), new(*auth.UserAuthTokenService)),
licensing.ProvideService,
wire.Bind(new(models.Licensing), new(*licensing.OSSLicensingService)),
setting.ProvideProvider,
wire.Bind(new(setting.Provider), new(*setting.OSSImpl)),
ossaccesscontrol.ProvideService,
wire.Bind(new(accesscontrol.RoleRegistry), new(*ossaccesscontrol.OSSAccessControlService)),
wire.Bind(new(accesscontrol.AccessControl), new(*ossaccesscontrol.OSSAccessControlService)),
validations.ProvideValidator,
wire.Bind(new(models.PluginRequestValidator), new(*validations.OSSPluginRequestValidator)),
provisioning.ProvideService,
wire.Bind(new(provisioning.ProvisioningService), new(*provisioning.ProvisioningServiceImpl)),
backgroundsvcs.ProvideBackgroundServiceRegistry,
wire.Bind(new(registry.BackgroundServiceRegistry), new(*backgroundsvcs.BackgroundServiceRegistry)),
datasources.ProvideCacheService,
wire.Bind(new(datasources.CacheService), new(*datasources.CacheServiceImpl)),
migrations.ProvideOSSMigrations,
wire.Bind(new(registry.DatabaseMigrator), new(*migrations.OSSMigrations)),
authinfoservice.ProvideOSSUserProtectionService,
wire.Bind(new(login.UserProtectionService), new(*authinfoservice.OSSUserProtectionImpl)),
)
var wireExtsSet = wire.NewSet(
wireSet,
wireExtsBasicSet,
)
var wireExtsTestSet = wire.NewSet(
wireTestSet,
wireExtsBasicSet,
)

View File

@ -12,21 +12,22 @@ import (
"github.com/prometheus/client_golang/prometheus"
)
// OSSAccessControlService is the service implementing role based access control.
type OSSAccessControlService struct {
Cfg *setting.Cfg `inject:""`
UsageStats usagestats.UsageStats `inject:""`
Log log.Logger
registrations accesscontrol.RegistrationList
func ProvideService(cfg *setting.Cfg, usageStats usagestats.UsageStats) *OSSAccessControlService {
s := &OSSAccessControlService{
Cfg: cfg,
UsageStats: usageStats,
Log: log.New("accesscontrol"),
}
s.registerUsageMetrics()
return s
}
// Init initializes the OSSAccessControlService.
func (ac *OSSAccessControlService) Init() error {
ac.Log = log.New("accesscontrol")
ac.registerUsageMetrics()
return nil
// OSSAccessControlService is the service implementing role based access control.
type OSSAccessControlService struct {
Cfg *setting.Cfg
UsageStats usagestats.UsageStats
Log log.Logger
registrations accesscontrol.RegistrationList
}
func (ac *OSSAccessControlService) IsDisabled() bool {

View File

@ -8,7 +8,6 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/usagestats"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/assert"
@ -20,16 +19,8 @@ func setupTestEnv(t testing.TB) *OSSAccessControlService {
cfg := setting.NewCfg()
cfg.FeatureToggles = map[string]bool{"accesscontrol": true}
ac := OSSAccessControlService{
Cfg: cfg,
UsageStats: &usageStatsMock{metricsFuncs: make([]usagestats.MetricsFunc, 0)},
Log: log.New("accesscontrol-test"),
}
err := ac.Init()
require.NoError(t, err)
return &ac
ac := ProvideService(cfg, &usageStatsMock{metricsFuncs: make([]usagestats.MetricsFunc, 0)})
return ac
}
func removeRoleHelper(role string) {
@ -128,7 +119,6 @@ func TestEvaluatingPermissions(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
ac := setupTestEnv(t)
t.Cleanup(registry.ClearOverrides)
user := &models.SignedInUser{
UserId: 1,
@ -172,15 +162,7 @@ func TestUsageMetrics(t *testing.T) {
cfg.FeatureToggles = map[string]bool{"accesscontrol": true}
}
s := &OSSAccessControlService{
Cfg: cfg,
UsageStats: &usageStatsMock{t: t, metricsFuncs: make([]usagestats.MetricsFunc, 0)},
Log: log.New("accesscontrol-test"),
}
err := s.Init()
assert.Nil(t, err)
s := ProvideService(cfg, &usageStatsMock{t: t, metricsFuncs: make([]usagestats.MetricsFunc, 0)})
report, err := s.UsageStats.GetUsageReport(context.Background())
assert.Nil(t, err)

View File

@ -8,6 +8,11 @@ import (
"github.com/grafana/grafana/pkg/models"
)
type RoleRegistry interface {
// RegisterFixedRoles registers all roles declared to AccessControl
RegisterFixedRoles() error
}
// Roles definition
var (
datasourcesEditorReadRole = RoleDTO{

View File

@ -55,9 +55,6 @@ func TestAlertingUsageStats(t *testing.T) {
return nil
})
err := ae.Init()
require.NoError(t, err, "Init should not return error")
result, err := ae.QueryUsageStats()
require.NoError(t, err, "getAlertingUsage should not return error")
@ -106,8 +103,6 @@ func TestParsingAlertRuleSettings(t *testing.T) {
}
ae := &AlertEngine{}
err := ae.Init()
require.NoError(t, err, "Init should not return an error")
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {

View File

@ -11,7 +11,6 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/rendering"
"github.com/grafana/grafana/pkg/setting"
"github.com/opentracing/opentracing-go"
@ -24,11 +23,11 @@ import (
// schedules alert evaluations and makes sure notifications
// are sent.
type AlertEngine struct {
RenderService rendering.Service `inject:""`
Bus bus.Bus `inject:""`
RequestValidator models.PluginRequestValidator `inject:""`
DataService plugins.DataRequestHandler `inject:""`
Cfg *setting.Cfg `inject:""`
RenderService rendering.Service
Bus bus.Bus
RequestValidator models.PluginRequestValidator
DataService plugins.DataRequestHandler
Cfg *setting.Cfg
execQueue chan *Job
ticker *Ticker
@ -39,17 +38,21 @@ type AlertEngine struct {
resultHandler resultHandler
}
func init() {
registry.RegisterService(&AlertEngine{})
}
// IsDisabled returns true if the alerting service is disable for this instance.
func (e *AlertEngine) IsDisabled() bool {
return !setting.AlertingEnabled || !setting.ExecuteAlerts || e.Cfg.IsNgAlertEnabled()
}
// Init initializes the AlertingService.
func (e *AlertEngine) Init() error {
// ProvideAlertEngine returns a new AlertEngine.
func ProvideAlertEngine(renderer rendering.Service, bus bus.Bus, requestValidator models.PluginRequestValidator,
dataService plugins.DataRequestHandler, cfg *setting.Cfg) *AlertEngine {
e := &AlertEngine{
Cfg: cfg,
RenderService: renderer,
Bus: bus,
RequestValidator: requestValidator,
DataService: dataService,
}
e.ticker = NewTicker(time.Now(), time.Second*0, clock.New(), 1)
e.execQueue = make(chan *Job, 1000)
e.scheduler = newScheduler()
@ -57,7 +60,8 @@ func (e *AlertEngine) Init() error {
e.ruleReader = newRuleReader()
e.log = log.New("alerting.engine")
e.resultHandler = newResultHandler(e.RenderService)
return nil
return e
}
// Run starts the alerting service background process.

View File

@ -17,9 +17,7 @@ import (
func TestEngineTimeouts(t *testing.T) {
Convey("Alerting engine timeout tests", t, func() {
engine := &AlertEngine{}
err := engine.Init()
So(err, ShouldBeNil)
engine := ProvideAlertEngine(nil, nil, nil, nil, setting.NewCfg())
setting.AlertingNotificationTimeout = 30 * time.Second
setting.AlertingMaxAttempts = 3
engine.resultHandler = &FakeResultHandler{}

View File

@ -39,9 +39,7 @@ func (handler *FakeResultHandler) handle(evalContext *EvalContext) error {
func TestEngineProcessJob(t *testing.T) {
Convey("Alerting engine job processing", t, func() {
engine := &AlertEngine{}
err := engine.Init()
So(err, ShouldBeNil)
engine := ProvideAlertEngine(nil, nil, nil, nil, setting.NewCfg())
setting.AlertingEvaluationTimeout = 30 * time.Second
setting.AlertingNotificationTimeout = 30 * time.Second
setting.AlertingMaxAttempts = 3

View File

@ -12,7 +12,6 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
@ -20,28 +19,26 @@ import (
const ServiceName = "UserAuthTokenService"
func init() {
registry.Register(&registry.Descriptor{
Name: ServiceName,
Instance: &UserAuthTokenService{},
InitPriority: registry.Medium,
})
}
var getTime = time.Now
const urgentRotateTime = 1 * time.Minute
type UserAuthTokenService struct {
SQLStore *sqlstore.SQLStore `inject:""`
ServerLockService *serverlock.ServerLockService `inject:""`
Cfg *setting.Cfg `inject:""`
log log.Logger
func ProvideUserAuthTokenService(sqlStore *sqlstore.SQLStore, serverLockService *serverlock.ServerLockService,
cfg *setting.Cfg) *UserAuthTokenService {
s := &UserAuthTokenService{
SQLStore: sqlStore,
ServerLockService: serverLockService,
Cfg: cfg,
log: log.New("auth"),
}
return s
}
func (s *UserAuthTokenService) Init() error {
s.log = log.New("auth")
return nil
type UserAuthTokenService struct {
SQLStore *sqlstore.SQLStore
ServerLockService *serverlock.ServerLockService
Cfg *setting.Cfg
log log.Logger
}
func (s *UserAuthTokenService) ActiveTokenCount(ctx context.Context) (int64, error) {

View File

@ -7,38 +7,34 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/setting"
"gopkg.in/square/go-jose.v2/jwt"
)
const ServiceName = "AuthService"
func init() {
registry.Register(&registry.Descriptor{
Name: ServiceName,
Instance: &AuthService{},
InitPriority: registry.Medium,
})
func ProvideService(cfg *setting.Cfg, remoteCache *remotecache.RemoteCache) (*AuthService, error) {
s := newService(cfg, remoteCache)
if err := s.init(); err != nil {
return nil, err
}
return s, nil
}
type AuthService struct {
Cfg *setting.Cfg `inject:""`
RemoteCache *remotecache.RemoteCache `inject:""`
keySet keySet
log log.Logger
expect map[string]interface{}
expectRegistered jwt.Expected
func newService(cfg *setting.Cfg, remoteCache *remotecache.RemoteCache) *AuthService {
return &AuthService{
Cfg: cfg,
RemoteCache: remoteCache,
log: log.New("auth.jwt"),
}
}
func (s *AuthService) Init() error {
func (s *AuthService) init() error {
if !s.Cfg.JWTAuthEnabled {
return nil
}
s.log = log.New("auth.jwt")
if err := s.initClaimExpectations(); err != nil {
return err
}
@ -49,6 +45,16 @@ func (s *AuthService) Init() error {
return nil
}
type AuthService struct {
Cfg *setting.Cfg
RemoteCache *remotecache.RemoteCache
keySet keySet
log log.Logger
expect map[string]interface{}
expectRegistered jwt.Expected
}
func (s *AuthService) Verify(ctx context.Context, strToken string) (models.JWTClaims, error) {
s.log.Debug("Parsing JSON Web Token")

View File

@ -13,8 +13,6 @@ import (
"time"
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -368,33 +366,18 @@ func scenario(t *testing.T, desc string, fn scenarioFunc, cbs ...configureFunc)
}
func initAuthService(t *testing.T, cbs ...configureFunc) (*AuthService, error) {
sqlStore := sqlstore.InitTestDB(t)
remoteCacheSvc := &remotecache.RemoteCache{}
t.Helper()
cfg := setting.NewCfg()
cfg.JWTAuthEnabled = true
cfg.JWTAuthExpectClaims = "{}"
cfg.RemoteCacheOptions = &setting.RemoteCacheOptions{Name: "database"}
for _, cb := range cbs {
cb(t, cfg)
}
service := &AuthService{}
err := registry.BuildServiceGraph([]interface{}{cfg}, []*registry.Descriptor{
{
Name: sqlstore.ServiceName,
Instance: sqlStore,
},
{
Name: remotecache.ServiceName,
Instance: remoteCacheSvc,
},
{
Name: ServiceName,
Instance: service,
},
})
service := newService(cfg, remotecache.NewFakeStore(t))
err := service.init()
return service, err
}

View File

@ -13,25 +13,26 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/serverlock"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/annotations"
"github.com/grafana/grafana/pkg/setting"
)
func ProvideService(cfg *setting.Cfg, serverLockService *serverlock.ServerLockService,
shortURLService shorturls.Service) *CleanUpService {
s := &CleanUpService{
Cfg: cfg,
ServerLockService: serverLockService,
ShortURLService: shortURLService,
log: log.New("cleanup"),
}
return s
}
type CleanUpService struct {
log log.Logger
Cfg *setting.Cfg `inject:""`
ServerLockService *serverlock.ServerLockService `inject:""`
ShortURLService shorturls.Service `inject:""`
}
func init() {
registry.RegisterService(&CleanUpService{})
}
func (srv *CleanUpService) Init() error {
srv.log = log.New("cleanup")
return nil
Cfg *setting.Cfg
ServerLockService *serverlock.ServerLockService
ShortURLService shorturls.Service
}
func (srv *CleanUpService) Run(ctx context.Context) error {

View File

@ -10,9 +10,7 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/auth/jwt"
"github.com/grafana/grafana/pkg/services/contexthandler/authproxy"
"github.com/grafana/grafana/pkg/services/rendering"
"github.com/grafana/grafana/pkg/services/sqlstore"
@ -30,6 +28,10 @@ func TestInitContextWithAuthProxy_CachedInvalidUserID(t *testing.T) {
const userID = int64(1)
const orgID = int64(4)
svc := getContextHandler(t)
// XXX: These handlers have to be injected AFTER calling getContextHandler, since the latter
// creates a SQLStore which installs its own handlers.
upsertHandler := func(cmd *models.UpsertUserCommand) error {
require.Equal(t, name, cmd.ExternalUser.Login)
cmd.Result = &models.User{Id: userID}
@ -53,8 +55,6 @@ func TestInitContextWithAuthProxy_CachedInvalidUserID(t *testing.T) {
bus.ClearBusHandlers()
})
svc := getContextHandler(t)
req, err := http.NewRequest("POST", "http://example.com", nil)
require.NoError(t, err)
ctx := &models.ReqContext{
@ -90,15 +90,10 @@ type fakeRenderService struct {
rendering.Service
}
func (s *fakeRenderService) Init() error {
return nil
}
func getContextHandler(t *testing.T) *ContextHandler {
t.Helper()
sqlStore := sqlstore.InitTestDB(t)
remoteCacheSvc := &remotecache.RemoteCache{}
cfg := setting.NewCfg()
cfg.RemoteCacheOptions = &setting.RemoteCacheOptions{
@ -107,38 +102,11 @@ func getContextHandler(t *testing.T) *ContextHandler {
cfg.AuthProxyHeaderName = "X-Killa"
cfg.AuthProxyEnabled = true
cfg.AuthProxyHeaderProperty = "username"
remoteCacheSvc, err := remotecache.ProvideService(cfg, sqlStore)
require.NoError(t, err)
userAuthTokenSvc := auth.NewFakeUserAuthTokenService()
renderSvc := &fakeRenderService{}
authJWTSvc := models.NewFakeJWTService()
svc := &ContextHandler{}
err := registry.BuildServiceGraph([]interface{}{cfg}, []*registry.Descriptor{
{
Name: sqlstore.ServiceName,
Instance: sqlStore,
},
{
Name: remotecache.ServiceName,
Instance: remoteCacheSvc,
},
{
Name: auth.ServiceName,
Instance: userAuthTokenSvc,
},
{
Name: rendering.ServiceName,
Instance: renderSvc,
},
{
Name: jwt.ServiceName,
Instance: authJWTSvc,
},
{
Name: ServiceName,
Instance: svc,
},
})
require.NoError(t, err)
return svc
return ProvideService(cfg, userAuthTokenSvc, authJWTSvc, remoteCacheSvc, renderSvc, sqlStore)
}

View File

@ -15,7 +15,6 @@ import (
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/middleware/cookies"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/contexthandler/authproxy"
"github.com/grafana/grafana/pkg/services/login"
"github.com/grafana/grafana/pkg/services/rendering"
@ -35,33 +34,32 @@ const (
const ServiceName = "ContextHandler"
func init() {
registry.Register(&registry.Descriptor{
Name: ServiceName,
Instance: &ContextHandler{},
InitPriority: registry.High,
})
func ProvideService(cfg *setting.Cfg, tokenService models.UserTokenService, jwtService models.JWTService,
remoteCache *remotecache.RemoteCache, renderService rendering.Service, sqlStore *sqlstore.SQLStore) *ContextHandler {
return &ContextHandler{
Cfg: cfg,
AuthTokenService: tokenService,
JWTAuthService: jwtService,
RemoteCache: remoteCache,
RenderService: renderService,
SQLStore: sqlStore,
}
}
// ContextHandler is a middleware.
type ContextHandler struct {
Cfg *setting.Cfg `inject:""`
AuthTokenService models.UserTokenService `inject:""`
JWTAuthService models.JWTService `inject:""`
RemoteCache *remotecache.RemoteCache `inject:""`
RenderService rendering.Service `inject:""`
SQLStore *sqlstore.SQLStore `inject:""`
Cfg *setting.Cfg
AuthTokenService models.UserTokenService
JWTAuthService models.JWTService
RemoteCache *remotecache.RemoteCache
RenderService rendering.Service
SQLStore *sqlstore.SQLStore
// GetTime returns the current time.
// Stubbable by tests.
GetTime func() time.Time
}
// Init initializes the service.
func (h *ContextHandler) Init() error {
return nil
}
type reqContextKey struct{}
// FromContext returns the ReqContext value stored in a context.Context, if any.

View File

@ -12,37 +12,41 @@ import (
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/oauthtoken"
"github.com/grafana/grafana/pkg/setting"
)
func init() {
registry.RegisterService(&DatasourceProxyService{})
func ProvideService(dataSourceCache datasources.CacheService, plugReqValidator models.PluginRequestValidator,
pm plugins.Manager, cfg *setting.Cfg, httpClientProvider httpclient.Provider,
oauthTokenService *oauthtoken.Service) *DataSourceProxyService {
return &DataSourceProxyService{
DataSourceCache: dataSourceCache,
PluginRequestValidator: plugReqValidator,
PluginManager: pm,
Cfg: cfg,
HTTPClientProvider: httpClientProvider,
OAuthTokenService: oauthTokenService,
}
}
type DatasourceProxyService struct {
DatasourceCache datasources.CacheService `inject:""`
PluginRequestValidator models.PluginRequestValidator `inject:""`
PluginManager plugins.Manager `inject:""`
Cfg *setting.Cfg `inject:""`
HTTPClientProvider httpclient.Provider `inject:""`
OAuthTokenService *oauthtoken.Service `inject:""`
type DataSourceProxyService struct {
DataSourceCache datasources.CacheService
PluginRequestValidator models.PluginRequestValidator
PluginManager plugins.Manager
Cfg *setting.Cfg
HTTPClientProvider httpclient.Provider
OAuthTokenService *oauthtoken.Service
}
func (p *DatasourceProxyService) Init() error {
return nil
}
func (p *DatasourceProxyService) ProxyDataSourceRequest(c *models.ReqContext) {
func (p *DataSourceProxyService) ProxyDataSourceRequest(c *models.ReqContext) {
p.ProxyDatasourceRequestWithID(c, c.ParamsInt64(":id"))
}
func (p *DatasourceProxyService) ProxyDatasourceRequestWithID(c *models.ReqContext, dsID int64) {
func (p *DataSourceProxyService) ProxyDatasourceRequestWithID(c *models.ReqContext, dsID int64) {
c.TimeRequest(metrics.MDataSourceProxyReqTimer)
ds, err := p.DatasourceCache.GetDatasource(dsID, c.SignedInUser, c.SkipCache)
ds, err := p.DataSourceCache.GetDatasource(dsID, c.SignedInUser, c.SkipCache)
if err != nil {
if errors.Is(err, models.ErrDataSourceAccessDenied) {
c.JsonApiErr(http.StatusForbidden, "Access denied to datasource", err)

View File

@ -6,30 +6,24 @@ import (
"github.com/grafana/grafana/pkg/infra/localcache"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/sqlstore"
)
func ProvideCacheService(cacheService *localcache.CacheService, sqlStore *sqlstore.SQLStore) *CacheServiceImpl {
return &CacheServiceImpl{
CacheService: cacheService,
SQLStore: sqlStore,
}
}
type CacheService interface {
GetDatasource(datasourceID int64, user *models.SignedInUser, skipCache bool) (*models.DataSource, error)
GetDatasourceByUID(datasourceUID string, user *models.SignedInUser, skipCache bool) (*models.DataSource, error)
}
type CacheServiceImpl struct {
CacheService *localcache.CacheService `inject:""`
SQLStore *sqlstore.SQLStore `inject:""`
}
func init() {
registry.Register(&registry.Descriptor{
Name: "DatasourceCacheService",
Instance: &CacheServiceImpl{},
InitPriority: registry.Low,
})
}
func (dc *CacheServiceImpl) Init() error {
return nil
CacheService *localcache.CacheService
SQLStore *sqlstore.SQLStore
}
func (dc *CacheServiceImpl) GetDatasource(

View File

@ -3,7 +3,6 @@ package hooks
import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
)
type IndexDataHook func(indexData *dtos.IndexViewData, req *models.ReqContext)
@ -15,12 +14,8 @@ type HooksService struct {
loginHooks []LoginHook
}
func init() {
registry.RegisterService(&HooksService{})
}
func (srv *HooksService) Init() error {
return nil
func ProvideService() *HooksService {
return &HooksService{}
}
func (srv *HooksService) AddIndexDataHook(hook IndexDataHook) {

View File

@ -4,11 +4,21 @@ import (
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
)
func ProvideService(cfg *setting.Cfg, sqlStore *sqlstore.SQLStore, routeRegister routing.RouteRegister) *LibraryElementService {
l := &LibraryElementService{
Cfg: cfg,
SQLStore: sqlStore,
RouteRegister: routeRegister,
log: log.New("library-elements"),
}
l.registerAPIEndpoints()
return l
}
// Service is a service for operating on library elements.
type Service interface {
CreateElement(c *models.ReqContext, cmd CreateLibraryElementCommand) (LibraryElementDTO, error)
@ -20,25 +30,12 @@ type Service interface {
// LibraryElementService is the service for the Library Element feature.
type LibraryElementService struct {
Cfg *setting.Cfg `inject:""`
SQLStore *sqlstore.SQLStore `inject:""`
RouteRegister routing.RouteRegister `inject:""`
Cfg *setting.Cfg
SQLStore *sqlstore.SQLStore
RouteRegister routing.RouteRegister
log log.Logger
}
func init() {
registry.RegisterService(&LibraryElementService{})
}
// Init initializes the LibraryElement service
func (l *LibraryElementService) Init() error {
l.log = log.New("library-elements")
l.registerAPIEndpoints()
return nil
}
// CreateElement creates a Library Element.
func (l *LibraryElementService) CreateElement(c *models.ReqContext, cmd CreateLibraryElementCommand) (LibraryElementDTO, error) {
return l.createLibraryElement(c, cmd)

View File

@ -17,7 +17,6 @@ import (
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
@ -281,8 +280,6 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
t.Helper()
t.Run(desc, func(t *testing.T) {
t.Cleanup(registry.ClearOverrides)
ctx := macaron.Context{
Req: macaron.Request{Request: &http.Request{}},
}

View File

@ -7,12 +7,22 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/libraryelements"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
)
func ProvideService(cfg *setting.Cfg, sqlStore *sqlstore.SQLStore, routeRegister routing.RouteRegister,
libraryElementService libraryelements.Service) *LibraryPanelService {
return &LibraryPanelService{
Cfg: cfg,
SQLStore: sqlStore,
RouteRegister: routeRegister,
LibraryElementService: libraryElementService,
log: log.New("library-panels"),
}
}
// Service is a service for operating on library panels.
type Service interface {
LoadLibraryPanelsForDashboard(c *models.ReqContext, dash *models.Dashboard) error
@ -22,23 +32,13 @@ type Service interface {
// LibraryPanelService is the service for the Panel Library feature.
type LibraryPanelService struct {
Cfg *setting.Cfg `inject:""`
SQLStore *sqlstore.SQLStore `inject:""`
RouteRegister routing.RouteRegister `inject:""`
LibraryElementService libraryelements.Service `inject:""`
Cfg *setting.Cfg
SQLStore *sqlstore.SQLStore
RouteRegister routing.RouteRegister
LibraryElementService libraryelements.Service
log log.Logger
}
func init() {
registry.RegisterService(&LibraryPanelService{})
}
// Init initializes the LibraryPanel service
func (lps *LibraryPanelService) Init() error {
lps.log = log.New("library-panels")
return nil
}
// LoadLibraryPanelsForDashboard loops through all panels in dashboard JSON and replaces any library panel JSON
// with JSON stored for library panel in db.
func (lps *LibraryPanelService) LoadLibraryPanelsForDashboard(c *models.ReqContext, dash *models.Dashboard) error {

View File

@ -16,7 +16,6 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson"
dboards "github.com/grafana/grafana/pkg/dashboards"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/libraryelements"
"github.com/grafana/grafana/pkg/services/sqlstore"
@ -716,8 +715,6 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
t.Helper()
t.Run(desc, func(t *testing.T) {
t.Cleanup(registry.ClearOverrides)
ctx := macaron.Context{
Req: macaron.Request{Request: &http.Request{}},
}

View File

@ -12,8 +12,8 @@ const (
)
type OSSLicensingService struct {
Cfg *setting.Cfg `inject:""`
HooksService *hooks.HooksService `inject:""`
Cfg *setting.Cfg
HooksService *hooks.HooksService
}
func (*OSSLicensingService) HasLicense() bool {
@ -44,7 +44,15 @@ func (l *OSSLicensingService) LicenseURL(user *models.SignedInUser) string {
return "https://grafana.com/oss/grafana?utm_source=grafana_footer"
}
func (l *OSSLicensingService) Init() error {
func (*OSSLicensingService) HasValidLicense() bool {
return false
}
func ProvideService(cfg *setting.Cfg, hooksService *hooks.HooksService) *OSSLicensingService {
l := &OSSLicensingService{
Cfg: cfg,
HooksService: hooksService,
}
l.HooksService.AddIndexDataHook(func(indexData *dtos.IndexViewData, req *models.ReqContext) {
for _, node := range indexData.NavTree {
if node.Id == "admin" {
@ -57,10 +65,5 @@ func (l *OSSLicensingService) Init() error {
}
}
})
return nil
}
func (*OSSLicensingService) HasValidLicense() bool {
return false
return l
}

View File

@ -5,39 +5,13 @@ import (
"time"
"github.com/grafana/grafana/pkg/infra/localcache"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/live"
"github.com/grafana/grafana/pkg/services/live/database"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
)
// SetupTestStorage initializes a storage to used by the integration tests.
// This is required to properly register and execute migrations.
func SetupTestStorage(t *testing.T) *database.Storage {
cfg := setting.NewCfg()
// Live is disabled by default and only if it's enabled its database migrations run
// and the related database tables are created.
cfg.FeatureToggles = map[string]bool{"live": true}
gLive := live.NewGrafanaLive()
gLive.Cfg = cfg
// Hook for initialising the service after the Cfg is populated
// so that database migrations will run.
overrideServiceFunc := func(descriptor registry.Descriptor) (*registry.Descriptor, bool) {
if _, ok := descriptor.Instance.(*live.GrafanaLive); ok {
return &registry.Descriptor{
Name: descriptor.Name,
Instance: gLive,
InitPriority: descriptor.InitPriority,
}, true
}
return nil, false
}
registry.RegisterOverride(overrideServiceFunc)
// Now we can use sql.Store.
sqlStore := sqlstore.InitTestDB(t)
localCache := localcache.New(time.Hour, time.Hour)
return database.NewStorage(sqlStore, localCache)

View File

@ -21,7 +21,6 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins/manager"
"github.com/grafana/grafana/pkg/plugins/plugincontext"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/live/database"
"github.com/grafana/grafana/pkg/services/live/features"
@ -33,7 +32,6 @@ import (
"github.com/grafana/grafana/pkg/services/live/runstream"
"github.com/grafana/grafana/pkg/services/live/survey"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch"
"github.com/grafana/grafana/pkg/util"
@ -49,14 +47,9 @@ var (
loggerCF = log.New("live.centrifuge")
)
func init() {
registry.RegisterServiceWithPriority(NewGrafanaLive(), registry.Low)
}
func NewGrafanaLive() *GrafanaLive {
return &GrafanaLive{
channels: make(map[string]models.ChannelHandler),
channelsMu: sync.RWMutex{},
GrafanaScope: CoreGrafanaScope{
Features: make(map[string]models.ChannelHandlerFactory),
},
@ -71,98 +64,41 @@ type CoreGrafanaScope struct {
Dashboards models.DashboardActivityChannel
}
// GrafanaLive manages live real-time connections to Grafana (over WebSocket at this moment).
// The main concept here is Channel. Connections can subscribe to many channels. Each channel
// can have different permissions and properties but once a connection subscribed to a channel
// it starts receiving all messages published into this channel. Thus GrafanaLive is a PUB/SUB
// server.
type GrafanaLive struct {
PluginContextProvider *plugincontext.Provider `inject:""`
Cfg *setting.Cfg `inject:""`
RouteRegister routing.RouteRegister `inject:""`
LogsService *cloudwatch.LogsService `inject:""`
PluginManager *manager.PluginManager `inject:""`
CacheService *localcache.CacheService `inject:""`
DatasourceCache datasources.CacheService `inject:""`
SQLStore *sqlstore.SQLStore `inject:""`
node *centrifuge.Node
surveyCaller *survey.Caller
// Websocket handlers
websocketHandler interface{}
pushWebsocketHandler interface{}
// Full channel handler
channels map[string]models.ChannelHandler
channelsMu sync.RWMutex
// The core internal features
GrafanaScope CoreGrafanaScope
ManagedStreamRunner *managedstream.Runner
contextGetter *liveplugin.ContextGetter
runStreamManager *runstream.Manager
storage *database.Storage
}
func (g *GrafanaLive) getStreamPlugin(pluginID string) (backend.StreamHandler, error) {
plugin, ok := g.PluginManager.BackendPluginManager.Get(pluginID)
if !ok {
return nil, fmt.Errorf("plugin not found: %s", pluginID)
func ProvideService(plugCtxProvider *plugincontext.Provider, cfg *setting.Cfg, routeRegister routing.RouteRegister,
logsService *cloudwatch.LogsService, pluginManager *manager.PluginManager, cacheService *localcache.CacheService,
dataSourceCache datasources.CacheService, sqlStore *sqlstore.SQLStore) (*GrafanaLive, error) {
g := &GrafanaLive{
Cfg: cfg,
PluginContextProvider: plugCtxProvider,
RouteRegister: routeRegister,
LogsService: logsService,
PluginManager: pluginManager,
CacheService: cacheService,
DataSourceCache: dataSourceCache,
SQLStore: sqlStore,
channels: make(map[string]models.ChannelHandler),
GrafanaScope: CoreGrafanaScope{
Features: make(map[string]models.ChannelHandlerFactory),
},
}
streamHandler, ok := plugin.(backend.StreamHandler)
if !ok {
return nil, fmt.Errorf("%s plugin does not implement StreamHandler: %#v", pluginID, plugin)
}
return streamHandler, nil
}
// AddMigration defines database migrations.
// This is an implementation of registry.DatabaseMigrator.
func (g *GrafanaLive) AddMigration(mg *migrator.Migrator) {
if g == nil || g.Cfg == nil || !g.Cfg.IsLiveConfigEnabled() {
return
}
database.AddLiveChannelMigrations(mg)
}
func (g *GrafanaLive) Run(ctx context.Context) error {
if g.runStreamManager != nil {
// Only run stream manager if GrafanaLive properly initialized.
_ = g.runStreamManager.Run(ctx)
return g.node.Shutdown(context.Background())
}
return nil
}
var clientConcurrency = 8
func (g *GrafanaLive) IsHA() bool {
return g.Cfg != nil && g.Cfg.LiveHAEngine != ""
}
// Init initializes Live service.
// Required to implement the registry.Service interface.
func (g *GrafanaLive) Init() error {
logger.Debug("GrafanaLive initialization", "ha", g.IsHA())
// We use default config here as starting point. Default config contains
// reasonable values for available options.
cfg := centrifuge.DefaultConfig
scfg := centrifuge.DefaultConfig
// cfg.LogLevel = centrifuge.LogLevelDebug
cfg.LogHandler = handleLog
cfg.LogLevel = centrifuge.LogLevelError
cfg.MetricsNamespace = "grafana_live"
// scfg.LogLevel = centrifuge.LogLevelDebug
scfg.LogHandler = handleLog
scfg.LogLevel = centrifuge.LogLevelError
scfg.MetricsNamespace = "grafana_live"
// Node is the core object in Centrifuge library responsible for many useful
// things. For example Node allows to publish messages to channels from server
// side with its Publish method.
node, err := centrifuge.New(cfg)
node, err := centrifuge.New(scfg)
if err != nil {
return err
return nil, err
}
g.node = node
@ -178,7 +114,7 @@ func (g *GrafanaLive) Init() error {
for _, redisConf := range redisShardConfigs {
redisShard, err := centrifuge.NewRedisShard(node, redisConf)
if err != nil {
return fmt.Errorf("error connecting to Live Redis: %v", err)
return nil, fmt.Errorf("error connecting to Live Redis: %v", err)
}
redisShards = append(redisShards, redisShard)
}
@ -199,7 +135,7 @@ func (g *GrafanaLive) Init() error {
Shards: redisShards,
})
if err != nil {
return fmt.Errorf("error creating Live Redis broker: %v", err)
return nil, fmt.Errorf("error creating Live Redis broker: %v", err)
}
node.SetBroker(broker)
@ -208,7 +144,7 @@ func (g *GrafanaLive) Init() error {
Shards: redisShards,
})
if err != nil {
return fmt.Errorf("error creating Live Redis presence manager: %v", err)
return nil, fmt.Errorf("error creating Live Redis presence manager: %v", err)
}
node.SetPresenceManager(presenceManager)
}
@ -235,7 +171,7 @@ func (g *GrafanaLive) Init() error {
})
cmd := redisClient.Ping()
if _, err := cmd.Result(); err != nil {
return fmt.Errorf("error pinging Redis: %v", err)
return nil, fmt.Errorf("error pinging Redis: %v", err)
}
managedStreamRunner = managedstream.NewRunner(
g.Publish,
@ -252,7 +188,7 @@ func (g *GrafanaLive) Init() error {
g.surveyCaller = survey.NewCaller(managedStreamRunner, node)
err = g.surveyCaller.SetupHandlers()
if err != nil {
return err
return nil, err
}
// Set ConnectHandler called when client successfully connected to Node. Your code
@ -312,12 +248,12 @@ func (g *GrafanaLive) Init() error {
// Run node. This method does not block.
if err := node.Run(); err != nil {
return err
return nil, err
}
appURL, err := url.Parse(g.Cfg.AppURL)
if err != nil {
return fmt.Errorf("error parsing AppURL %s: %w", g.Cfg.AppURL, err)
return nil, fmt.Errorf("error parsing AppURL %s: %w", g.Cfg.AppURL, err)
}
originPatterns := g.Cfg.LiveAllowedOrigins
@ -368,6 +304,62 @@ func (g *GrafanaLive) Init() error {
group.Get("/push/:streamId", g.pushWebsocketHandler)
}, middleware.ReqOrgAdmin)
return g, nil
}
// GrafanaLive manages live real-time connections to Grafana (over WebSocket at this moment).
// The main concept here is Channel. Connections can subscribe to many channels. Each channel
// can have different permissions and properties but once a connection subscribed to a channel
// it starts receiving all messages published into this channel. Thus GrafanaLive is a PUB/SUB
// server.
type GrafanaLive struct {
PluginContextProvider *plugincontext.Provider
Cfg *setting.Cfg
RouteRegister routing.RouteRegister
LogsService *cloudwatch.LogsService
PluginManager *manager.PluginManager
CacheService *localcache.CacheService
DataSourceCache datasources.CacheService
SQLStore *sqlstore.SQLStore
node *centrifuge.Node
surveyCaller *survey.Caller
// Websocket handlers
websocketHandler interface{}
pushWebsocketHandler interface{}
// Full channel handler
channels map[string]models.ChannelHandler
channelsMu sync.RWMutex
// The core internal features
GrafanaScope CoreGrafanaScope
ManagedStreamRunner *managedstream.Runner
contextGetter *liveplugin.ContextGetter
runStreamManager *runstream.Manager
storage *database.Storage
}
func (g *GrafanaLive) getStreamPlugin(pluginID string) (backend.StreamHandler, error) {
plugin, ok := g.PluginManager.BackendPluginManager.Get(pluginID)
if !ok {
return nil, fmt.Errorf("plugin not found: %s", pluginID)
}
streamHandler, ok := plugin.(backend.StreamHandler)
if !ok {
return nil, fmt.Errorf("%s plugin does not implement StreamHandler: %#v", pluginID, plugin)
}
return streamHandler, nil
}
func (g *GrafanaLive) Run(ctx context.Context) error {
if g.runStreamManager != nil {
// Only run stream manager if GrafanaLive properly initialized.
return g.runStreamManager.Run(ctx)
}
return nil
}
@ -419,6 +411,12 @@ func checkAllowedOrigin(origin string, appURL *url.URL, originGlobs []glob.Glob)
return false, nil
}
var clientConcurrency = 8
func (g *GrafanaLive) IsHA() bool {
return g.Cfg != nil && g.Cfg.LiveHAEngine != ""
}
func runConcurrentlyIfNeeded(ctx context.Context, semaphore chan struct{}, fn func()) error {
if cap(semaphore) > 1 {
select {
@ -691,7 +689,7 @@ func (g *GrafanaLive) handleStreamScope(u *models.SignedInUser, namespace string
}
func (g *GrafanaLive) handleDatasourceScope(user *models.SignedInUser, namespace string) (models.ChannelHandlerFactory, error) {
ds, err := g.DatasourceCache.GetDatasourceByUID(namespace, user, false)
ds, err := g.DataSourceCache.GetDatasourceByUID(namespace, user, false)
if err != nil {
return nil, fmt.Errorf("error getting datasource: %w", err)
}

View File

@ -8,7 +8,6 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/live"
"github.com/grafana/grafana/pkg/services/live/convert"
"github.com/grafana/grafana/pkg/services/live/pushurl"
@ -19,26 +18,24 @@ var (
logger = log.New("live.push_http")
)
func init() {
registry.RegisterServiceWithPriority(&Gateway{}, registry.Low)
func ProvideService(cfg *setting.Cfg, live *live.GrafanaLive) *Gateway {
logger.Info("Live Push Gateway initialization")
g := &Gateway{
Cfg: cfg,
GrafanaLive: live,
converter: convert.NewConverter(),
}
return g
}
// Gateway receives data and translates it to Grafana Live publications.
type Gateway struct {
Cfg *setting.Cfg `inject:""`
GrafanaLive *live.GrafanaLive `inject:""`
Cfg *setting.Cfg
GrafanaLive *live.GrafanaLive
converter *convert.Converter
}
// Init Gateway.
func (g *Gateway) Init() error {
logger.Info("Live Push Gateway initialization")
g.converter = convert.NewConverter()
return nil
}
// Run Gateway.
func (g *Gateway) Run(ctx context.Context) error {
<-ctx.Done()

View File

@ -7,33 +7,27 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/login"
"github.com/grafana/grafana/pkg/services/sqlstore"
)
const genericOAuthModule = "oauth_generic_oauth"
func init() {
srv := &Implementation{}
registry.Register(&registry.Descriptor{
Name: "UserAuthInfo",
Instance: srv,
InitPriority: registry.MediumHigh,
})
}
type Implementation struct {
Bus bus.Bus `inject:""`
SQLStore *sqlstore.SQLStore `inject:""`
UserProtectionService login.UserProtectionService `inject:""`
Bus bus.Bus
SQLStore *sqlstore.SQLStore
UserProtectionService login.UserProtectionService
logger log.Logger
}
func (s *Implementation) Init() error {
s.logger = log.New("login.authinfo")
func ProvideAuthInfoService(bus bus.Bus, store *sqlstore.SQLStore, service login.UserProtectionService) *Implementation {
s := &Implementation{
Bus: bus,
SQLStore: store,
UserProtectionService: service,
logger: log.New("login.authinfo"),
}
s.Bus.AddHandler(s.GetExternalUserInfoByLogin)
s.Bus.AddHandler(s.GetAuthInfo)
@ -41,7 +35,7 @@ func (s *Implementation) Init() error {
s.Bus.AddHandler(s.UpdateAuthInfo)
s.Bus.AddHandler(s.DeleteAuthInfo)
return nil
return s
}
func (s *Implementation) getUserById(id int64) (bool, *models.User, error) {

View File

@ -20,12 +20,7 @@ import (
//nolint:goconst
func TestUserAuth(t *testing.T) {
sqlStore := sqlstore.InitTestDB(t)
srv := &Implementation{
Bus: bus.New(),
SQLStore: sqlStore,
UserProtectionService: OSSUserProtectionImpl{},
}
srv.Init()
srv := ProvideAuthInfoService(bus.New(), sqlStore, &OSSUserProtectionImpl{})
t.Run("Given 5 users", func(t *testing.T) {
for i := 0; i < 5; i++ {

View File

@ -2,20 +2,15 @@ package authinfoservice
import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
)
func init() {
registry.RegisterService(&OSSUserProtectionImpl{})
}
type OSSUserProtectionImpl struct {
}
func (OSSUserProtectionImpl) Init() error {
return nil
func ProvideOSSUserProtectionService() *OSSUserProtectionImpl {
return &OSSUserProtectionImpl{}
}
func (OSSUserProtectionImpl) AllowUserMapping(_ *models.User, _ string) error {
func (*OSSUserProtectionImpl) AllowUserMapping(_ *models.User, _ string) error {
return nil
}

View File

@ -7,32 +7,32 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/login"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/sqlstore"
)
func init() {
registry.RegisterService(&Implementation{})
}
var (
logger = log.New("login.ext_user")
)
type Implementation struct {
SQLStore *sqlstore.SQLStore `inject:""`
Bus bus.Bus `inject:""`
AuthInfoService login.AuthInfoService `inject:""`
QuotaService *quota.QuotaService `inject:""`
TeamSync login.TeamSyncFunc
func ProvideService(sqlStore *sqlstore.SQLStore, bus bus.Bus, quotaService *quota.QuotaService, authInfoService login.AuthInfoService) *Implementation {
s := &Implementation{
SQLStore: sqlStore,
Bus: bus,
QuotaService: quotaService,
AuthInfoService: authInfoService,
}
bus.AddHandler(s.UpsertUser)
return s
}
func (ls *Implementation) Init() error {
ls.Bus.AddHandler(ls.UpsertUser)
return nil
type Implementation struct {
SQLStore *sqlstore.SQLStore
Bus bus.Bus
AuthInfoService login.AuthInfoService
QuotaService *quota.QuotaService
TeamSync login.TeamSyncFunc
}
// CreateUser creates inserts a new one.

View File

@ -60,7 +60,7 @@ type API struct {
InstanceStore store.InstanceStore
AlertingStore store.AlertingStore
AdminConfigStore store.AdminConfigurationStore
DataProxy *datasourceproxy.DatasourceProxyService
DataProxy *datasourceproxy.DataSourceProxyService
MultiOrgAlertmanager *notifier.MultiOrgAlertmanager
StateManager *state.Manager
}

View File

@ -10,7 +10,7 @@ import (
"github.com/grafana/grafana/pkg/services/ngalert/store"
"github.com/grafana/grafana/pkg/services/quota"
coreapi "github.com/grafana/grafana/pkg/api"
"github.com/grafana/grafana/pkg/api/apierrors"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
@ -304,5 +304,5 @@ func toNamespaceErrorResponse(err error) response.Response {
if errors.Is(err, models.ErrDashboardIdentifierNotSet) {
return ErrResp(http.StatusBadRequest, err, err.Error())
}
return coreapi.ToFolderErrorResponse(err)
return apierrors.ToFolderErrorResponse(err)
}

View File

@ -76,7 +76,7 @@ func (p *LotexProm) RouteGetRuleStatuses(ctx *models.ReqContext) response.Respon
}
func (p *LotexProm) getEndpoints(ctx *models.ReqContext) (*promEndpoints, error) {
ds, err := p.DataProxy.DatasourceCache.GetDatasource(ctx.ParamsInt64("Recipient"), ctx.SignedInUser, ctx.SkipCache)
ds, err := p.DataProxy.DataSourceCache.GetDatasource(ctx.ParamsInt64("Recipient"), ctx.SignedInUser, ctx.SkipCache)
if err != nil {
return nil, err
}

View File

@ -150,7 +150,7 @@ func (r *LotexRuler) RoutePostNameRulesConfig(ctx *models.ReqContext, conf apimo
}
func (r *LotexRuler) getPrefix(ctx *models.ReqContext) (string, error) {
ds, err := r.DataProxy.DatasourceCache.GetDatasource(ctx.ParamsInt64("Recipient"), ctx.SignedInUser, ctx.SkipCache)
ds, err := r.DataProxy.DataSourceCache.GetDatasource(ctx.ParamsInt64("Recipient"), ctx.SignedInUser, ctx.SkipCache)
if err != nil {
return "", err
}

Some files were not shown because too many files have changed in this diff Show More