The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/pkg/tsdb/grafanads/grafana.go

236 lines
6.0 KiB

package grafanads
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana-plugin-sdk-go/experimental"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
Plugins: Refactor Plugin Management (#40477) * add core plugin flow * add instrumentation * move func * remove cruft * support external backend plugins * refactor + clean up * remove comments * refactor loader * simplify core plugin path arg * cleanup loggers * move signature validator to plugins package * fix sig packaging * cleanup plugin model * remove unnecessary plugin field * add start+stop for pm * fix failures * add decommissioned state * export fields just to get things flowing * fix comments * set static routes * make image loading idempotent * merge with backend plugin manager * re-use funcs * reorder imports + remove unnecessary interface * add some TODOs + remove unused func * remove unused instrumentation func * simplify client usage * remove import alias * re-use backendplugin.Plugin interface * re order funcs * improve var name * fix log statements * refactor data model * add logic for dupe check during loading * cleanup state setting * refactor loader * cleanup manager interface * add rendering flow * refactor loading + init * add renderer support * fix renderer plugin * reformat imports * track errors * fix plugin signature inheritance * name param in interface * update func comment * fix func arg name * introduce class concept * remove func * fix external plugin check * apply changes from pm-experiment * fix core plugins * fix imports * rename interface * comment API interface * add support for testdata plugin * enable alerting + use correct core plugin contracts * slim manager API * fix param name * fix filter * support static routes * fix rendering * tidy rendering * get tests compiling * fix install+uninstall * start finder test * add finder test coverage * start loader tests * add test for core plugins * load core + bundled test * add test for nested plugin loading * add test files * clean interface + fix registering some core plugins * refactoring * reformat and create sub packages * simplify core plugin init * fix ctx cancel scenario * migrate initializer * remove Init() funcs * add test starter * new logger * flesh out initializer tests * refactoring * remove unused svc * refactor rendering flow * fixup loader tests * add enabled helper func * fix logger name * fix data fetchers * fix case where plugin dir doesn't exist * improve coverage + move dupe checking to loader * remove noisy debug logs * register core plugins automagically * add support for renderer in catalog * make private func + fix req validation * use interface * re-add check for renderer in catalog * tidy up from moving to auto reg core plugins * core plugin registrar * guards * copy over core plugins for test infra * all tests green * renames * propagate new interfaces * kill old manager * get compiling * tidy up * update naming * refactor manager test + cleanup * add more cases to finder test * migrate validator to field * more coverage * refactor dupe checking * add test for plugin class * add coverage for initializer * split out rendering * move * fixup tests * fix uss test * fix frontend settings * fix grafanads test * add check when checking sig errors * fix enabled map * fixup * allow manual setup of CM * rename to cloud-monitoring * remove TODO * add installer interface for testing * loader interface returns * tests passing * refactor + add more coverage * support 'stackdriver' * fix frontend settings loading * improve naming based on package name * small tidy * refactor test * fix renderer start * make cloud-monitoring plugin ID clearer * add plugin update test * add integration tests * don't break all if sig can't be calculated * add root URL check test * add more signature verification tests * update DTO name * update enabled plugins comment * update comments * fix linter * revert fe naming change * fix errors endpoint * reset error code field name * re-order test to help verify * assert -> require * pm check * add missing entry + re-order * re-check * dump icon log * verify manager contents first * reformat * apply PR feedback * apply style changes * fix one vs all loading err * improve log output * only start when no signature error * move log * rework plugin update check * fix test * fix multi loading from cfg.PluginSettings * improve log output #2 * add error abstraction to capture errors without registering a plugin * add debug log * add unsigned warning * e2e test attempt * fix logger * set home path * prevent panic * alternate * ugh.. fix home path * return renderer even if not started * make renderer plugin managed * add fallback renderer icon, update renderer badge + prevent changes when renderer is installed * fix icon loading * rollback renderer changes * use correct field * remove unneccessary block * remove newline * remove unused func * fix bundled plugins base + module fields * remove unused field since refactor * add authorizer abstraction * loader only returns plugins expected to run * fix multi log output
4 years ago
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/searchV2"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/tsdb/testdatasource"
)
// DatasourceName is the string constant used as the datasource name in requests
// to identify it as a Grafana DS command.
const DatasourceName = "-- Grafana --"
// DatasourceID is the fake datasource id used in requests to identify it as a
// Grafana DS command.
const DatasourceID = -1
// DatasourceUID is the fake datasource uid used in requests to identify it as a
// Grafana DS command.
const DatasourceUID = "grafana"
// Make sure Service implements required interfaces.
// This is important to do since otherwise we will only get a
// not implemented error response from plugin at runtime.
var (
_ backend.QueryDataHandler = (*Service)(nil)
_ backend.CheckHealthHandler = (*Service)(nil)
logger = log.New("tsdb.grafana")
)
func ProvideService(cfg *setting.Cfg, search searchV2.SearchService) *Service {
return newService(cfg, search)
}
func newService(cfg *setting.Cfg, search searchV2.SearchService) *Service {
s := &Service{
staticRootPath: cfg.StaticRootPath,
roots: []string{
"testdata",
"img/icons",
"img/bg",
"gazetteer",
"maps",
"upload", // does not exist yet
},
search: search,
}
return s
}
// Service exists regardless of user settings
type Service struct {
// path to the public folder
staticRootPath string
roots []string
search searchV2.SearchService
}
func DataSourceModel(orgId int64) *models.DataSource {
return &models.DataSource{
Id: DatasourceID,
Uid: DatasourceUID,
Name: DatasourceName,
Type: "grafana",
OrgId: orgId,
JsonData: simplejson.New(),
Encryption: Refactor securejsondata.SecureJsonData to stop relying on global functions (#38865) * Encryption: Add support to encrypt/decrypt sjd * Add datasources.Service as a proxy to datasources db operations * Encrypt ds.SecureJsonData before calling SQLStore * Move ds cache code into ds service * Fix tlsmanager tests * Fix pluginproxy tests * Remove some securejsondata.GetEncryptedJsonData usages * Add pluginsettings.Service as a proxy for plugin settings db operations * Add AlertNotificationService as a proxy for alert notification db operations * Remove some securejsondata.GetEncryptedJsonData usages * Remove more securejsondata.GetEncryptedJsonData usages * Fix lint errors * Minor fixes * Remove encryption global functions usages from ngalert * Fix lint errors * Minor fixes * Minor fixes * Remove securejsondata.DecryptedValue usage * Refactor the refactor * Remove securejsondata.DecryptedValue usage * Move securejsondata to migrations package * Move securejsondata to migrations package * Minor fix * Fix integration test * Fix integration tests * Undo undesired changes * Fix tests * Add context.Context into encryption methods * Fix tests * Fix tests * Fix tests * Trigger CI * Fix test * Add names to params of encryption service interface * Remove bus from CacheServiceImpl * Add logging * Add keys to logger Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com> * Add missing key to logger Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com> * Undo changes in markdown files * Fix formatting * Add context to secrets service * Rename decryptSecureJsonData to decryptSecureJsonDataFn * Name args in GetDecryptedValueFn * Add template back to NewAlertmanagerNotifier * Copy GetDecryptedValueFn to ngalert * Add logging to pluginsettings * Fix pluginsettings test Co-authored-by: Tania B <yalyna.ts@gmail.com> Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
4 years ago
SecureJsonData: make(map[string][]byte),
}
}
func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
response := backend.NewQueryDataResponse()
for _, q := range req.Queries {
switch q.QueryType {
case queryTypeRandomWalk:
response.Responses[q.RefID] = s.doRandomWalk(q)
case queryTypeList:
response.Responses[q.RefID] = s.doListQuery(q)
case queryTypeRead:
response.Responses[q.RefID] = s.doReadQuery(q)
case queryTypeSearch:
response.Responses[q.RefID] = s.doSearchQuery(ctx, req, q)
default:
response.Responses[q.RefID] = backend.DataResponse{
Error: fmt.Errorf("unknown query type"),
}
}
}
return response, nil
}
func (s *Service) CheckHealth(_ context.Context, _ *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
return &backend.CheckHealthResult{
Status: backend.HealthStatusOk,
Message: "OK",
}, nil
}
func (s *Service) publicPath(path string) (string, error) {
if strings.Contains(path, "..") {
return "", fmt.Errorf("invalid string")
}
ok := false
for _, root := range s.roots {
if strings.HasPrefix(path, root) {
ok = true
break
}
}
if !ok {
return "", fmt.Errorf("bad root path")
}
return filepath.Join(s.staticRootPath, path), nil
}
func (s *Service) doListQuery(query backend.DataQuery) backend.DataResponse {
q := &listQueryModel{}
response := backend.DataResponse{}
err := json.Unmarshal(query.JSON, &q)
if err != nil {
response.Error = err
return response
}
if q.Path == "" {
count := len(s.roots)
names := data.NewFieldFromFieldType(data.FieldTypeString, count)
mtype := data.NewFieldFromFieldType(data.FieldTypeString, count)
names.Name = "name"
mtype.Name = "mediaType"
for i, f := range s.roots {
names.Set(i, f)
mtype.Set(i, "directory")
}
frame := data.NewFrame("", names, mtype)
frame.SetMeta(&data.FrameMeta{
Type: data.FrameTypeDirectoryListing,
})
response.Frames = data.Frames{frame}
} else {
path, err := s.publicPath(q.Path)
if err != nil {
response.Error = err
return response
}
frame, err := experimental.GetDirectoryFrame(path, false)
if err != nil {
response.Error = err
return response
}
response.Frames = data.Frames{frame}
}
return response
}
func (s *Service) doReadQuery(query backend.DataQuery) backend.DataResponse {
q := &listQueryModel{}
response := backend.DataResponse{}
err := json.Unmarshal(query.JSON, &q)
if err != nil {
response.Error = err
return response
}
if filepath.Ext(q.Path) != ".csv" {
response.Error = fmt.Errorf("unsupported file type")
return response
}
path, err := s.publicPath(q.Path)
if err != nil {
response.Error = err
return response
}
// Can ignore gosec G304 here, because we check the file pattern above
// nolint:gosec
fileReader, err := os.Open(path)
if err != nil {
response.Error = fmt.Errorf("failed to read file")
return response
}
defer func() {
if err := fileReader.Close(); err != nil {
logger.Warn("Failed to close file", "err", err, "path", path)
}
}()
frame, err := testdatasource.LoadCsvContent(fileReader, filepath.Base(path))
if err != nil {
response.Error = err
return response
}
response.Frames = data.Frames{frame}
return response
}
func (s *Service) doRandomWalk(query backend.DataQuery) backend.DataResponse {
response := backend.DataResponse{}
model := simplejson.New()
response.Frames = data.Frames{testdatasource.RandomWalk(query, model, 0)}
return response
}
func (s *Service) doSearchQuery(ctx context.Context, req *backend.QueryDataRequest, query backend.DataQuery) backend.DataResponse {
q := searchV2.DashboardQuery{}
err := json.Unmarshal(query.JSON, &q)
if err != nil {
return backend.DataResponse{
Error: err,
}
}
return *s.search.DoDashboardQuery(ctx, req.PluginContext.User, req.PluginContext.OrgID, q)
}