Skip to content

Instantly share code, notes, and snippets.

@kylebrandt
Last active January 26, 2022 13:58
Show Gist options
  • Save kylebrandt/e6f38b6421a17ee8d0d64ce5703b2721 to your computer and use it in GitHub Desktop.
Save kylebrandt/e6f38b6421a17ee8d0d64ce5703b2721 to your computer and use it in GitHub Desktop.
Datasource Object Notes

Datasource Object Variations (Backend)

SQL Table (MySQL)

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)

Datasource Reference Included in API Queries

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

API/dtos (/pkg/api/dtos/datasource.go)

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"`
}

Models (pkg/models/datasource.go)

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

Plugin Proxy (/pkg/pluginproxy/ds_auth_provider.go)

type DSInfo struct {
    ID                      int64
    Updated                 time.Time
    JSONData                map[string]interface{}
    DecryptedSecureJSONData map[string]string
}

SDK backend Package

Permalink to below code

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

permalink

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

Events

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment