Skip to content

Commit

Permalink
feat: label & variable selectors
Browse files Browse the repository at this point in the history
  • Loading branch information
apricote committed Jan 4, 2024
1 parent 4d54468 commit 52f8ea1
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 29 deletions.
18 changes: 12 additions & 6 deletions pkg/plugin/datasource.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ func (d *Datasource) QueryData(ctx context.Context, req *backend.QueryDataReques
}

func (d *Datasource) queryResourceList(ctx context.Context, query backend.DataQuery) backend.DataResponse {
var response backend.DataResponse
var resp backend.DataResponse

queryData := QueryModel{}
err := json.Unmarshal(query.JSON, &queryData)
Expand All @@ -188,19 +188,21 @@ func (d *Datasource) queryResourceList(ctx context.Context, query backend.DataQu

switch queryData.ResourceType {
case ResourceTypeServer:
servers, err := d.client.Server.All(ctx)
servers, err := d.client.Server.AllWithOpts(ctx, hcloud.ServerListOpts{ListOpts: hcloud.ListOpts{LabelSelector: strings.Join(queryData.LabelSelectors, ", ")}})
if err != nil {
return backend.ErrDataResponseWithSource(backend.StatusInternal, backend.ErrorSourceDownstream, fmt.Sprintf("error getting servers: %v", err.Error()))
}

ids := make([]int64, 0, len(servers))
vars := make([]string, 0, len(servers))
names := make([]string, 0, len(servers))
serverTypes := make([]string, 0, len(servers))
status := make([]string, 0, len(servers))
labels := make([]json.RawMessage, 0, len(servers))

for _, server := range servers {
ids = append(ids, server.ID)
vars = append(vars, fmt.Sprintf("%s : %d", server.Name, server.ID))
names = append(names, server.Name)
serverTypes = append(serverTypes, server.ServerType.Name)
status = append(status, string(server.Status))
Expand All @@ -215,27 +217,30 @@ func (d *Datasource) queryResourceList(ctx context.Context, query backend.DataQu
frame := data.NewFrame("servers")
frame.Fields = append(frame.Fields,
data.NewField("id", nil, ids),
data.NewField("var", nil, vars),
data.NewField("name", nil, names),
data.NewField("server_type", nil, serverTypes),
data.NewField("status", nil, status),
data.NewField("labels", nil, labels),
)

response.Frames = append(response.Frames, frame)
resp.Frames = append(resp.Frames, frame)

case ResourceTypeLoadBalancer:
loadBalancers, err := d.client.LoadBalancer.All(ctx)
loadBalancers, err := d.client.LoadBalancer.AllWithOpts(ctx, hcloud.LoadBalancerListOpts{ListOpts: hcloud.ListOpts{LabelSelector: strings.Join(queryData.LabelSelectors, ", ")}})
if err != nil {
return backend.ErrDataResponseWithSource(backend.StatusInternal, backend.ErrorSourceDownstream, fmt.Sprintf("error getting load balancers: %v", err.Error()))
}

ids := make([]int64, 0, len(loadBalancers))
vars := make([]string, 0, len(loadBalancers))
names := make([]string, 0, len(loadBalancers))
loadBalancerTypes := make([]string, 0, len(loadBalancers))
labels := make([]json.RawMessage, 0, len(loadBalancers))

for _, lb := range loadBalancers {
ids = append(ids, lb.ID)
vars = append(vars, fmt.Sprintf("%s : %d", lb.Name, lb.ID))
names = append(names, lb.Name)
loadBalancerTypes = append(loadBalancerTypes, lb.LoadBalancerType.Name)

Expand All @@ -249,17 +254,18 @@ func (d *Datasource) queryResourceList(ctx context.Context, query backend.DataQu
frame := data.NewFrame("load-balancers")
frame.Fields = append(frame.Fields,
data.NewField("id", nil, ids),
data.NewField("var", nil, vars),
data.NewField("name", nil, names),
data.NewField("load_balancer_type", nil, loadBalancerTypes),
data.NewField("labels", nil, labels),
)

response.Frames = append(response.Frames, frame)
resp.Frames = append(resp.Frames, frame)
default:
return backend.ErrDataResponseWithSource(backend.StatusBadRequest, backend.ErrorSourcePlugin, fmt.Sprintf("unknown resource type: %v", queryData.ResourceType))
}

return response
return resp
}

func (d *Datasource) queryMetrics(ctx context.Context, query backend.DataQuery) backend.DataResponse {
Expand Down
95 changes: 76 additions & 19 deletions provisioning/dashboards/demo.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,10 @@
"metricsType": "cpu",
"queryType": "metrics",
"refId": "A",
"resourceIDs": [
40488783,
40540648
],
"resourceType": "server"
"resourceIDs": [],
"resourceIDsVariable": "$servers",
"resourceType": "server",
"selectBy": "name"
}
],
"title": "CPU",
Expand Down Expand Up @@ -202,11 +201,10 @@
"metricsType": "disk",
"queryType": "metrics",
"refId": "A",
"resourceIDs": [
40488783,
40540648
],
"resourceType": "server"
"resourceIDs": [],
"resourceIDsVariable": "$servers",
"resourceType": "server",
"selectBy": "name"
}
],
"title": "Disk",
Expand Down Expand Up @@ -297,11 +295,10 @@
"metricsType": "network",
"queryType": "metrics",
"refId": "A",
"resourceIDs": [
40540648,
40488783
],
"resourceType": "server"
"resourceIDs": [],
"resourceIDsVariable": "$servers",
"resourceType": "server",
"selectBy": "name"
}
],
"title": "Network",
Expand All @@ -317,8 +314,8 @@
{
"current": {
"selected": false,
"text": "hcloud",
"value": "hcloud"
"text": "home-cloud",
"value": "home-cloud"
},
"hide": 0,
"includeAll": false,
Expand All @@ -332,6 +329,66 @@
"regex": "",
"skipUrlSync": false,
"type": "datasource"
},
{
"current": {
"selected": false,
"text": "All",
"value": "$__all"
},
"datasource": {
"type": "apricote-hcloud-datasource",
"uid": "${datasource}"
},
"definition": "",
"hide": 0,
"includeAll": true,
"label": "Servers",
"multi": true,
"name": "servers",
"options": [],
"query": {
"labelSelectors": [],
"queryType": "resource-list",
"resourceIDs": [],
"resourceType": "server"
},
"refresh": 1,
"regex": "(?<text>.*) : (?<value>.*)",
"skipUrlSync": false,
"sort": 0,
"type": "query"
},
{
"current": {
"isNone": true,
"selected": false,
"text": "None",
"value": ""
},
"datasource": {
"type": "apricote-hcloud-datasource",
"uid": "${datasource}"
},
"definition": "",
"hide": 0,
"includeAll": false,
"label": "Load Balancers",
"multi": false,
"name": "load_balancers",
"options": [],
"query": {
"labelSelectors": [],
"metricsType": "open-connections",
"queryType": "resource-list",
"resourceIDs": [],
"resourceType": "load-balancer"
},
"refresh": 1,
"regex": "(?<text>.*) : (?<value>.*)",
"skipUrlSync": false,
"sort": 0,
"type": "query"
}
]
},
Expand All @@ -354,6 +411,6 @@
"timezone": "",
"title": "Hetzner Cloud Datasource Demo",
"uid": "d351478f-c755-4390-b0ee-f07b9a9081e1",
"version": 2,
"version": 9,
"weekStart": ""
}
}
30 changes: 29 additions & 1 deletion src/components/QueryEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
AsyncMultiSelect,
InlineField,
InlineFieldRow,
Input,
LinkButton,
RadioButtonGroup,
Select,
Expand Down Expand Up @@ -48,7 +49,15 @@ export function QueryEditor({ query, onChange, onRunQuery, datasource }: Props)
onRunQuery();
};

