Skip to content

Commit

Permalink
ui: Migrate Thanos Ruler UI to React (#2865)
Browse files Browse the repository at this point in the history
* ui: Migrate Thanos Ruler UI to React

Signed-off-by: Prem Kumar <prmsrswt@gmail.com>

* ui: Pass component specific variables as map to BaseUI

Signed-off-by: Prem Kumar <prmsrswt@gmail.com>
  • Loading branch information
onprem authored Jul 31, 2020
1 parent ee52915 commit 69b8760
Show file tree
Hide file tree
Showing 15 changed files with 126 additions and 80 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ We use *breaking* word for marking changes that are not backward compatible (rel
- [#2305](https://github.com/thanos-io/thanos/pull/2305) Receive,Sidecar,Ruler: Propagate correct (stricter) MinTime for no-block TSDBs.
- [#2926](https://github.com/thanos-io/thanos/pull/2926) API: Add new blocks HTTP API to serve blocks metadata. The status endpoints (`/api/v1/status/flags`, `/api/v1/status/runtimeinfo` and `/api/v1/status/buildinfo`) are now available on all components with a HTTP API.
- [#2892](https://github.com/thanos-io/thanos/pull/2892) Receive: Receiver fails when the initial upload fails.
- [#2865](https://github.com/thanos-io/thanos/pull/2865) ui: Migrate Thanos Ruler UI to React

### Changed

Expand Down
106 changes: 53 additions & 53 deletions pkg/ui/bindata.go

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion pkg/ui/bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,12 @@ type Bucket struct {
}

func NewBucketUI(logger log.Logger, label, externalPrefix, prefixHeader string) *Bucket {
tmplVariables := map[string]string{
"Component": component.Bucket.String(),
}

return &Bucket{
BaseUI: NewBaseUI(log.With(logger, "component", "bucketUI"), "bucket_menu.html", queryTmplFuncs(), externalPrefix, prefixHeader, component.Bucket),
BaseUI: NewBaseUI(log.With(logger, "component", "bucketUI"), "bucket_menu.html", queryTmplFuncs(), tmplVariables, externalPrefix, prefixHeader, component.Bucket),
Blocks: "[]",
Label: label,
externalPrefix: externalPrefix,
Expand Down
5 changes: 4 additions & 1 deletion pkg/ui/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,13 @@ type Query struct {
}

func NewQueryUI(logger log.Logger, reg prometheus.Registerer, storeSet *query.StoreSet, externalPrefix, prefixHeader string) *Query {
tmplVariables := map[string]string{
"Component": component.Query.String(),
}
runtimeInfo := api.GetRuntimeInfoFunc(logger)

return &Query{
BaseUI: NewBaseUI(logger, "query_menu.html", queryTmplFuncs(), externalPrefix, prefixHeader, component.Query),
BaseUI: NewBaseUI(logger, "query_menu.html", queryTmplFuncs(), tmplVariables, externalPrefix, prefixHeader, component.Query),
storeSet: storeSet,
externalPrefix: externalPrefix,
prefixHeader: prefixHeader,
Expand Down
9 changes: 6 additions & 3 deletions pkg/ui/react-app/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@

<!--
This constant's placeholder magic value is replaced during serving by Thanos
and set to Thanos's external URL path. It gets prepended to all links back
and set to Thanos' external URL path. It gets prepended to all links back
to Thanos, both for asset loading as well as API accesses.
-->
<script>const GLOBAL_PATH_PREFIX="{{ pathPrefix }}";</script>
<script>const THANOS_COMPONENT="{{ .Component }}";</script>
<script>
const THANOS_COMPONENT="{{ .Component }}";
const THANOS_QUERY_URL="{{ .queryURL }}";
</script>

<!--
manifest.json provides metadata used when your web app is added to the
Expand All @@ -31,7 +34,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Thanos Expression Browser</title>
<title>Thanos | Highly available Prometheus setup</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
Expand Down
13 changes: 11 additions & 2 deletions pkg/ui/react-app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,22 @@ import { Stores, ErrorBoundary } from './thanos/pages';

import './App.css';

const defaultRouteConfig: { [component: string]: string } = {
query: '/graph',
rule: '/alerts',
};

const App: FC<PathPrefixProps & ThanosComponentProps> = ({ pathPrefix, thanosComponent }) => {
return (
<ErrorBoundary>
<Navigation pathPrefix={pathPrefix} thanosComponent={thanosComponent} />
<Navigation
pathPrefix={pathPrefix}
thanosComponent={thanosComponent}
defaultRoute={defaultRouteConfig[thanosComponent]}
/>
<Container fluid style={{ paddingTop: 70 }}>
<Router basepath={`${pathPrefix}/new`}>
<Redirect from="/" to={`${pathPrefix}/new/graph`} />
<Redirect from="/" to={`${pathPrefix}/new${defaultRouteConfig[thanosComponent]}`} />

{/*
NOTE: Any route added here needs to also be added to the list of
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 @@ -2,3 +2,5 @@ import jquery from 'jquery';

(window as any).jQuery = jquery;
(window as any).moment = require('moment');

(window as any).THANOS_QUERY_URL = '';
7 changes: 3 additions & 4 deletions pkg/ui/react-app/src/pages/alerts/CollapsibleAlertPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import React, { FC, useState, Fragment } from 'react';
import { Link } from '@reach/router';
import { Alert, Collapse, Table, Badge } from 'reactstrap';
import { RuleStatus } from './AlertContents';
import { Rule } from '../../types/types';
import { faChevronDown, faChevronRight } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { createExpressionLink } from '../../utils/index';
import { createExternalExpressionLink } from '../../utils/index';

interface CollapsibleAlertPanelProps {
rule: Rule;
Expand All @@ -31,10 +30,10 @@ const CollapsibleAlertPanel: FC<CollapsibleAlertPanelProps> = ({ rule, showAnnot
<pre style={{ background: '#f5f5f5', padding: 15 }}>
<code>
<div>
name: <Link to={createExpressionLink(`ALERTS{alertname="${rule.name}"}`)}>{rule.name}</Link>
name: <a href={createExternalExpressionLink(`ALERTS{alertname="${rule.name}"}`)}>{rule.name}</a>
</div>
<div>
expr: <Link to={createExpressionLink(rule.query)}>{rule.query}</Link>
expr: <a href={createExternalExpressionLink(rule.query)}>{rule.query}</a>
</div>
<div>
<div>labels:</div>
Expand Down
7 changes: 3 additions & 4 deletions pkg/ui/react-app/src/pages/rules/RulesContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import React, { FC } from 'react';
import { RouteComponentProps } from '@reach/router';
import { APIResponse } from '../../hooks/useFetch';
import { Alert, Table, Badge } from 'reactstrap';
import { Link } from '@reach/router';
import { formatRelative, createExpressionLink, humanizeDuration } from '../../utils';
import { formatRelative, createExternalExpressionLink, humanizeDuration } from '../../utils';
import { Rule } from '../../types/types';
import { now } from 'moment';

Expand All @@ -27,9 +26,9 @@ const GraphExpressionLink: FC<{ expr: string; title: string }> = props => {
return (
<>
<strong>{props.title}:</strong>
<Link className="ml-4" to={createExpressionLink(props.expr)}>
<a className="ml-4" href={createExternalExpressionLink(props.expr)}>
{props.expr}
</Link>
</a>
<br />
</>
);
Expand Down
16 changes: 12 additions & 4 deletions pkg/ui/react-app/src/thanos/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
DropdownToggle,
} from 'reactstrap';
import PathPrefixProps from '../types/PathPrefixProps';
import ThanosComponentProps from './types/ThanosComponentProps';

interface NavConfig {
name: string;
Expand All @@ -37,15 +36,24 @@ const navConfig: { [component: string]: (NavConfig | NavDropDown)[] } = {
],
},
],
rule: [
{ name: 'Alerts', uri: '/new/alerts' },
{ name: 'Rules', uri: '/new/rules' },
],
};

const Navigation: FC<PathPrefixProps & ThanosComponentProps> = ({ pathPrefix, thanosComponent }) => {
interface NavigationProps {
thanosComponent: string;
defaultRoute: string;
}

const Navigation: FC<PathPrefixProps & NavigationProps> = ({ pathPrefix, thanosComponent, defaultRoute }) => {
const [isOpen, setIsOpen] = useState(false);
const toggle = () => setIsOpen(!isOpen);
return (
<Navbar className="mb-3" dark color="dark" expand="md" fixed="top">
<NavbarToggler onClick={toggle} />
<Link className="navbar-brand" to={`${pathPrefix}/new/graph`}>
<Link className="navbar-brand" to={`${pathPrefix}/new${defaultRoute}`}>
Thanos - {thanosComponent[0].toUpperCase()}
{thanosComponent.substr(1, thanosComponent.length)}
</Link>
Expand Down Expand Up @@ -80,7 +88,7 @@ const Navigation: FC<PathPrefixProps & ThanosComponentProps> = ({ pathPrefix, th
<NavLink href="https://thanos.io/getting-started.md/">Help</NavLink>
</NavItem>
<NavItem>
<NavLink href={`${pathPrefix}/graph${window.location.search}`}>Classic UI</NavLink>
<NavLink href={`${pathPrefix}${defaultRoute}${window.location.search}`}>Classic UI</NavLink>
</NavItem>
</Nav>
</Collapse>
Expand Down
6 changes: 6 additions & 0 deletions pkg/ui/react-app/src/thanos/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
declare const THANOS_QUERY_URL: string;

export let queryURL = THANOS_QUERY_URL;
if (queryURL === '' || queryURL === '{{ .queryURL }}') {
queryURL = 'http://localhost:10902';
}
7 changes: 7 additions & 0 deletions pkg/ui/react-app/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import moment from 'moment-timezone';

import { PanelOptions, PanelType, PanelDefaultOptions } from '../pages/graph/Panel';
import { PanelMeta } from '../pages/graph/PanelList';
import { queryURL } from '../thanos/config';

export const generateID = () => {
return `_${Math.random()
Expand Down Expand Up @@ -223,6 +224,12 @@ export const encodePanelOptionsToQueryString = (panels: PanelMeta[]) => {
export const createExpressionLink = (expr: string) => {
return `../graph?g0.expr=${encodeURIComponent(expr)}&g0.tab=1&g0.stacked=0&g0.range_input=1h`;
};

export const createExternalExpressionLink = (expr: string) => {
const expLink = createExpressionLink(expr);
return `${queryURL}${expLink.replace(/^\.\./, '')}`;
};

export const mapObjEntries = <T, key extends keyof T, Z>(
o: T,
cb: ([k, v]: [string, T[key]], i: number, arr: [string, T[key]][]) => Z
Expand Down
7 changes: 6 additions & 1 deletion pkg/ui/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,13 @@ type Rule struct {
}

func NewRuleUI(logger log.Logger, reg prometheus.Registerer, ruleManager *thanosrules.Manager, queryURL, externalPrefix, prefixHeader string) *Rule {
tmplVariables := map[string]string{
"Component": component.Rule.String(),
"queryURL": queryURL,
}

return &Rule{
BaseUI: NewBaseUI(logger, "rule_menu.html", ruleTmplFuncs(queryURL), externalPrefix, prefixHeader, component.Rule),
BaseUI: NewBaseUI(logger, "rule_menu.html", ruleTmplFuncs(queryURL), tmplVariables, externalPrefix, prefixHeader, component.Rule),
externalPrefix: externalPrefix,
prefixHeader: prefixHeader,
ruleManager: ruleManager,
Expand Down
3 changes: 3 additions & 0 deletions pkg/ui/templates/rule_menu.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
<li class="nav-item">
<a class="nav-link" href="https://thanos.io/getting-started.md/" target="_blank">Help</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ pathPrefix }}/new/" target="_blank">New UI</a>
</li>
</ul>
</div>
</div>
Expand Down
11 changes: 4 additions & 7 deletions pkg/ui/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,16 @@ type BaseUI struct {
logger log.Logger
menuTmpl string
tmplFuncs template.FuncMap
tmplVariables map[string]string
externalPrefix, prefixHeader string
component component.Component
}

func NewBaseUI(logger log.Logger, menuTmpl string, funcMap template.FuncMap, externalPrefix, prefixHeader string, component component.Component) *BaseUI {
func NewBaseUI(logger log.Logger, menuTmpl string, funcMap template.FuncMap, tmplVariables map[string]string, externalPrefix, prefixHeader string, component component.Component) *BaseUI {
funcMap["pathPrefix"] = func() string { return "" }
funcMap["buildVersion"] = func() string { return version.Revision }

return &BaseUI{logger: logger, menuTmpl: menuTmpl, tmplFuncs: funcMap, externalPrefix: externalPrefix, prefixHeader: prefixHeader, component: component}
return &BaseUI{logger: logger, menuTmpl: menuTmpl, tmplFuncs: funcMap, tmplVariables: tmplVariables, externalPrefix: externalPrefix, prefixHeader: prefixHeader, component: component}
}
func (bu *BaseUI) serveStaticAsset(w http.ResponseWriter, req *http.Request) {
fp := route.Param(req.Context(), "filepath")
Expand Down Expand Up @@ -88,11 +89,7 @@ func (bu *BaseUI) serveReactIndex(index string, w http.ResponseWriter, req *http
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := tmpl.Execute(w, struct {
Component string
}{
Component: bu.component.String(),
}); err != nil {
if err := tmpl.Execute(w, bu.tmplVariables); err != nil {
level.Warn(bu.logger).Log("msg", "template expansion failed", "err", err)
}
}
Expand Down

0 comments on commit 69b8760

Please sign in to comment.