mirror of
https://github.com/grafana/grafana.git
synced 2024-11-24 09:50:29 -06:00
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:
parent
e61bc33163
commit
78596a6756
12
.bingo/.gitignore
vendored
Normal file
12
.bingo/.gitignore
vendored
Normal 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
14
.bingo/README.md
Normal 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
25
.bingo/Variables.mk
Normal 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
1
.bingo/go.mod
Normal 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
12
.bingo/variables.env
Normal 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
5
.bingo/wire.mod
Normal 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
|
@ -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"]
|
||||
|
49
.drone.yml
49
.drone.yml
@ -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
4
.gitignore
vendored
@ -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
|
||||
|
9
Makefile
9
Makefile
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
1
go.mod
@ -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
4
go.sum
@ -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=
|
||||
|
@ -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`.
|
||||
|
44
pkg/api/apierrors/folder.go
Normal file
44
pkg/api/apierrors/folder.go
Normal 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)
|
||||
}
|
@ -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: "a.QuotaService{
|
||||
Cfg: cfg,
|
||||
},
|
||||
AccessControl: accesscontrolmock.New().WithPermissions(permissions),
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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(®istry.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
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) Init() error {
|
||||
hs.log = log.New("http.server")
|
||||
type ServerOptions struct {
|
||||
Listener net.Listener
|
||||
}
|
||||
|
||||
hs.macaron = hs.newMacaron()
|
||||
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 := &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()
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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{
|
||||
prefix: "",
|
||||
routes: []route{},
|
||||
subfixHandlers: []macaron.Handler{},
|
||||
namedMiddleware: namedMiddleware,
|
||||
func NewRouteRegister(namedMiddlewares ...RegisterNamedMiddleware) *RouteRegisterImpl {
|
||||
return &RouteRegisterImpl{
|
||||
prefix: "",
|
||||
routes: []route{},
|
||||
subfixHandlers: []macaron.Handler{},
|
||||
namedMiddlewares: namedMiddlewares,
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,15 +72,15 @@ type route struct {
|
||||
handlers []macaron.Handler
|
||||
}
|
||||
|
||||
type routeRegister struct {
|
||||
prefix string
|
||||
subfixHandlers []macaron.Handler
|
||||
namedMiddleware []RegisterNamedMiddleware
|
||||
routes []route
|
||||
groups []*routeRegister
|
||||
type RouteRegisterImpl struct {
|
||||
prefix string
|
||||
subfixHandlers []macaron.Handler
|
||||
namedMiddlewares []RegisterNamedMiddleware
|
||||
routes []route
|
||||
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{
|
||||
prefix: rr.prefix + pattern,
|
||||
subfixHandlers: append(rr.subfixHandlers, handlers...),
|
||||
routes: []route{},
|
||||
namedMiddleware: rr.namedMiddleware,
|
||||
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{},
|
||||
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...)
|
||||
}
|
||||
|
@ -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{
|
||||
logger: log.New("bus"),
|
||||
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() {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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{
|
||||
|
@ -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 {
|
||||
|
@ -27,13 +27,18 @@ const (
|
||||
ServiceName = "RemoteCache"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rc := &RemoteCache{}
|
||||
registry.Register(®istry.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)
|
||||
|
@ -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{
|
||||
RemoteCacheOptions: opts,
|
||||
},
|
||||
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")
|
||||
|
@ -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{
|
||||
RemoteCacheOptions: opts,
|
||||
},
|
||||
}
|
||||
|
||||
err := dc.Init()
|
||||
dc, err := ProvideService(&setting.Cfg{
|
||||
RemoteCacheOptions: opts,
|
||||
}, sqlStore)
|
||||
require.NoError(t, err, "Failed to init remote cache for test")
|
||||
|
||||
return dc
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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{
|
||||
logger: log.New("plugins.backend"),
|
||||
plugins: map[string]backendplugin.Plugin{},
|
||||
}, registry.MediumHigh)
|
||||
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{},
|
||||
}
|
||||
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
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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(®istry.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,
|
||||
dataSources: map[string]*plugins.DataSourcePlugin{},
|
||||
plugins: map[string]*plugins.PluginBase{},
|
||||
panels: map[string]*plugins.PanelPlugin{},
|
||||
apps: map[string]*plugins.AppPlugin{},
|
||||
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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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(®istry.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)
|
||||
}
|
||||
|
@ -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(®istry.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")
|
||||
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
)
|
||||
|
76
pkg/server/backgroundsvcs/background_services.go
Normal file
76
pkg/server/backgroundsvcs/background_services.go
Normal 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
|
||||
}
|
@ -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{
|
||||
context: childCtx,
|
||||
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,
|
||||
s := &Server{
|
||||
context: childCtx,
|
||||
childRoutines: childRoutines,
|
||||
HTTPServer: httpServer,
|
||||
provisioningService: provisioningService,
|
||||
roleRegistry: roleRegistry,
|
||||
shutdownFn: shutdownFn,
|
||||
shutdownFinished: make(chan struct{}),
|
||||
log: log.New("server"),
|
||||
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
|
||||
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")
|
||||
|
@ -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
|
||||
started chan struct{}
|
||||
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,
|
||||
started: make(chan struct{}),
|
||||
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
12
pkg/server/test_env.go
Normal 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
159
pkg/server/wire.go
Normal 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
|
||||
}
|
56
pkg/server/wireexts_oss.go
Normal file
56
pkg/server/wireexts_oss.go
Normal 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,
|
||||
)
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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{
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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{}
|
||||
|
@ -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
|
||||
|
@ -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(®istry.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) {
|
||||
|
@ -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(®istry.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")
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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(®istry.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.
|
||||
|
@ -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)
|
||||
|
@ -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(®istry.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(
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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{}},
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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{}},
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 ®istry.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)
|
||||
|
@ -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{},
|
||||
channels: make(map[string]models.ChannelHandler),
|
||||
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)
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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(®istry.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) {
|
||||
|
@ -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++ {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user