const { queryType, resourceType, metricsType, resourceIDs, selectBy, labelSelectors } = query;
const {
queryType,
resourceType,
metricsType,
selectBy,
labelSelectors = [],
resourceIDs = [],
resourceIDsVariable = '',
} = query;

const multiselectLoadResources = useCallback(
async (_: string) => {
Expand Down Expand Up @@ -99,6 +108,15 @@ export function QueryEditor({ query, onChange, onRunQuery, datasource }: Props)
onChange={onResourceTypeChange}
></Select>
</InlineField>
{queryType === 'resource-list' && (
<LabelSelectorInput
values={labelSelectors}
onChange={(v) => {
onChange({ ...query, labelSelectors: v });
onRunQuery();
}}
/>
)}
{queryType === 'metrics' && (
<>
<InlineField label="Metrics Type">
Expand All @@ -118,6 +136,7 @@ export function QueryEditor({ query, onChange, onRunQuery, datasource }: Props)
options={[
{ label: 'Labels', value: 'label', icon: 'filter' },
{ label: 'IDs', value: 'id', icon: 'gf-layout-simple' },
{ label: 'Variable', value: 'name', icon: 'grafana' },
]}
/>
</InlineField>
Expand All @@ -143,6 +162,15 @@ export function QueryEditor({ query, onChange, onRunQuery, datasource }: Props)
></AsyncMultiSelect>
</InlineField>
)}
{selectBy === 'name' && (
<InlineField label={'Variable Name'} tooltip={'Make sure to prefix with $'}>
<Input
value={resourceIDsVariable}
placeholder={'$variableName'}
onChange={(e) => onChange({ ...query, resourceIDsVariable: e.currentTarget.value })}
></Input>
</InlineField>
)}
</>
)}
</InlineFieldRow>
Expand Down
38 changes: 36 additions & 2 deletions src/datasource.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { DataSourceInstanceSettings, CoreApp, SelectableValue } from '@grafana/data';
import { DataSourceWithBackend } from '@grafana/runtime';
import { DataSourceInstanceSettings, CoreApp, SelectableValue, ScopedVars } from '@grafana/data';
import { DataSourceWithBackend, getTemplateSrv } from '@grafana/runtime';

