Skip to content

Commit

Permalink
Merge pull request #99 from questdb/remove-tls-dropdown
Browse files Browse the repository at this point in the history
Remove `TLS Method` configuration option
  • Loading branch information
insmac authored Jun 6, 2024
2 parents 2882a61 + 6d8212c commit 8ba8ae1
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 133 deletions.
45 changes: 24 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,17 @@ example, statements like `UPDATE users SET name='blahblah'`
and `DROP TABLE importantTable;` would be executed.

To configure a readonly user, follow these steps:
* Open Source version

- Open Source version
1. Set the following properties in server.conf file:
- pg.readonly.user.enabled=true
- pg.readonly.user=myuser
- pg.readonly.password=secret
2. Restart QuestDB instance.
* Enterprise version
- Enterprise version
1. Create user:
- CREATE USER grafana_readonly;
2. Grant read permission on selected tables/table columns ;
2. Grant read permission on selected tables/table columns ;
- GRANT SELECT ON table1, ... TO grafana_readonly;

### Manual configuration
Expand Down Expand Up @@ -58,7 +59,6 @@ datasources:
port: 8812
username: admin
tlsMode: disable
# tlsConfigurationMethod: file-path | file-content
# tlsCACertFile: <string>
# timeout: <seconds>
# queryTimeout: <seconds>
Expand Down Expand Up @@ -88,11 +88,13 @@ interprets timestamp rows without explicit time zone as UTC. Any column except

To create multi-line time series, the query must return at least 3 fields in
the following order:
- field 1: `timestamp` field with an alias of `time`
- field 2: value to group by

- field 1: `timestamp` field with an alias of `time`
- field 2: value to group by
- field 3+: the metric values

For example:

