mysql> desc data_source;
+---------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+--------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| org_id | bigint(20) | NO | MUL | NULL | |
| version | int(11) | NO | | NULL | |
| type | varchar(255) | NO | | NULL | |
| name | varchar(190) | NO | | NULL | |
| access | varchar(255) | NO | | NULL | |
| url | varchar(255) | NO | | NULL | |
| password | varchar(255) | YES | | NULL | |
| user | varchar(255) | YES | | NULL | |
| database | varchar(255) | YES | | NULL | |
| basic_auth | tinyint(1) | NO | | NULL | |
| basic_auth_user | varchar(255) | YES | | NULL | |
| basic_auth_password | varchar(255) | YES | | NULL | |
| is_default | tinyint(1) | NO | | NULL | |
| json_data | text | YES | | NULL | |
| created | datetime | NO | | NULL | |
| updated | datetime | NO | | NULL | |
| with_credentials | tinyint(1) | NO | | 0 | |
| secure_json_data | text | YES | | NULL | |
| read_only | tinyint(1) | YES | | NULL | |
| uid | varchar(40) | NO | | 0 | |
+---------------------+--------------+------+-----+---------+----------------+
21 rows in set (0.00 sec)
(pkg/services/query/query.go
)
func (s *Service) getDataSourceFromQuery(ctx context.Context, user *models.SignedInUser, skipCache bool, query *simplejson.Json, history map[string]*models.DataSource) (*models.DataSource, error) {
var err error
uid := query.Get("datasource").Get("uid").MustString()
// before 8.3 special types could be sent as datasource (expr)
if uid == "" {
uid = query.Get("datasource").MustString()
}
// check cache value
ds, ok := history[uid]
if ok {
return ds, nil
}
if expr.IsDataSource(uid) {
return expr.DataSourceModel(), nil
}
if uid == grafanads.DatasourceUID {
return grafanads.DataSourceModel(user.OrgId), nil
}
// use datasourceId if it exists
id := query.Get("datasourceId").MustInt64(0)
if id > 0 {
ds, err = s.dataSourceCache.GetDatasource(ctx, id, user, skipCache)
if err != nil {
return nil, err
}
return ds, nil
}
if uid != "" {
ds, err = s.dataSourceCache.GetDatasourceByUID(ctx, uid, user, skipCache)
if err != nil {
return nil, err
}
return ds, nil
}
return nil, NewErrBadQuery("missing data source ID/UID")
}
type DataSource struct {
Id int64 `json:"id"`
UID string `json:"uid"`
OrgId int64 `json:"orgId"`
Name string `json:"name"`
Type string `json:"type"`
TypeLogoUrl string `json:"typeLogoUrl"`
Access models.DsAccess `json:"access"`
Url string `json:"url"`
Password string `json:"password"`
User string `json:"user"`
Database string `json:"database"`
BasicAuth bool `json:"basicAuth"`
BasicAuthUser string `json:"basicAuthUser"`
BasicAuthPassword string `json:"basicAuthPassword"`
WithCredentials bool `json:"withCredentials"`
IsDefault bool `json:"isDefault"`
JsonData *simplejson.Json `json:"jsonData,omitempty"`
SecureJsonFields map[string]bool `json:"secureJsonFields"`
Version int `json:"version"`
ReadOnly bool `json:"readOnly"`
AccessControl accesscontrol.Metadata `json:"accessControl,omitempty"`
}
type DataSourceListItemDTO struct {
Id int64 `json:"id"`
UID string `json:"uid"`
OrgId int64 `json:"orgId"`
Name string `json:"name"`
Type string `json:"type"`
TypeName string `json:"typeName"`
TypeLogoUrl string `json:"typeLogoUrl"`
Access models.DsAccess `json:"access"`
Url string `json:"url"`
Password string `json:"password"`
User string `json:"user"`
Database string `json:"database"`
BasicAuth bool `json:"basicAuth"`
IsDefault bool `json:"isDefault"`
JsonData *simplejson.Json `json:"jsonData,omitempty"`
ReadOnly bool `json:"readOnly"`
}
type DataSource struct {
Id int64 `json:"id"`
OrgId int64 `json:"orgId"`
Version int `json:"version"`
Name string `json:"name"`
Type string `json:"type"`
Access DsAccess `json:"access"`
Url string `json:"url"`
Password string `json:"password"`
User string `json:"user"`
Database string `json:"database"`
BasicAuth bool `json:"basicAuth"`
BasicAuthUser string `json:"basicAuthUser"`
BasicAuthPassword string `json:"basicAuthPassword"`
WithCredentials bool `json:"withCredentials"`
IsDefault bool `json:"isDefault"`
JsonData *simplejson.Json `json:"jsonData"`
SecureJsonData map[string][]byte `json:"secureJsonData"`
ReadOnly bool `json:"readOnly"`
Uid string `json:"uid"`
Created time.Time `json:"created"`
Updated time.Time `json:"updated"`
}
type DataSourceList []DataSourceListItemDTO
type DSInfo struct {
ID int64
Updated time.Time
JSONData map[string]interface{}
DecryptedSecureJSONData map[string]string
}
type DataSourceInstanceSettings struct {
// ID is the Grafana assigned numeric identifier of the the data source instance.
ID int64
// UID is the Grafana assigned string identifier of the the data source instance.
UID string
// Name is the configured name of the data source instance.
Name string
// URL is the configured URL of a data source instance (e.g. the URL of an API endpoint).
URL string
// User is a configured user for a data source instance. This is not a Grafana user, rather an arbitrary string.
User string
// Database is the configured database for a data source instance. (e.g. the default Database a SQL data source would connect to).
Database string
// BasicAuthEnabled indicates if this data source instance should use basic authentication.
BasicAuthEnabled bool
// BasicAuthUser is the configured user for basic authentication. (e.g. when a data source uses basic
// authentication to connect to whatever API it fetches data from).
BasicAuthUser string
// JSONData contains the raw DataSourceConfig as JSON as stored by Grafana server. It repeats the properties in
// this object and includes custom properties.
JSONData json.RawMessage
// DecryptedSecureJSONData contains key,value pairs where the encrypted configuration in Grafana server have been
// decrypted before passing them to the plugin.
DecryptedSecureJSONData map[string]string
// Updated is the last time the configuration for the data source instance was updated.
Updated time.Time
}
Protobuf
message DataSourceInstanceSettings {
int64 id = 1;
string name = 2;
string url = 3;
string user = 4;
string database = 5;
bool basicAuthEnabled = 6;
string basicAuthUser = 7;
bytes jsonData = 8;
map<string,string> decryptedSecureJsonData = 9;
int64 lastUpdatedMS = 10;
string uid = 11;
}
In SQLStore's AddDatasource
an event is published when a data source is added:
sess.publishAfterCommit(&events.DataSourceCreated{
Timestamp: time.Now(),
Name: cmd.Name,
ID: ds.Id,
UID: cmd.Uid,
OrgID: cmd.OrgId,
})
Then in SQLStore's inTransactionWithRetryCtx
the events are published to the bus:
if len(sess.events) > 0 {
for _, e := range sess.events {
if err = bus.Publish(ctx, e); err != nil {
tsclogger.Error("Failed to publish event after commit.", "error", err)
}
}
}
There is one Listener for this, it is in pkg/plugins/plugindashboards/service.go
bus.AddEventListener(s.handlePluginStateChanged)
func (s *Service) handlePluginStateChanged(ctx context.Context, event *models.PluginStateChangedEvent) error {
s.logger.Info("Plugin state changed", "pluginId", event.PluginId, "enabled", event.Enabled)
if event.Enabled {
p, exists := s.pluginStore.Plugin(ctx, event.PluginId)
if !exists {
return fmt.Errorf("plugin %s not found. Could not sync plugin dashboards", event.PluginId)
}
s.syncPluginDashboards(ctx, p, event.OrgId)
} else {
query := models.GetDashboardsByPluginIdQuery{PluginId: event.PluginId, OrgId: event.OrgId}
if err := bus.Dispatch(ctx, &query); err != nil {
return err
}
for _, dash := range query.Result {
s.logger.Info("Deleting plugin dashboard", "pluginId", event.PluginId, "dashboard", dash.Slug)
deleteCmd := models.DeleteDashboardCommand{OrgId: dash.OrgId, Id: dash.Id}
if err := bus.Dispatch(ctx, &deleteCmd); err != nil {
return err
}
}
}
return nil
}
Which seems to be for managing dashboards that are provided by plugins.
Recording rules in Enterprise also sets up a listener for datasource change events.