import { Query, DataSourceOptions, DEFAULT_QUERY } from './types';
import { VariableSupport } from './variables';
Expand All @@ -11,6 +11,32 @@ export class DataSource extends DataSourceWithBackend<Query, DataSourceOptions>
this.variables = new VariableSupport();
}

applyTemplateVariables(query: Query, scopedVars: ScopedVars): Query {
const templateSrv = getTemplateSrv();

if (query.labelSelectors) {
query.labelSelectors = query.labelSelectors.map((selector) =>
getTemplateSrv().replace(selector, scopedVars, 'json')
);
}

if (query.selectBy === 'name') {
query.selectBy = 'id';

const replacedValue = getTemplateSrv().replace(query.resourceIDsVariable, scopedVars, 'json');

if (replacedValue !== '') {
query.resourceIDs = (JSON.parse(replacedValue) as string[]).map((stringID) => parseInt(stringID, 10));
} else {
query.resourceIDs = [];
}
}

console.log('applyTemplateVariables', { query, scopedVars, vars: templateSrv.getVariables() });

return query;
}

getDefaultQuery(_: CoreApp): Partial<Query> {
return DEFAULT_QUERY;
}
Expand All @@ -22,4 +48,12 @@ export class DataSource extends DataSourceWithBackend<Query, DataSourceOptions>
async getLoadBalancers(): Promise<Array<SelectableValue<number>>> {
return this.getResource('/load-balancers');
}

filterQuery(query: Query): boolean {
if (query.selectBy === 'name' && query.resourceIDsVariable === '') {
return false;
}

return true;
}
}
7 changes: 6 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ export interface Query extends DataQuery {
resourceType: 'server' | 'load-balancer';
metricsType: (typeof ServerMetricsTypes)[number] | (typeof LoadBalancerMetricsTypes)[number];

selectBy: 'label' | 'id';
selectBy: 'label' | 'id' | 'name';
labelSelectors: string[];
resourceIDs: number[];
resourceIDsVariable: string;
}

export const DEFAULT_QUERY: Partial<Query> = {
Expand All @@ -26,11 +27,15 @@ export const DEFAULT_QUERY: Partial<Query> = {
selectBy: 'label',
labelSelectors: [],
resourceIDs: [],
resourceIDsVariable: '',
};

export const DEFAULT_VARIABLE_QUERY: Partial<Query> = {
queryType: 'resource-list',
resourceType: 'server',

labelSelectors: [],
resourceIDs: [],
};

/**
Expand Down

0 comments on commit 52f8ea1

Please sign in to comment.