```sql
SELECT pickup_datetime AS time, cab_type, avg(fare_amount) AS avg_fare_amount
FROM trips
Expand All @@ -109,24 +111,25 @@ Table visualizations will always be available for any valid QuestDB query.
To simplify syntax and to allow for dynamic parts, like date range filters, the query can contain macros.

Here is an example of a query with a macro that will use Grafana's time filter:

```sql
SELECT desginated_timestamp, data_stuff
FROM test_data
WHERE $__timeFilter(desginated_timestamp)
```

| Macro | Description | Output example |
|----------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------|
| *$__timeFilter(columnName)* | Replaced by a conditional that filters the data (using the provided column) based on the time range of the panel in seconds | `timestamp >= cast(1706263425598000 as timestamp) AND timestamp <= cast(1706285057560000 as timestamp)` |
| *$__fromTime* | Replaced by the starting time of the range of the panel cast to timestamp | `cast(1706263425598000 as timestamp)` |
| *$__toTime* | Replaced by the ending time of the range of the panel cast to timestamp | `cast(1706285057560000 as timestamp)` |
| *$__sampleByInterval* | Replaced by the interval followed by unit: d, h, s or T (millisecond). Example: 1d, 5h, 20s, 1T. | `20s` (20 seconds) , `1T` (1 millisecond) |
| *$__conditionalAll(condition, $templateVar)* | Replaced by the first parameter when the template variable in the second parameter does not select every value. Replaced by the 1=1 when the template variable selects every value. | `condition` or `1=1` |
| Macro | Description | Output example |
| ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| _$\_\_timeFilter(columnName)_ | Replaced by a conditional that filters the data (using the provided column) based on the time range of the panel in seconds | `timestamp >= cast(1706263425598000 as timestamp) AND timestamp <= cast(1706285057560000 as timestamp)` |
| _$\_\_fromTime_ | Replaced by the starting time of the range of the panel cast to timestamp | `cast(1706263425598000 as timestamp)` |
| _$\_\_toTime_ | Replaced by the ending time of the range of the panel cast to timestamp | `cast(1706285057560000 as timestamp)` |
| _$\_\_sampleByInterval_ | Replaced by the interval followed by unit: d, h, s or T (millisecond). Example: 1d, 5h, 20s, 1T. | `20s` (20 seconds) , `1T` (1 millisecond) |
| _$\_\_conditionalAll(condition, $templateVar)_ | Replaced by the first parameter when the template variable in the second parameter does not select every value. Replaced by the 1=1 when the template variable selects every value. | `condition` or `1=1` |

The plugin also supports notation using braces {}. Use this notation when queries are needed inside parameters.

Additionally, Grafana has the built-in [`$__interval` macro][query-transform-data-query-options], which calculates an interval in seconds or milliseconds.
It shouldn't be used with SAMPLE BY because of time unit incompatibility, 1ms vs 1T (expected by QuestDB). Use `$__sampleByInterval` instead.
Additionally, Grafana has the built-in [`$__interval` macro][query-transform-data-query-options], which calculates an interval in seconds or milliseconds.
It shouldn't be used with SAMPLE BY because of time unit incompatibility, 1ms vs 1T (expected by QuestDB). Use `$__sampleByInterval` instead.

### Templates and variables

Expand All @@ -144,12 +147,12 @@ Ad hoc filters allow you to add key/value filters that are automatically added
to all metric queries that use the specified data source, without being
explicitly used in queries.

By default, Ad Hoc filters will be populated with all Tables and Columns. If
By default, Ad Hoc filters will be populated with all Tables and Columns. If
you have a default database defined in the Datasource settings, all Tables from
that database will be used to populate the filters. As this could be
slow/expensive, you can introduce a second variable to allow limiting the
Ad Hoc filters. It should be a `constant` type named `questdb_adhoc_query`
and can contain: a comma delimited list of tables to show only columns for one or more tables.
and can contain: a comma delimited list of tables to show only columns for one or more tables.

For more information on Ad Hoc filters, check the [Grafana
docs](https://grafana.com/docs/grafana/latest/variables/variable-types/add-ad-hoc-filters/)
Expand All @@ -162,7 +165,7 @@ You may choose to hide this variable from view as it serves no further purpose.

## Learn more

* Add [Annotations](https://grafana.com/docs/grafana/latest/dashboards/annotations/).
* Configure and use [Templates and variables](https://grafana.com/docs/grafana/latest/variables/).
* Add [Transformations](https://grafana.com/docs/grafana/latest/panels/transformations/).
* Set up alerting; refer to [Alerts overview](https://grafana.com/docs/grafana/latest/alerting/).
- Add [Annotations](https://grafana.com/docs/grafana/latest/dashboards/annotations/).
- Configure and use [Templates and variables](https://grafana.com/docs/grafana/latest/variables/).
- Add [Transformations](https://grafana.com/docs/grafana/latest/panels/transformations/).
- Set up alerting; refer to [Alerts overview](https://grafana.com/docs/grafana/latest/alerting/).
16 changes: 7 additions & 9 deletions pkg/plugin/driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import (
"database/sql"
"encoding/json"
"fmt"
"github.com/docker/docker/api/types/mount"
"github.com/lib/pq"
"math"
"os"
"path"
Expand All @@ -15,6 +13,9 @@ import (
"testing"
"time"

"github.com/docker/docker/api/types/mount"
"github.com/lib/pq"

"github.com/docker/docker/api/types/container"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/data"
Expand Down Expand Up @@ -212,7 +213,6 @@ func TestInsertAndQueryData(t *testing.T) {
" ge8 geohash(8c)," +
" ip ipv4, " +
" uuid_ uuid ," +
" l256 long256," +
" ts timestamp " +
") TIMESTAMP(ts) PARTITION BY YEAR BYPASS WAL")
require.NoError(t, err)
Expand All @@ -233,20 +233,18 @@ func TestInsertAndQueryData(t *testing.T) {
require.NoError(t, err)

stmt, err := conn.Prepare("INSERT INTO all_types values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, " +
"cast($13 as geohash(1c)), cast($14 as geohash(2c)) , cast($15 as geohash(4c)), cast($16 as geohash(8c)), $17, $18, cast('' || $19 as long256), $20)")
"cast($13 as geohash(1c)), cast($14 as geohash(2c)) , cast($15 as geohash(4c)), cast($16 as geohash(8c)), $17, $18, $19)")
require.NoError(t, err)
defer stmt.Close()

var data = [][]interface{}{
{bool(false), int16(0), int16(0), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, timestamp},

{bool(false), int16(0), int16(0), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, timestamp},
{bool(true), int16(1), int16(1), mkstring("a"), mkint32(4), mkint64(5), &date, &timestamp, mkfloat32(12.345), mkfloat64(1.0234567890123),
mkstring("string"), mkstring("symbol"), mkstring("r"), mkstring("rj"), mkstring("rjtw"), mkstring("rjtwedd0"), mkstring("1.2.3.4"),
mkstring("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"), mkstring("0x5dd94b8492b4be20632d0236ddb8f47c91efc2568b4d452847b4a645dbe4871a"), &timestamp},

mkstring("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"), &timestamp},
{bool(true), int16(math.MaxInt8), int16(math.MaxInt16), mkstring("z"), mkint32(math.MaxInt32), mkint64(math.MaxInt64), &date, mktimestamp("1970-01-01T00:00:00.000000", t),
mkfloat32(math.MaxFloat32), mkfloat64(math.MaxFloat64), mkstring("XXX"), mkstring(" "), mkstring("e"), mkstring("ee"), mkstring("eeee"), mkstring("eeeeeeee"),
mkstring("255.255.255.255"), mkstring("a0eebc99-ffff-ffff-ffff-ffffffffffff"), mkstring("0x5dd94b8492b4be20632d0236ddb8f47c91efc2568b4d452847b4a645dbefffff"), mktimestamp("2020-03-31T00:00:00.987654", t)}}
mkstring("255.255.255.255"), mkstring("a0eebc99-ffff-ffff-ffff-ffffffffffff"), mktimestamp("2020-03-31T00:00:00.987654", t)}}

for i := 1; i < len(data); i++ {
_, err = stmt.Exec(data[i]...)
Expand Down
1 change: 0 additions & 1 deletion provisioning/datasources/questdb_questdb_datasource.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ datasources:
port: 8812
username: admin
tlsMode: disable
# tlsConfigurationMethod: file-path | file-content
# tlsCACertFile: <string>
# timeout: <seconds>
# queryTimeout: <seconds>
Expand Down
5 changes: 3 additions & 2 deletions src/components/ui/CertificationKey.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ import { Input, Button, TextArea, Field } from '@grafana/ui';

interface Props {
label: string;
tooltip?: string;
hasCert: boolean;
placeholder: string;
onChange: (event: ChangeEvent<HTMLTextAreaElement>) => void;
onClick: (event: MouseEvent<HTMLButtonElement>) => void;
}

export const CertificationKey: FC<Props> = ({ hasCert, label, onChange, onClick, placeholder }) => {
export const CertificationKey: FC<Props> = ({ hasCert, label, tooltip, onChange, onClick, placeholder }) => {
return (
<Field label={label}>
<Field label={label} description={tooltip}>
{hasCert ? (
<>
<Input type="text" disabled value="configured" width={24} />
Expand Down
2 changes: 2 additions & 0 deletions src/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export const Components = {
TLSCACert: {
label: 'TLS/SSL Root Certificate',
placeholder: 'CA Cert. Begins with -----BEGIN CERTIFICATE-----',
tooltip:
"Allows you to configure certificates by specifying its content. The content will be stored encrypted in Grafana's database.",
},
TLSClientCert: {
label: 'TLS/SSL Client Certificate',
Expand Down
11 changes: 4 additions & 7 deletions src/views/QuestDBConfigEditor.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ describe('ConfigEditor', () => {
expect(screen.getByPlaceholderText(Components.ConfigEditor.Username.placeholder)).toBeInTheDocument();
expect(screen.getByPlaceholderText(Components.ConfigEditor.Password.placeholder)).toBeInTheDocument();
expect(screen.getByText(Components.ConfigEditor.TlsMode.placeholder)).toBeInTheDocument();
expect(screen.getByText(Components.ConfigEditor.TlsMethod.label)).toBeInTheDocument();
});
it('with password', async () => {
render(
Expand Down Expand Up @@ -63,7 +62,7 @@ describe('ConfigEditor', () => {
expect(screen.queryByPlaceholderText(Components.ConfigEditor.TLSClientCert.placeholder)).not.toBeInTheDocument();
expect(screen.queryByPlaceholderText(Components.ConfigEditor.TLSClientKey.placeholder)).not.toBeInTheDocument();
});
it('with tlsMode and filePath tlsMethod', async () => {
it('with verifyCA tlsMode and fileContent tlsMethod', async () => {
render(
<ConfigEditor
{...mockConfigEditorProps()}
Expand All @@ -72,15 +71,14 @@ describe('ConfigEditor', () => {
jsonData: {
...mockConfigEditorProps().options.jsonData,
tlsMode: PostgresTLSModes.verifyCA,
tlsConfigurationMethod: PostgresTLSMethods.filePath,
tlsConfigurationMethod: PostgresTLSMethods.fileContent,
},
}}
/>
);
expect(screen.queryByText(PostgresTLSModes.verifyCA)).toBeInTheDocument();
expect(screen.queryByText(Components.ConfigEditor.TlsMethod.placeholder)).toBeInTheDocument();
expect(screen.queryByPlaceholderText(Components.ConfigEditor.TLSCACertFile.placeholder)).toBeInTheDocument();
expect(screen.queryByPlaceholderText(Components.ConfigEditor.TLSCACert.placeholder)).not.toBeInTheDocument();
expect(screen.queryByPlaceholderText(Components.ConfigEditor.TLSCACertFile.placeholder)).not.toBeInTheDocument();
expect(screen.queryByPlaceholderText(Components.ConfigEditor.TLSCACert.placeholder)).toBeInTheDocument();
});

it('with verifyFull tlsMode and fileContent tlsMethod', async () => {
Expand All @@ -98,7 +96,6 @@ describe('ConfigEditor', () => {
/>
);
expect(screen.queryByText(PostgresTLSModes.verifyFull)).toBeInTheDocument();
expect(screen.queryByText(Components.ConfigEditor.TlsMethod.placeholder)).toBeInTheDocument();
expect(screen.queryByPlaceholderText(Components.ConfigEditor.TLSCACertFile.placeholder)).not.toBeInTheDocument();
expect(screen.queryByPlaceholderText(Components.ConfigEditor.TLSCACert.placeholder)).toBeInTheDocument();
});
Expand Down
Loading

0 comments on commit 8ba8ae1

Please sign in to comment.