Skip to content

Commit fbf694f

Browse files
authored
Add more context handling (#139)
* chore: finish adding context handling throughout
1 parent de7b382 commit fbf694f

File tree

9 files changed

+114
-79
lines changed

9 files changed

+114
-79
lines changed

.drone.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,17 @@ steps:
1313
- name: build
1414
image: grafana/grafana-plugin-ci:1.9.5
1515
commands:
16-
- mage -v build
16+
- mage --keep -v build
1717

1818
- name: lint
1919
image: grafana/grafana-plugin-ci:1.9.5
2020
commands:
21-
- mage -v lint
21+
- mage --keep -v lint
2222

2323
- name: test
2424
image: grafana/grafana-plugin-ci:1.9.5
2525
commands:
26-
- mage -v test
26+
- mage --keep -v test
2727

2828
- name: vuln check
2929
image: golang:1.22

pkg/awsds/asyncDatasource.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ func isAsyncFlow(query backend.DataQuery) bool {
7777
}
7878

7979
func (ds *AsyncAWSDatasource) NewDatasource(ctx context.Context, settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
80-
db, err := ds.driver.GetAsyncDB(settings, nil)
80+
db, err := ds.driver.GetAsyncDB(ctx, settings, nil)
8181
if err != nil {
8282
return nil, err
8383
}
@@ -153,7 +153,7 @@ func (ds *AsyncAWSDatasource) CheckHealth(ctx context.Context, req *backend.Chec
153153
}, nil
154154
}
155155

156-
func (ds *AsyncAWSDatasource) getAsyncDBFromQuery(q *AsyncQuery, datasourceUID string) (AsyncDB, error) {
156+
func (ds *AsyncAWSDatasource) getAsyncDBFromQuery(ctx context.Context, q *AsyncQuery, datasourceUID string) (AsyncDB, error) {
157157
if !ds.EnableMultipleConnections && len(q.ConnectionArgs) > 0 {
158158
return nil, sqlds.ErrorMissingMultipleConnectionsConfig
159159
}
@@ -174,7 +174,7 @@ func (ds *AsyncAWSDatasource) getAsyncDBFromQuery(q *AsyncQuery, datasourceUID s
174174
}
175175

176176
var err error
177-
db, err := ds.driver.GetAsyncDB(dbConn.settings, q.ConnectionArgs)
177+
db, err := ds.driver.GetAsyncDB(ctx, dbConn.settings, q.ConnectionArgs)
178178
if err != nil {
179179
return nil, err
180180
}
@@ -211,7 +211,7 @@ func (ds *AsyncAWSDatasource) handleAsyncQuery(ctx context.Context, req backend.
211211
fillMode = q.FillMissing
212212
}
213213

214-
asyncDB, err := ds.getAsyncDBFromQuery(q, datasourceUID)
214+
asyncDB, err := ds.getAsyncDBFromQuery(ctx, q, datasourceUID)
215215
if err != nil {
216216
return getErrorFrameFromQuery(q), err
217217
}

pkg/awsds/asyncDatasource_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ type fakeDriver struct {
4242
AsyncDriver
4343
}
4444

45-
func (d fakeDriver) GetAsyncDB(backend.DataSourceInstanceSettings, json.RawMessage) (db AsyncDB, err error) {
45+
func (d fakeDriver) GetAsyncDB(context.Context, backend.DataSourceInstanceSettings, json.RawMessage) (db AsyncDB, err error) {
4646
return d.openDBfn()
4747
}
4848

@@ -96,7 +96,7 @@ func Test_getDBConnectionFromQuery(t *testing.T) {
9696
ds.storeDBConnection(key, dbConnection{tt.existingDB, settings})
9797
}
9898

99-
dbConn, err := ds.getAsyncDBFromQuery(&AsyncQuery{Query: sqlutil.Query{ConnectionArgs: json.RawMessage(tt.args)}}, tt.dsUID)
99+
dbConn, err := ds.getAsyncDBFromQuery(context.Background(), &AsyncQuery{Query: sqlutil.Query{ConnectionArgs: json.RawMessage(tt.args)}}, tt.dsUID)
100100
if err != nil {
101101
t.Fatalf("unexpected error %v", err)
102102
}

pkg/awsds/types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,5 +116,5 @@ type AsyncDB interface {
116116
// AsyncDriver extends the driver interface to also connect to async SQL datasources
117117
type AsyncDriver interface {
118118
sqlds.Driver
119-
GetAsyncDB(settings backend.DataSourceInstanceSettings, queryArgs json.RawMessage) (AsyncDB, error)
119+
GetAsyncDB(ctx context.Context, settings backend.DataSourceInstanceSettings, queryArgs json.RawMessage) (AsyncDB, error)
120120
}

pkg/sql/api/api.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77

88
"github.com/aws/aws-sdk-go/aws"
99
"github.com/grafana/grafana-aws-sdk/pkg/awsds"
10-
"github.com/grafana/grafana-aws-sdk/pkg/sql/models"
1110
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
1211
"github.com/grafana/sqlds/v3"
1312
"github.com/jpillora/backoff"
@@ -54,8 +53,6 @@ type AWSAPI interface {
5453
Resources
5554
}
5655

57-
type Loader func(cache *awsds.SessionCache, settings models.Settings) (AWSAPI, error)
58-
5956
// WaitOnQuery polls the datasource api until the query finishes, returning an error if it failed.
6057
func WaitOnQuery(ctx context.Context, api SQL, output *ExecuteQueryOutput) error {
6158
backoffInstance := backoff.Backoff{

pkg/sql/api/api_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func TestWaitOnQuery(t *testing.T) {
7272

7373
for _, tc := range tests {
7474
t.Run(tc.description, func(t *testing.T) {
75-
err := WaitOnQuery(context.TODO(), tc.ds, &ExecuteQueryOutput{})
75+
err := WaitOnQuery(context.Background(), tc.ds, &ExecuteQueryOutput{})
7676
if tc.ds.statusCounter != len(tc.ds.status) {
7777
t.Errorf("status not called the right amount of times. Want %d got %d", len(tc.ds.status), tc.ds.statusCounter)
7878
}

pkg/sql/datasource/datasource.go

Lines changed: 49 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package datasource
22

33
import (
4+
"context"
45
"database/sql"
56
"fmt"
67
"sync"
@@ -14,28 +15,45 @@ import (
1415
"github.com/grafana/sqlds/v3"
1516
)
1617

17-
// AWSDatasource stores a cache of several instances.
18+
// AWSClient provides creation and caching of sessions, database connections, and API clients
19+
type AWSClient interface {
20+
Init(config backend.DataSourceInstanceSettings)
21+
GetDB(ctx context.Context, id int64, options sqlds.Options) (*sql.DB, error)
22+
GetAsyncDB(ctx context.Context, id int64, options sqlds.Options) (awsds.AsyncDB, error)
23+
GetAPI(ctx context.Context, id int64, options sqlds.Options) (api.AWSAPI, error)
24+
}
25+
26+
type Loader interface {
27+
LoadSettings(context.Context) models.Settings
28+
LoadAPI(context.Context, *awsds.SessionCache, models.Settings) (api.AWSAPI, error)
29+
LoadDriver(context.Context, api.AWSAPI) (driver.Driver, error)
30+
LoadAsyncDriver(context.Context, api.AWSAPI) (asyncDriver.Driver, error)
31+
}
32+
33+
// awsClient provides creation and caching of several types of instances.
1834
// Each Map will depend on the datasource ID (and connection options):
1935
// - sessionCache: AWS cache. This is not a Map since it does not depend on the datasource.
2036
// - config: Base configuration. It will be used as base to populate datasource settings.
2137
// It does not depend on connection options (only one per datasource)
2238
// - api: API instance with the common methods to contact the data source API.
23-
type AWSDatasource struct {
39+
type awsClient struct {
2440
sessionCache *awsds.SessionCache
2541
config sync.Map
2642
api sync.Map
43+
44+
loader Loader
2745
}
2846

29-
func New() *AWSDatasource {
30-
ds := &AWSDatasource{sessionCache: awsds.NewSessionCache()}
47+
func New(loader Loader) AWSClient {
48+
ds := &awsClient{sessionCache: awsds.NewSessionCache(), loader: loader}
3149
return ds
3250
}
3351

34-
func (ds *AWSDatasource) storeConfig(config backend.DataSourceInstanceSettings) {
52+
func (ds *awsClient) storeConfig(config backend.DataSourceInstanceSettings) {
3553
ds.config.Store(config.ID, config)
3654
}
3755

38-
func (ds *AWSDatasource) createDB(dr driver.Driver) (*sql.DB, error) {
56+
func (ds *awsClient) createDB(dr driver.Driver) (*sql.DB, error) {
3957
db, err := dr.OpenDB()
4058
if err != nil {
4159
return nil, fmt.Errorf("%w: failed to connect to database (check hostname and port?)", err)
@@ -44,7 +62,7 @@ func (ds *AWSDatasource) createDB(dr driver.Driver) (*sql.DB, error) {
4462
return db, nil
4563
}
4664

47-
func (ds *AWSDatasource) createAsyncDB(dr asyncDriver.Driver) (awsds.AsyncDB, error) {
65+
func (ds *awsClient) createAsyncDB(dr asyncDriver.Driver) (awsds.AsyncDB, error) {
4866
db, err := dr.GetAsyncDB()
4967
if err != nil {
5068
return nil, fmt.Errorf("%w: failed to connect to database (check hostname and port)", err)
@@ -53,12 +71,12 @@ func (ds *AWSDatasource) createAsyncDB(dr asyncDriver.Driver) (awsds.AsyncDB, er
5371
return db, nil
5472
}
5573

56-
func (ds *AWSDatasource) storeAPI(id int64, args sqlds.Options, dsAPI api.AWSAPI) {
74+
func (ds *awsClient) storeAPI(id int64, args sqlds.Options, dsAPI api.AWSAPI) {
5775
key := connectionKey(id, args)
5876
ds.api.Store(key, dsAPI)
5977
}
6078

61-
func (ds *AWSDatasource) loadAPI(id int64, args sqlds.Options) (api.AWSAPI, bool) {
79+
func (ds *awsClient) loadAPI(id int64, args sqlds.Options) (api.AWSAPI, bool) {
6280
key := connectionKey(id, args)
6381
dsAPI, exists := ds.api.Load(key)
6482
if exists {
@@ -67,34 +85,34 @@ func (ds *AWSDatasource) loadAPI(id int64, args sqlds.Options) (api.AWSAPI, bool
6785
return nil, false
6886
}
6987

70-
func (ds *AWSDatasource) createAPI(id int64, args sqlds.Options, settings models.Settings, loader api.Loader) (api.AWSAPI, error) {
71-
dsAPI, err := loader(ds.sessionCache, settings)
88+
func (ds *awsClient) createAPI(ctx context.Context, id int64, args sqlds.Options, settings models.Settings) (api.AWSAPI, error) {
89+
dsAPI, err := ds.loader.LoadAPI(ctx, ds.sessionCache, settings)
7290
if err != nil {
7391
return nil, fmt.Errorf("%w: Failed to create client", err)
7492
}
7593
ds.storeAPI(id, args, dsAPI)
7694
return dsAPI, err
7795
}
7896

79-
func (ds *AWSDatasource) createDriver(dsAPI api.AWSAPI, loader driver.Loader) (driver.Driver, error) {
80-
dr, err := loader(dsAPI)
97+
func (ds *awsClient) createDriver(ctx context.Context, dsAPI api.AWSAPI) (driver.Driver, error) {
98+
dr, err := ds.loader.LoadDriver(ctx, dsAPI)
8199
if err != nil {
82100
return nil, fmt.Errorf("%w: Failed to create client", err)
83101
}
84102

85103
return dr, nil
86104
}
87105

88-
func (ds *AWSDatasource) createAsyncDriver(dsAPI api.AWSAPI, loader asyncDriver.Loader) (asyncDriver.Driver, error) {
89-
dr, err := loader(dsAPI)
106+
func (ds *awsClient) createAsyncDriver(ctx context.Context, dsAPI api.AWSAPI) (asyncDriver.Driver, error) {
107+
dr, err := ds.loader.LoadAsyncDriver(ctx, dsAPI)
90108
if err != nil {
91109
return nil, fmt.Errorf("%w: Failed to create client", err)
92110
}
93111

94112
return dr, nil
95113
}
96114

97-
func (ds *AWSDatasource) parseSettings(id int64, args sqlds.Options, settings models.Settings) error {
115+
func (ds *awsClient) parseSettings(id int64, args sqlds.Options, settings models.Settings) error {
98116
config, ok := ds.config.Load(id)
99117
if !ok {
100118
return fmt.Errorf("unable to find stored configuration for datasource %d. Initialize it first", id)
@@ -108,31 +126,29 @@ func (ds *AWSDatasource) parseSettings(id int64, args sqlds.Options, settings mo
108126
}
109127

110128
// Init stores the data source configuration. It's needed for the GetDB and GetAPI functions
111-
func (ds *AWSDatasource) Init(config backend.DataSourceInstanceSettings) {
129+
func (ds *awsClient) Init(config backend.DataSourceInstanceSettings) {
112130
ds.storeConfig(config)
113131
}
114132

115133
// GetDB returns a *sql.DB. It will use the loader functions to initialize the required
116134
// settings, API and driver and finally create a DB.
117-
func (ds *AWSDatasource) GetDB(
135+
func (ds *awsClient) GetDB(
136+
ctx context.Context,
118137
id int64,
119138
options sqlds.Options,
120-
settingsLoader models.Loader,
121-
apiLoader api.Loader,
122-
driverLoader driver.Loader,
123139
) (*sql.DB, error) {
124-
settings := settingsLoader()
140+
settings := ds.loader.LoadSettings(ctx)
125141
err := ds.parseSettings(id, options, settings)
126142
if err != nil {
127143
return nil, err
128144
}
129145

130-
dsAPI, err := ds.createAPI(id, options, settings, apiLoader)
146+
dsAPI, err := ds.createAPI(ctx, id, options, settings)
131147
if err != nil {
132148
return nil, err
133149
}
134150

135-
dr, err := ds.createDriver(dsAPI, driverLoader)
151+
dr, err := ds.createDriver(ctx, dsAPI)
136152
if err != nil {
137153
return nil, err
138154
}
@@ -142,25 +158,23 @@ func (ds *AWSDatasource) GetDB(
142158

143159
// GetAsyncDB returns a sqlds.AsyncDB. It will use the loader functions to initialize the required
144160
// settings, API and driver and finally create a DB.
145-
func (ds *AWSDatasource) GetAsyncDB(
161+
func (ds *awsClient) GetAsyncDB(
162+
ctx context.Context,
146163
id int64,
147164
options sqlds.Options,
148-
settingsLoader models.Loader,
149-
apiLoader api.Loader,
150-
driverLoader asyncDriver.Loader,
151165
) (awsds.AsyncDB, error) {
152-
settings := settingsLoader()
166+
settings := ds.loader.LoadSettings(ctx)
153167
err := ds.parseSettings(id, options, settings)
154168
if err != nil {
155169
return nil, err
156170
}
157171

158-
dsAPI, err := ds.createAPI(id, options, settings, apiLoader)
172+
dsAPI, err := ds.createAPI(ctx, id, options, settings)
159173
if err != nil {
160174
return nil, err
161175
}
162176

163-
dr, err := ds.createAsyncDriver(dsAPI, driverLoader)
177+
dr, err := ds.createAsyncDriver(ctx, dsAPI)
164178
if err != nil {
165179
return nil, err
166180
}
@@ -171,22 +185,21 @@ func (ds *AWSDatasource) GetAsyncDB(
171185
// GetAPI returns an API interface. When called multiple times with the same id and options, it
172186
// will return a cached version of the API. The first time, it will use the loader
173187
// functions to initialize the required settings and API.
174-
func (ds *AWSDatasource) GetAPI(
188+
func (ds *awsClient) GetAPI(
189+
ctx context.Context,
175190
id int64,
176191
options sqlds.Options,
177-
settingsLoader models.Loader,
178-
apiLoader api.Loader,
179192
) (api.AWSAPI, error) {
180193
cachedAPI, exists := ds.loadAPI(id, options)
181194
if exists {
182195
return cachedAPI, nil
183196
}
184197

185198
// create new api
186-
settings := settingsLoader()
199+
settings := ds.loader.LoadSettings(ctx)
187200
err := ds.parseSettings(id, options, settings)
188201
if err != nil {
189202
return nil, err
190203
}
191-
return ds.createAPI(id, options, settings, apiLoader)
204+
return ds.createAPI(ctx, id, options, settings)
192205
}

0 commit comments

Comments
 (0)