Skip to content

Commit

Permalink
Query UI: Add tenant box (thanos-io#6867)
Browse files Browse the repository at this point in the history
* Query UI: Add tenant box

With this commit as tenant box is added to the query UI. It can be used
to specify which tenant to use when making a query.

Signed-off-by: Jacob Baungard Hansen <[email protected]>

* Re-compile static react app

Recompiles the static react app as now needed following:
thanos-io#6900

Signed-off-by: Jacob Baungard Hansen <[email protected]>

* Move changelog item to appropiate future release

After merging it was under the 0.34 release.

Signed-off-by: Jacob Baungard Hansen <[email protected]>

* Move query path tenancy proposal to done

Signed-off-by: Jacob Baungard Hansen <[email protected]>

---------

Signed-off-by: Jacob Baungard Hansen <[email protected]>
  • Loading branch information
jacobbaungard authored and jnyi committed Apr 4, 2024
1 parent 67abf60 commit a8ece96
Show file tree
Hide file tree
Showing 17 changed files with 87 additions and 20 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ We use *breaking :warning:* to mark changes that are not backward compatible (re

### Added
- [#7105](https://github.com/thanos-io/thanos/pull/7105) Rule: add flag `--query.enable-x-functions` to allow usage of extended promql functions (xrate, xincrease, xdelta) in loaded rules
- [#6867](https://github.com/thanos-io/thanos/pull/6867) Query UI: Tenant input box added to the Query UI, in order to be able to specify which tenant the query should use.

### Changed

Expand Down
2 changes: 1 addition & 1 deletion cmd/thanos/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,7 @@ func runQuery(

ins := extpromhttp.NewTenantInstrumentationMiddleware(tenantHeader, defaultTenant, reg, nil)
// TODO(bplotka in PR #513 review): pass all flags, not only the flags needed by prefix rewriting.
ui.NewQueryUI(logger, endpoints, webExternalPrefix, webPrefixHeaderName, alertQueryURL).Register(router, ins)
ui.NewQueryUI(logger, endpoints, webExternalPrefix, webPrefixHeaderName, alertQueryURL, tenantHeader, defaultTenant).Register(router, ins)

api := apiv1.NewQueryAPI(
logger,
Expand Down
8 changes: 5 additions & 3 deletions pkg/ui/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@ type Query struct {
now func() model.Time
}

func NewQueryUI(logger log.Logger, endpointSet *query.EndpointSet, externalPrefix, prefixHeader, alertQueryURL string) *Query {
func NewQueryUI(logger log.Logger, endpointSet *query.EndpointSet, externalPrefix, prefixHeader, alertQueryURL string, tenantHeader string, defaultTenant string) *Query {
tmplVariables := map[string]string{
"Component": component.Query.String(),
"queryURL": alertQueryURL,
"Component": component.Query.String(),
"queryURL": alertQueryURL,
"tenantHeader": tenantHeader,
"defaultTenant": defaultTenant,
}
runtimeInfo := api.GetRuntimeInfoFunc(logger)

Expand Down
2 changes: 2 additions & 0 deletions pkg/ui/react-app/globals.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ declare global {
jQuery: JQueryStatic;
moment: Moment;
THANOS_QUERY_URL: string;
THANOS_DEFAULT_TENANT: string;
THANOS_TENANT_HEADER: string;
}
}
2 changes: 2 additions & 0 deletions pkg/ui/react-app/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
<script>
const THANOS_COMPONENT="{{ .Component }}";
const THANOS_QUERY_URL="{{ .queryURL }}";
const THANOS_TENANT_HEADER="{{ .tenantHeader }}";
const THANOS_DEFAULT_TENANT="{{ .defaultTenant }}";
</script>

<!--
Expand Down
2 changes: 2 additions & 0 deletions pkg/ui/react-app/src/globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ import jquery from 'jquery';
window.jQuery = jquery;
window.moment = require('moment');
window.THANOS_QUERY_URL = '';
window.THANOS_TENANT_HEADER = '';
window.THANOS_DEFAULT_TENANT = '';
2 changes: 2 additions & 0 deletions pkg/ui/react-app/src/pages/graph/Panel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const defaultProps: PanelProps = {
engine: 'prometheus',
analyze: false,
disableAnalyzeCheckbox: false,
tenant: 'default-tenant',
},
onUsePartialResponseChange: (): void => {
// Do nothing.
Expand Down Expand Up @@ -105,6 +106,7 @@ describe('Panel', () => {
engine: 'prometheus',
analyze: false,
disableAnalyzeCheckbox: false,
tenant: 'default-tenant',
};
const graphPanel = mount(<Panel {...defaultProps} options={options} />);
const controls = graphPanel.find(GraphControls);
Expand Down
48 changes: 42 additions & 6 deletions pkg/ui/react-app/src/pages/graph/Panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { Store } from '../../thanos/pages/stores/store';
import PathPrefixProps from '../../types/PathPrefixProps';
import { QueryParams } from '../../types/types';
import { parseDuration } from '../../utils';
import { defaultTenant, tenantHeader } from '../../thanos/config';

export interface PanelProps {
id: string;
Expand Down Expand Up @@ -78,6 +79,7 @@ export interface PanelOptions {
engine: string;
analyze: boolean;
disableAnalyzeCheckbox: boolean;
tenant: string;
}

export enum PanelType {
Expand All @@ -100,6 +102,7 @@ export const PanelDefaultOptions: PanelOptions = {
engine: '',
analyze: false,
disableAnalyzeCheckbox: false,
tenant: '',
};

class Panel extends Component<PanelProps & PathPrefixProps, PanelState> {
Expand Down Expand Up @@ -134,6 +137,7 @@ class Panel extends Component<PanelProps & PathPrefixProps, PanelState> {
this.handleChangeAnalyze = this.handleChangeAnalyze.bind(this);
this.handleMouseEnter = this.handleMouseEnter.bind(this);
this.handleMouseLeave = this.handleMouseLeave.bind(this);
this.handleChangeTenant = this.handleChangeTenant.bind(this);
}
componentDidUpdate({ options: prevOpts }: PanelProps): void {
const {
Expand All @@ -147,6 +151,7 @@ class Panel extends Component<PanelProps & PathPrefixProps, PanelState> {
usePartialResponse,
engine,
analyze,
tenant,
// TODO: Add support for Store Matches
} = this.props.options;
if (
Expand All @@ -159,7 +164,8 @@ class Panel extends Component<PanelProps & PathPrefixProps, PanelState> {
prevOpts.usePartialResponse !== usePartialResponse ||
prevOpts.forceTracing !== forceTracing ||
prevOpts.engine !== engine ||
prevOpts.analyze !== analyze
prevOpts.analyze !== analyze ||
prevOpts.tenant !== tenant
// Check store matches
) {
this.executeQuery();
Expand Down Expand Up @@ -224,25 +230,35 @@ class Panel extends Component<PanelProps & PathPrefixProps, PanelState> {
params.append('max_source_resolution', this.props.options.maxSourceResolution);
params.append('engine', this.props.options.engine);
params.append('analyze', this.props.options.analyze.toString());
params.append('tenant', this.props.options.tenant);
// TODO path prefix here and elsewhere.
break;
case 'table':
path = '/api/v1/query';
params.append('time', endTime.toString());
params.append('engine', this.props.options.engine);
params.append('analyze', this.props.options.analyze.toString());
params.append('tenant', this.props.options.tenant);
break;
default:
throw new Error('Invalid panel type "' + this.props.options.type + '"');
}

// Create request headers
const requestHeaders: HeadersInit = new Headers();
requestHeaders.set('Content-Type', 'application/json');

if (this.props.options.forceTracing) {
requestHeaders.set('X-Thanos-Force-Tracing', 'true');
}

if (this.props.options.tenant.length > 0) {
requestHeaders.set(tenantHeader, this.props.options.tenant);
}

fetch(`${this.props.pathPrefix}${path}?${params}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
// Conditionally add the header if the checkbox is enabled
...(this.props.options.forceTracing ? { 'X-Thanos-Force-Tracing': 'true' } : {}),
},
headers: requestHeaders,
cache: 'no-store',
credentials: 'same-origin',
signal: abortController.signal,
Expand Down Expand Up @@ -387,6 +403,10 @@ class Panel extends Component<PanelProps & PathPrefixProps, PanelState> {
this.setOptions({ analyze: event.target.checked });
};

handleChangeTenant = (event: React.ChangeEvent<HTMLInputElement>): void => {
this.setOptions({ tenant: event.target.value });
};

handleMouseEnter = () => {
this.setState({ isHovered: true });
};
Expand Down Expand Up @@ -564,6 +584,22 @@ class Panel extends Component<PanelProps & PathPrefixProps, PanelState> {
<option value="prometheus">Prometheus</option>
<option value="thanos">Thanos</option>
</Input>
<Label style={{ marginLeft: '10px', display: 'inline-block' }} className="control-label">
Tenant
</Label>
<Input
style={{
width: 'auto',
marginLeft: '10px',
display: 'inline-block',
}}
id={`tenant=${id}`}
type="text"
bsSize="sm"
onChange={this.handleChangeTenant}
placeholder={`${defaultTenant}`}
value={options.tenant}
></Input>
</div>
<div className="float-right" onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
<Checkbox
Expand Down
12 changes: 12 additions & 0 deletions pkg/ui/react-app/src/thanos/config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
declare const THANOS_QUERY_URL: string;
declare const THANOS_TENANT_HEADER: string;
declare const THANOS_DEFAULT_TENANT: string;

export let queryURL = THANOS_QUERY_URL;
if (queryURL === '' || queryURL === '{{ .queryURL }}') {
queryURL = 'http://localhost:10902';
}

export let defaultTenant = THANOS_DEFAULT_TENANT;
if (defaultTenant === '' || defaultTenant === '{{ .defaultTenant }}') {
defaultTenant = 'default-tenant';
}

export let tenantHeader = THANOS_TENANT_HEADER;
if (tenantHeader === '' || tenantHeader === '{{ .tenantHeader }}') {
tenantHeader = 'thanos-tenant';
}
5 changes: 5 additions & 0 deletions pkg/ui/react-app/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,9 @@ export const parseOption = (param: string): Partial<PanelOptions> => {

case 'analyze':
return { analyze: decodedValue === '1' };

case 'tenant':
return { tenant: decodedValue };
}
return {};
};
Expand All @@ -258,6 +261,7 @@ export const toQueryString = ({ key, options }: PanelMeta): string => {
storeMatches,
engine,
analyze,
tenant,
} = options;
const time = isPresent(endTime) ? formatTime(endTime) : false;
const urlParams = [
Expand All @@ -271,6 +275,7 @@ export const toQueryString = ({ key, options }: PanelMeta): string => {
formatWithKey('store_matches', JSON.stringify(storeMatches, ['name'])),
formatWithKey('engine', engine),
formatWithKey('analyze', analyze ? 1 : 0),
formatWithKey('tenant', tenant),
time ? `${formatWithKey('end_input', time)}&${formatWithKey('moment_input', time)}` : '',
isPresent(resolution) ? formatWithKey('step_input', resolution) : '',
];
Expand Down
7 changes: 5 additions & 2 deletions pkg/ui/react-app/src/utils/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ describe('Utils', () => {
storeMatches: [],
engine: 'prometheus',
analyze: false,
tenant: 'default-tenant',
},
},
{
Expand All @@ -240,11 +241,12 @@ describe('Utils', () => {
storeMatches: stores,
engine: 'prometheus',
analyze: false,
tenant: 'default-tenant',
},
},
];
const query =
'?g0.expr=rate(node_cpu_seconds_total%7Bmode%3D%22system%22%7D%5B1m%5D)&g0.tab=0&g0.stacked=0&g0.range_input=1h&g0.max_source_resolution=raw&g0.deduplicate=1&g0.partial_response=0&g0.store_matches=%5B%5D&g0.engine=prometheus&g0.analyze=0&g0.end_input=2019-10-25%2023%3A37%3A00&g0.moment_input=2019-10-25%2023%3A37%3A00&g1.expr=node_filesystem_avail_bytes&g1.tab=1&g1.stacked=0&g1.range_input=1h&g1.max_source_resolution=auto&g1.deduplicate=0&g1.partial_response=1&g1.store_matches=%5B%7B%22name%22%3A%22thanos_sidecar_one%3A10901%22%7D%5D&g1.engine=prometheus&g1.analyze=0';
'?g0.expr=rate(node_cpu_seconds_total%7Bmode%3D%22system%22%7D%5B1m%5D)&g0.tab=0&g0.stacked=0&g0.range_input=1h&g0.max_source_resolution=raw&g0.deduplicate=1&g0.partial_response=0&g0.store_matches=%5B%5D&g0.engine=prometheus&g0.analyze=0&g0.tenant=default-tenant&g0.end_input=2019-10-25%2023%3A37%3A00&g0.moment_input=2019-10-25%2023%3A37%3A00&g1.expr=node_filesystem_avail_bytes&g1.tab=1&g1.stacked=0&g1.range_input=1h&g1.max_source_resolution=auto&g1.deduplicate=0&g1.partial_response=1&g1.store_matches=%5B%7B%22name%22%3A%22thanos_sidecar_one%3A10901%22%7D%5D&g1.engine=prometheus&g1.analyze=0&g1.tenant=default-tenant';

describe('decodePanelOptionsFromQueryString', () => {
it('returns [] when query is empty', () => {
Expand Down Expand Up @@ -338,10 +340,11 @@ describe('Utils', () => {
engine: 'prometheus',
analyze: false,
disableAnalyzeCheckbox: false,
tenant: 'default-tenant',
},
})
).toEqual(
'g0.expr=foo&g0.tab=0&g0.stacked=1&g0.range_input=0s&g0.max_source_resolution=raw&g0.deduplicate=1&g0.partial_response=0&g0.store_matches=%5B%5D&g0.engine=prometheus&g0.analyze=0&g0.step_input=1'
'g0.expr=foo&g0.tab=0&g0.stacked=1&g0.range_input=0s&g0.max_source_resolution=raw&g0.deduplicate=1&g0.partial_response=0&g0.store_matches=%5B%5D&g0.engine=prometheus&g0.analyze=0&g0.tenant=default-tenant&g0.step_input=1'
);
});
});
Expand Down
6 changes: 3 additions & 3 deletions pkg/ui/static/react/asset-manifest.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"files": {
"main.css": "./static/css/main.5a4981c4.css",
"main.js": "./static/js/main.f33bee70.js",
"main.js": "./static/js/main.1da5c0bf.js",
"static/media/codicon.ttf": "./static/media/codicon.b3726f0165bf67ac6849.ttf",
"index.html": "./index.html",
"static/media/index.cjs": "./static/media/index.cd351d7c31d0d3fccf96.cjs",
"main.5a4981c4.css.map": "./static/css/main.5a4981c4.css.map",
"main.f33bee70.js.map": "./static/js/main.f33bee70.js.map"
"main.1da5c0bf.js.map": "./static/js/main.1da5c0bf.js.map"
},
"entrypoints": [
"static/css/main.5a4981c4.css",
"static/js/main.f33bee70.js"
"static/js/main.1da5c0bf.js"
]
}
2 changes: 1 addition & 1 deletion pkg/ui/static/react/index.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="shortcut icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"/><meta name="theme-color" content="#000000"/><script>const GLOBAL_PATH_PREFIX="{{ pathPrefix }}"</script><script>const THANOS_COMPONENT="{{ .Component }}",THANOS_QUERY_URL="{{ .queryURL }}"</script><link rel="manifest" href="./manifest.json"/><title>Thanos | Highly available Prometheus setup</title><script defer="defer" src="./static/js/main.f33bee70.js"></script><link href="./static/css/main.5a4981c4.css" rel="stylesheet"></head><body class="bootstrap"><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="shortcut icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"/><meta name="theme-color" content="#000000"/><script>const GLOBAL_PATH_PREFIX="{{ pathPrefix }}"</script><script>const THANOS_COMPONENT="{{ .Component }}",THANOS_QUERY_URL="{{ .queryURL }}",THANOS_TENANT_HEADER="{{ .tenantHeader }}",THANOS_DEFAULT_TENANT="{{ .defaultTenant }}"</script><link rel="manifest" href="./manifest.json"/><title>Thanos | Highly available Prometheus setup</title><script defer="defer" src="./static/js/main.1da5c0bf.js"></script><link href="./static/css/main.5a4981c4.css" rel="stylesheet"></head><body class="bootstrap"><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

Large diffs are not rendered by default.

Large diffs are not rendered by default.

0 comments on commit a8ece96

Please sign in to comment.