WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 943e388

Browse files
committed
Support trino roles
1 parent 19a0af7 commit 943e388

File tree

7 files changed

+104
-1
lines changed

7 files changed

+104
-1
lines changed

.github/workflows/ci.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ jobs:
8787
--name trino \
8888
--net trino \
8989
--volume "$(pwd)/test-data/trino/test-trino-config.properties:/etc/trino/config.properties" \
90+
--volume "$(pwd)/test-data/trino/catalog/hive.properties:/etc/trino/catalog/hive.properties" \
9091
trinodb/trino:468
9192
9293
echo "Starting Grafana..."
@@ -97,6 +98,20 @@ jobs:
9798
--volume "$(pwd):/var/lib/grafana/plugins/trino" \
9899
--env "GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS=trino-datasource" \
99100
grafana/grafana:11.4.0
101+
102+
echo "Waiting for Trino to be ready..."
103+
while true; do
104+
if docker logs trino 2>&1 | grep -q '======== SERVER STARTED ========'; then
105+
echo "Trino is ready!"
106+
break
107+
fi
108+
echo "Waiting for Trino..."
109+
sleep 5
110+
done
111+
112+
echo "Preconfiguring trino..."
113+
docker exec trino trino --user admin --execute "GRANT admin TO USER grafana IN hive;"
114+
echo "Done."
100115
101116
- name: End to end test
102117
run: |

pkg/trino/driver/driver.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ import (
66
"database/sql"
77
"errors"
88
"fmt"
9-
trinoClient "github.com/trinodb/grafana-trino/pkg/trino/client"
109
"net/http"
1110
"strings"
1211

12+
trinoClient "github.com/trinodb/grafana-trino/pkg/trino/client"
13+
1314
"github.com/trinodb/grafana-trino/pkg/trino/models"
1415
"github.com/trinodb/trino-go-client/trino"
1516
_ "github.com/trinodb/trino-go-client/trino"
@@ -94,12 +95,19 @@ func Open(settings models.TrinoDatasourceSettings) (*sql.DB, error) {
9495
if err != nil {
9596
return nil, err
9697
}
98+
99+
rolesMap, err := parseRoles(settings.Role)
100+
if err != nil {
101+
return nil, err
102+
}
103+
97104
config := trino.Config{
98105
ServerURI: settings.URL.String(),
99106
Source: "grafana",
100107
CustomClientName: "grafana",
101108
ForwardAuthorizationHeader: true,
102109
AccessToken: settings.AccessToken,
110+
Roles: rolesMap,
103111
}
104112

105113
dsn, err := config.FormatDSN()
@@ -108,3 +116,23 @@ func Open(settings models.TrinoDatasourceSettings) (*sql.DB, error) {
108116
}
109117
return sql.Open(DriverName, dsn)
110118
}
119+
120+
func parseRoles(roleStr string) (map[string]string, error) {
121+
rolesMap := make(map[string]string)
122+
if strings.TrimSpace(roleStr) == "" {
123+
return rolesMap, nil
124+
}
125+
pairs := strings.Split(roleStr, ";")
126+
for _, pair := range pairs {
127+
parts := strings.SplitN(pair, ":", 2)
128+
if len(parts) != 2 {
129+
return nil, fmt.Errorf("Invalid role format. expected catalog:role, got '%s'", pair)
130+
}
131+
catalog := strings.TrimSpace(parts[0])
132+
role := strings.TrimSpace(parts[1])
133+
if catalog != "" && role != "" {
134+
rolesMap[catalog] = role
135+
}
136+
}
137+
return rolesMap, nil
138+
}

pkg/trino/models/settings.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type TrinoDatasourceSettings struct {
2020
ClientId string `json:"clientId"`
2121
ClientSecret string `json:"clientSecret"`
2222
ImpersonationUser string `json:"impersonationUser"`
23+
Role string `json:"role"`
2324
ClientTags string `json:"clientTags"`
2425
}
2526

src/ConfigEditor.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ export class ConfigEditor extends PureComponent<Props, State> {
3434
const onImpersonationUserChange = (event: ChangeEvent<HTMLInputElement>) => {
3535
onOptionsChange({...options, jsonData: {...options.jsonData, impersonationUser: event.target.value}})
3636
};
37+
const onRoleChange = (event: ChangeEvent<HTMLInputElement>) => {
38+
onOptionsChange({...options, jsonData: {...options.jsonData, role: event.target.value}})
39+
};
3740
const onClientTagsChange = (event: ChangeEvent<HTMLInputElement>) => {
3841
onOptionsChange({...options, jsonData: {...options.jsonData, clientTags: event.target.value}})
3942
};
@@ -75,6 +78,19 @@ export class ConfigEditor extends PureComponent<Props, State> {
7578
/>
7679
</InlineField>
7780
</div>
81+
<div className="gf-form-inline">
82+
<InlineField
83+
label="Role"
84+
tooltip="The role to be used connecting to Trino"
85+
labelWidth={26}
86+
>
87+
<Input
88+
value={options.jsonData?.role ?? ''}
89+
onChange={onRoleChange}
90+
width={40}
91+
/>
92+
</InlineField>
93+
</div>
7894
<div className="gf-form-inline">
7995
<InlineField
8096
label="Client Tags"

src/e2e.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,40 @@ test('test with client tags', async ({ page }) => {
9191
await setupDataSourceWithClientTags(page, 'tag1,tag2,tag3');
9292
await runQueryAndCheckResults(page);
9393
});
94+
95+
test('test with role', async ({ page }) => {
96+
await login(page);
97+
await goToTrinoSettings(page);
98+
await setupDataSourceWithRole(page, 'system:ALL;hive:admin');
99+
await runRoleQuery(page);
100+
await expect(page.getByTestId('data-testid table body')).toContainText(/.*admin.*/);
101+
102+
});
103+
104+
test('test without role', async ({ page }) => {
105+
await login(page);
106+
await goToTrinoSettings(page);
107+
await setupDataSourceWithRole(page, '');
108+
await runRoleQuery(page);
109+
await expect(page.getByText(/Access Denied: Cannot show roles/)).toBeVisible();
110+
});
111+
112+
async function setupDataSourceWithRole(page: Page, role: string) {
113+
await page.getByTestId('data-testid Datasource HTTP settings url').fill('http://trino:8080');
114+
await page.locator('div').filter({hasText: /^Role$/}).locator('input').fill(role);
115+
await page.getByTestId('data-testid Data source settings page Save and Test button').click();
116+
}
117+
118+
async function runRoleQuery(page: Page) {
119+
await page.getByLabel(EXPORT_DATA).click();
120+
await page.locator('div').filter({hasText: /^Format asChoose$/}).locator('svg').click();
121+
await page.getByRole('option', {name: 'Table'}).click();
122+
await setQuery(page, 'SHOW ROLES FROM hive')
123+
await page.getByTestId('data-testid Code editor container').click();
124+
await page.getByTestId('data-testid RefreshPicker run button').click();
125+
}
126+
127+
async function setQuery(page: Page, query: string) {
128+
await page.getByTestId('data-testid Code editor container').click({ clickCount: 4 });
129+
await page.keyboard.type(query);
130+
}

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export interface TrinoDataSourceOptions extends DataSourceJsonData {
5656
tokenUrl?: string;
5757
clientId?: string;
5858
impersonationUser?: string;
59+
role?: string;
5960
clientTags?: string;
6061
}
6162
/**
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
connector.name=hive
2+
hive.metastore=file
3+
hive.metastore.catalog.dir=/tmp/metastore
4+
hive.security=sql-standard
5+
fs.hadoop.enabled=true

0 commit comments

Comments
 (0)