Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ jobs:
docker run --rm --detach \
--name trino \
--net trino \
--volume "$(pwd)/test-data/test-trino-config.properties:/etc/trino/config.properties" \
--volume "$(pwd)/test-data/trino/test-trino-config.properties:/etc/trino/config.properties" \
--volume "$(pwd)/test-data/trino/catalog/hive.properties:/etc/trino/catalog/hive.properties" \
trinodb/trino:468

echo "Starting Grafana..."
Expand All @@ -97,6 +98,20 @@ jobs:
--volume "$(pwd):/var/lib/grafana/plugins/trino" \
--env "GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS=trino-datasource" \
grafana/grafana:11.4.0

echo "Waiting for Trino to be ready..."
while true; do
if docker logs trino 2>&1 | grep -q '======== SERVER STARTED ========'; then
echo "Trino is ready!"
break
fi
echo "Waiting for Trino..."
sleep 5
done
Comment on lines +102 to +110

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a timeout?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be job default timeout.
Also this code is an adapted version of the preexisting behaviour for Keycloak.


echo "Preconfiguring trino..."
docker exec trino trino --user admin --execute "GRANT admin TO USER grafana IN hive;"
echo "Done."

- name: End to end test
run: |
Expand Down
5 changes: 5 additions & 0 deletions pkg/trino/datasource-context.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
const (
accessTokenKey = "accessToken"
trinoUserHeader = "X-Trino-User"
trinoRoleHeader = "X-Trino-Role"
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trinoClientTagsKey = "X-Trino-Client-Tags"
bearerPrefix = "Bearer "
)
Expand Down Expand Up @@ -41,6 +42,10 @@ func (ds *SQLDatasourceWithTrinoUserContext) QueryData(ctx context.Context, req
ctx = context.WithValue(ctx, trinoUserHeader, user)
}

if settings.Role != "" {
ctx = context.WithValue(ctx, trinoRoleHeader, settings.Role)
}

if settings.ClientTags != "" {
ctx = context.WithValue(ctx, trinoClientTagsKey, settings.ClientTags)
}
Expand Down
5 changes: 5 additions & 0 deletions pkg/trino/datasource.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ func (s *TrinoDatasource) SetQueryArgs(ctx context.Context, headers http.Header)

user := ctx.Value(trinoUserHeader)
accessToken := ctx.Value(accessTokenKey)
role := ctx.Value(trinoRoleHeader)
clientTags := ctx.Value(trinoClientTagsKey)

if user != nil {
Expand All @@ -93,6 +94,10 @@ func (s *TrinoDatasource) SetQueryArgs(ctx context.Context, headers http.Header)
args = append(args, sql.Named(accessTokenKey, accessToken.(string)))
}

if role != nil {
args = append(args, sql.Named(trinoRoleHeader, role.(string)))
}

if clientTags != nil {
args = append(args, sql.Named(trinoClientTagsKey, clientTags.(string)))
}
Expand Down
1 change: 1 addition & 0 deletions pkg/trino/models/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type TrinoDatasourceSettings struct {
ClientId string `json:"clientId"`
ClientSecret string `json:"clientSecret"`
ImpersonationUser string `json:"impersonationUser"`
Role string `json:"role"`
ClientTags string `json:"clientTags"`
}

Expand Down
16 changes: 16 additions & 0 deletions src/ConfigEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ export class ConfigEditor extends PureComponent<Props, State> {
const onImpersonationUserChange = (event: ChangeEvent<HTMLInputElement>) => {
onOptionsChange({...options, jsonData: {...options.jsonData, impersonationUser: event.target.value}})
};
const onRoleChange = (event: ChangeEvent<HTMLInputElement>) => {
onOptionsChange({...options, jsonData: {...options.jsonData, role: event.target.value}})
};
const onClientTagsChange = (event: ChangeEvent<HTMLInputElement>) => {
onOptionsChange({...options, jsonData: {...options.jsonData, clientTags: event.target.value}})
};
Expand Down Expand Up @@ -75,6 +78,19 @@ export class ConfigEditor extends PureComponent<Props, State> {
/>
</InlineField>
</div>
<div className="gf-form-inline">
<InlineField
label="Role"
tooltip="For X-Trino-Role"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that tooltip is very helpful :)

labelWidth={26}
>
<Input
value={options.jsonData?.role ?? ''}
onChange={onRoleChange}
width={40}
/>
</InlineField>
</div>
<div className="gf-form-inline">
<InlineField
label="Client Tags"
Expand Down
37 changes: 37 additions & 0 deletions src/e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ async function setupDataSourceWithClientTags(page: Page, clientTags: string) {
await page.getByTestId('data-testid Data source settings page Save and Test button').click();
}

async function setupDataSourceWithRole(page: Page, role: string) {
await page.getByTestId('data-testid Datasource HTTP settings url').fill('http://trino:8080');
await page.locator('div').filter({hasText: /^Role$/}).locator('input').fill(role);
await page.getByTestId('data-testid Data source settings page Save and Test button').click();
}

async function runQueryAndCheckResults(page: Page) {
await page.getByLabel(EXPORT_DATA).click();
await page.getByTestId('data-testid TimePicker Open Button').click();
Expand Down Expand Up @@ -91,3 +97,34 @@ test('test with client tags', async ({ page }) => {
await setupDataSourceWithClientTags(page, 'tag1,tag2,tag3');
await runQueryAndCheckResults(page);
});

test('test with role', async ({ page }) => {
await login(page);
await goToTrinoSettings(page);
await setupDataSourceWithRole(page, 'hive=ROLE{admin}');

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, so the user needs to know the proper format of this header's value. I'm not sure this is a good idea. I never remember that :) But I'm not familiar with this repository - is this something that only very technical people will use?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had the same reasoning initially #322 (comment)
But later realised that there is no better way to make it reusable.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could make it very complicated with a checkbox for "system or catalog role", then if it's a catalog role then an input box appears to enter the catalog name :)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I guess there should be a test that sets a system role too

Copy link
Author

@ssheikin ssheikin Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not possible to test successful path scenario on trino yet.

await runRoleQuery(page);
await expect(page.getByTestId('data-testid table body')).toContainText(/.*admin.*/);

});

test('test without role', async ({ page }) => {
await login(page);
await goToTrinoSettings(page);
await setupDataSourceWithRole(page, '');
await runRoleQuery(page);
await expect(page.getByText(/Access Denied: Cannot show roles/)).toBeVisible();
});

async function runRoleQuery(page: Page) {
await page.getByLabel(EXPORT_DATA).click();
await page.locator('div').filter({hasText: /^Format asChoose$/}).locator('svg').click();
await page.getByRole('option', {name: 'Table'}).click();
await setQuery(page, 'SHOW ROLES FROM hive')
await page.getByTestId('data-testid Code editor container').click();
await page.getByTestId('data-testid RefreshPicker run button').click();
}

async function setQuery(page: Page, query: string) {
await page.getByTestId('data-testid Code editor container').click({ clickCount: 4 });
await page.keyboard.type(query);
}
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export interface TrinoDataSourceOptions extends DataSourceJsonData {
tokenUrl?: string;
clientId?: string;
impersonationUser?: string;
role?: string;
clientTags?: string;
}
/**
Expand Down
5 changes: 5 additions & 0 deletions test-data/trino/catalog/hive.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
connector.name=hive
hive.metastore=file
hive.metastore.catalog.dir=/tmp/metastore
hive.security=sql-standard
fs.hadoop.enabled=true
Loading