mirror of https://github.com/linkerd/linkerd2.git
UI tweaks: sidebar collapse, latency formatting, table row spacing (#361)
- reduce row spacing on tables to make them more compact - Rename TabbedMetricsTable to MetricsTable since it's not tabbed any more - Format latencies greater than 1000ms as seconds - Make sidebar collapsible - poll the /pods endpoint from the sidebar in order to refresh the list of deployments in the autocomplete - display the conduit namespace in the service mesh details table - Use floats rather than Col for more responsive layout (fixes #224)
This commit is contained in:
parent
01e694ad71
commit
8bc7c5acde
|
|
@ -288,6 +288,7 @@ spec:
|
|||
- "-static-dir=/dist"
|
||||
- "-template-dir=/templates"
|
||||
- "-uuid={{.UUID}}"
|
||||
- "-controller-namespace={{.Namespace}}"
|
||||
- "-log-level={{.ControllerLogLevel}}"
|
||||
|
||||
### Prometheus ###
|
||||
|
|
|
|||
|
|
@ -272,6 +272,7 @@ spec:
|
|||
- "-static-dir=/dist"
|
||||
- "-template-dir=/templates"
|
||||
- "-uuid=UUID"
|
||||
- "-controller-namespace=Namespace"
|
||||
- "-log-level=ControllerLogLevel"
|
||||
|
||||
### Prometheus ###
|
||||
|
|
|
|||
|
|
@ -19,13 +19,19 @@
|
|||
}
|
||||
|
||||
& .action-steps div {
|
||||
height: 41px;
|
||||
line-height: 41px;
|
||||
margin-bottom: 8px;
|
||||
max-width: 272px;
|
||||
height: 41px;
|
||||
|
||||
@media screen and (min-width: 1120px) {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
& .step-container {
|
||||
border-radius: calc(var(--base-width)*4);
|
||||
margin-right: var(--base-width);
|
||||
width: 98%
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { incompleteMeshMessage } from './util/CopyUtils.jsx';
|
||||
import React from 'react';
|
||||
import { Col, Row } from 'antd';
|
||||
import './../../css/cta.css';
|
||||
|
||||
export default class CallToAction extends React.Component {
|
||||
|
|
@ -10,37 +9,31 @@ export default class CallToAction extends React.Component {
|
|||
<div className="action summary">The service mesh was successfully installed!</div>
|
||||
|
||||
<div className="action-steps">
|
||||
<Row gutter={0}>
|
||||
<Col span={8}>
|
||||
<div className="step-container complete">
|
||||
<div className="icon-container">
|
||||
<i className="fa fa-check-circle" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="message"><p>Controller successfully installed</p></div>
|
||||
</div>
|
||||
</Col>
|
||||
<div className="step-container complete">
|
||||
<div className="icon-container">
|
||||
<i className="fa fa-check-circle" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="message"><p>Controller successfully installed</p></div>
|
||||
</div>
|
||||
|
||||
<Col span={8}>
|
||||
<div className="step-container complete">
|
||||
<div className="icon-container">
|
||||
<i className="fa fa-check-circle" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="message">{this.props.numDeployments || 0} deployments detected</div>
|
||||
</div>
|
||||
</Col>
|
||||
<div className="step-container complete">
|
||||
<div className="icon-container">
|
||||
<i className="fa fa-check-circle" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="message">{this.props.numDeployments || 0} deployments detected</div>
|
||||
</div>
|
||||
|
||||
<Col span={8}>
|
||||
<div className="step-container incomplete">
|
||||
<div className="icon-container">
|
||||
<i className="fa fa-circle-o" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="message">Connect your first deployment</div>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<div className="step-container incomplete">
|
||||
<div className="icon-container">
|
||||
<i className="fa fa-circle-o" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="message">Connect your first deployment</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{incompleteMeshMessage()}
|
||||
<div className="clearfix">
|
||||
{incompleteMeshMessage()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ import _ from 'lodash';
|
|||
import CallToAction from './CallToAction.jsx';
|
||||
import ConduitSpinner from "./ConduitSpinner.jsx";
|
||||
import ErrorBanner from './ErrorBanner.jsx';
|
||||
import MetricsTable from './MetricsTable.jsx';
|
||||
import PageHeader from './PageHeader.jsx';
|
||||
import React from 'react';
|
||||
import TabbedMetricsTable from './TabbedMetricsTable.jsx';
|
||||
import { emptyMetric, getPodsByDeployment, processRollupMetrics } from './util/MetricUtils.js';
|
||||
import './../../css/deployments.css';
|
||||
import 'whatwg-fetch';
|
||||
|
|
@ -88,7 +88,7 @@ export default class DeploymentsList extends React.Component {
|
|||
{ _.isEmpty(this.state.metrics) ?
|
||||
<CallToAction numDeployments={_.size(this.state.metrics)} /> :
|
||||
<div className="deployments-list">
|
||||
<TabbedMetricsTable
|
||||
<MetricsTable
|
||||
resource="deployment"
|
||||
metrics={this.state.metrics}
|
||||
api={this.api} />
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ const columnDefinitions = (sortable = true, resource, ConduitLink) => {
|
|||
title: resource.title,
|
||||
dataIndex: "name",
|
||||
key: "name",
|
||||
defaultSortOrder: 'ascend',
|
||||
width: 150,
|
||||
sorter: sortable ? (a, b) => (a.name || "").localeCompare(b.name) : false,
|
||||
render: name => !resource.url ? name :
|
||||
|
|
@ -30,7 +31,6 @@ const columnDefinitions = (sortable = true, resource, ConduitLink) => {
|
|||
title: "Request Rate",
|
||||
dataIndex: "requestRate",
|
||||
key: "requestRateRollup",
|
||||
defaultSortOrder: 'descend',
|
||||
className: "numeric",
|
||||
sorter: sortable ? (a, b) => numericSort(a.requestRate, b.requestRate) : false,
|
||||
render: d => metricToFormatter["REQUEST_RATE"](d)
|
||||
|
|
@ -72,7 +72,7 @@ const columnDefinitions = (sortable = true, resource, ConduitLink) => {
|
|||
|
||||
const numericSort = (a, b) => (_.isNil(a) ? -1 : a) - (_.isNil(b) ? -1 : b);
|
||||
|
||||
export default class TabbedMetricsTable extends React.Component {
|
||||
export default class MetricsTable extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.api = this.props.api;
|
||||
|
|
@ -100,6 +100,7 @@ export default class TabbedMetricsTable extends React.Component {
|
|||
columns={columns}
|
||||
pagination={false}
|
||||
className="conduit-table"
|
||||
rowKey={r => r.name} />);
|
||||
rowKey={r => r.name}
|
||||
size="middle" />);
|
||||
}
|
||||
}
|
||||
|
|
@ -149,10 +149,11 @@ export default class ServiceMesh extends React.Component {
|
|||
getServiceMeshDetails() {
|
||||
return [
|
||||
{ key: 1, name: "Conduit version", value: this.props.releaseVersion },
|
||||
{ key: 2, name: "Control plane components", value: this.componentCount() },
|
||||
{ key: 3, name: "Added deployments", value: this.addedDeploymentCount() },
|
||||
{ key: 4, name: "Unadded deployments", value: this.unaddedDeploymentCount() },
|
||||
{ key: 5, name: "Data plane proxies", value: this.proxyCount() }
|
||||
{ key: 2, name: "Conduit namespace", value: this.props.controllerNamespace },
|
||||
{ key: 3, name: "Control plane components", value: this.componentCount() },
|
||||
{ key: 4, name: "Added deployments", value: this.addedDeploymentCount() },
|
||||
{ key: 5, name: "Unadded deployments", value: this.unaddedDeploymentCount() },
|
||||
{ key: 6, name: "Data plane proxies", value: this.proxyCount() }
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -264,7 +265,8 @@ export default class ServiceMesh extends React.Component {
|
|||
className="conduit-table"
|
||||
dataSource={this.getServiceMeshDetails()}
|
||||
columns={serviceMeshDetailsColumns}
|
||||
pagination={false} />
|
||||
pagination={false}
|
||||
size="middle" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -15,20 +15,37 @@ export default class Sidebar extends React.Component {
|
|||
this.api = this.props.api;
|
||||
this.filterDeployments = this.filterDeployments.bind(this);
|
||||
this.onAutocompleteSelect = this.onAutocompleteSelect.bind(this);
|
||||
this.loadFromServer();
|
||||
this.loadFromServer = this.loadFromServer.bind(this);
|
||||
|
||||
this.state = {
|
||||
pollingInterval: 10000, // longer, this doesn't need to be updated as often
|
||||
pendingRequests: false,
|
||||
autocompleteValue: '',
|
||||
deployments: [],
|
||||
filteredDeployments: []
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.loadFromServer();
|
||||
this.timerId = window.setInterval(this.loadFromServer, this.state.pollingInterval);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.clearInterval(this.timerId);
|
||||
}
|
||||
|
||||
loadFromServer() {
|
||||
if (this.state.pendingRequests) {
|
||||
return; // don't make more requests if the ones we sent haven't completed
|
||||
}
|
||||
this.setState({ pendingRequests: true });
|
||||
|
||||
this.api.fetchPods().then(r => {
|
||||
let deploys = _.map(getPodsByDeployment(r.pods), 'name');
|
||||
|
||||
this.setState({
|
||||
pendingRequests: false,
|
||||
deployments: deploys,
|
||||
filteredDeployments: deploys
|
||||
});
|
||||
|
|
@ -60,6 +77,7 @@ export default class Sidebar extends React.Component {
|
|||
render() {
|
||||
let normalizedPath = this.props.location.pathname.replace(this.props.pathPrefix, "");
|
||||
let ConduitLink = this.api.ConduitLink;
|
||||
|
||||
return (
|
||||
<div className="sidebar">
|
||||
<div className="list-container">
|
||||
|
|
@ -88,11 +106,16 @@ export default class Sidebar extends React.Component {
|
|||
</Menu.Item>
|
||||
</Menu>
|
||||
|
||||
<SocialLinks />
|
||||
|
||||
<Version
|
||||
releaseVersion={this.props.releaseVersion}
|
||||
uuid={this.props.uuid} />
|
||||
{
|
||||
!this.props.collapsed ? (
|
||||
<React.Fragment>
|
||||
<SocialLinks />
|
||||
<Version
|
||||
releaseVersion={this.props.releaseVersion}
|
||||
uuid={this.props.uuid} />
|
||||
</React.Fragment>
|
||||
) : null
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ export default class StatusTable extends React.Component {
|
|||
columns={tableCols}
|
||||
pagination={false}
|
||||
className="conduit-table"
|
||||
rowKey={r => r.name} />);
|
||||
rowKey={r => r.name}
|
||||
size="middle" />);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import _ from 'lodash';
|
||||
import MetricsTable from './MetricsTable.jsx';
|
||||
import React from 'react';
|
||||
import { rowGutter } from './util/Utils.js';
|
||||
import TabbedMetricsTable from './TabbedMetricsTable.jsx';
|
||||
import { Col, Row } from 'antd';
|
||||
|
||||
export default class UpstreamDownstreamTables extends React.Component {
|
||||
|
|
@ -17,7 +17,7 @@ export default class UpstreamDownstreamTables extends React.Component {
|
|||
<div className="border-container border-neutral subsection-header">
|
||||
<div className="border-container-content subsection-header">Upstreams</div>
|
||||
</div>
|
||||
<TabbedMetricsTable
|
||||
<MetricsTable
|
||||
resource={`upstream_${this.props.resourceType}`}
|
||||
resourceName={this.props.resourceName}
|
||||
metrics={this.props.upstreamMetrics}
|
||||
|
|
@ -30,7 +30,7 @@ export default class UpstreamDownstreamTables extends React.Component {
|
|||
<div className="border-container border-neutral subsection-header">
|
||||
<div className="border-container-content subsection-header">Downstreams</div>
|
||||
</div>
|
||||
<TabbedMetricsTable
|
||||
<MetricsTable
|
||||
resource={`downstream_${this.props.resourceType}`}
|
||||
resourceName={this.props.resourceName}
|
||||
metrics={this.props.downstreamMetrics}
|
||||
|
|
|
|||
|
|
@ -11,12 +11,23 @@ export const rowGutter = 3 * baseWidth;
|
|||
* Number formatters
|
||||
*/
|
||||
const successRateFormatter = d3.format(".2%");
|
||||
const latencySecFormatter = d3.format(".3f");
|
||||
const latencyFormatter = d3.format(",");
|
||||
|
||||
const formatLatency = m => {
|
||||
if (_.isNil(m)) {
|
||||
return "---";
|
||||
} else if (m < 1000) {
|
||||
return `${latencyFormatter(m)} ms`;
|
||||
} else {
|
||||
return `${latencySecFormatter(m / 1000)} s`;
|
||||
}
|
||||
};
|
||||
|
||||
export const metricToFormatter = {
|
||||
"REQUEST_RATE": m => _.isNil(m) ? "---" : styleNum(m, " RPS", true),
|
||||
"SUCCESS_RATE": m => _.isNil(m) ? "---" : successRateFormatter(m),
|
||||
"LATENCY": m => `${_.isNil(m) ? "---" : latencyFormatter(m)} ms`
|
||||
"LATENCY": formatLatency
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -21,18 +21,31 @@ if (proxyPathMatch) {
|
|||
|
||||
let api = ApiHelpers(pathPrefix);
|
||||
|
||||
ReactDOM.render((
|
||||
let applicationHtml = hideSidebar => (
|
||||
<BrowserRouter>
|
||||
<Layout>
|
||||
<Layout.Sider width="310">
|
||||
<Route render={routeProps => <Sidebar {...routeProps} goVersion={appData.goVersion} releaseVersion={appData.releaseVersion} api={api} pathPrefix={pathPrefix} uuid={appData.uuid} />} />
|
||||
<Layout.Sider
|
||||
width="310"
|
||||
breakpoint="lg"
|
||||
collapsible={true}
|
||||
collapsedWidth={0}
|
||||
onCollapse={onSidebarCollapse}>
|
||||
<Route
|
||||
render={routeProps => (<Sidebar
|
||||
{...routeProps}
|
||||
goVersion={appData.goVersion}
|
||||
releaseVersion={appData.releaseVersion}
|
||||
api={api}
|
||||
collapsed={hideSidebar}
|
||||
pathPrefix={pathPrefix}
|
||||
uuid={appData.uuid} />)} />
|
||||
</Layout.Sider>
|
||||
<Layout>
|
||||
<Layout.Content style={{ margin: '0 0', padding: 0, background: '#fff' }}>
|
||||
<div className="main-content">
|
||||
<Switch>
|
||||
<Redirect exact from={`${pathPrefix}/`} to={`${pathPrefix}/servicemesh`} />
|
||||
<Route path={`${pathPrefix}/servicemesh`} render={() => <ServiceMesh api={api} releaseVersion={appData.releaseVersion} />} />
|
||||
<Route path={`${pathPrefix}/servicemesh`} render={() => <ServiceMesh api={api} releaseVersion={appData.releaseVersion} controllerNamespace={appData.controllerNamespace} />} />
|
||||
<Route path={`${pathPrefix}/deployments`} render={() => <DeploymentsList api={api} />} />
|
||||
<Route path={`${pathPrefix}/deployment`} render={props => <DeploymentDetail api={api} location={props.location} />} />
|
||||
<Route component={NoMatch} />
|
||||
|
|
@ -42,4 +55,10 @@ ReactDOM.render((
|
|||
</Layout>
|
||||
</Layout>
|
||||
</BrowserRouter>
|
||||
), appMain);
|
||||
);
|
||||
|
||||
const onSidebarCollapse = isHidden => {
|
||||
ReactDOM.render(applicationHtml(isHidden), appMain);
|
||||
};
|
||||
|
||||
ReactDOM.render(applicationHtml(false), appMain);
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ describe('DeploymentsList', () => {
|
|||
expect(component.find("DeploymentsList").length).to.equal(1);
|
||||
expect(component.find("ConduitSpinner").length).to.equal(0);
|
||||
expect(component.find("CallToAction").length).to.equal(0);
|
||||
expect(component.find("TabbedMetricsTable").length).to.equal(1);
|
||||
expect(component.find("MetricsTable").length).to.equal(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ describe('Utils', () => {
|
|||
let undefinedMetric;
|
||||
expect(metricToFormatter["REQUEST_RATE"](undefinedMetric)).to.equal('---');
|
||||
expect(metricToFormatter["SUCCESS_RATE"](undefinedMetric)).to.equal('---');
|
||||
expect(metricToFormatter["LATENCY"](undefinedMetric)).to.equal('--- ms');
|
||||
expect(metricToFormatter["LATENCY"](undefinedMetric)).to.equal('---');
|
||||
});
|
||||
|
||||
it('formats requests with rounding and unit', () => {
|
||||
|
|
@ -68,12 +68,15 @@ describe('Utils', () => {
|
|||
expect(metricToFormatter["REQUEST_RATE"](99999)).to.equal('100k RPS');
|
||||
});
|
||||
|
||||
it('formats latency', () => {
|
||||
it('formats subsecond latency as ms', () => {
|
||||
expect(metricToFormatter["LATENCY"](99)).to.equal('99 ms');
|
||||
expect(metricToFormatter["LATENCY"](999)).to.equal('999 ms');
|
||||
expect(metricToFormatter["LATENCY"](1000)).to.equal('1,000 ms');
|
||||
expect(metricToFormatter["LATENCY"](9999)).to.equal('9,999 ms');
|
||||
expect(metricToFormatter["LATENCY"](99999)).to.equal('99,999 ms');
|
||||
});
|
||||
|
||||
it('formats latency greater than 1s as s', () => {
|
||||
expect(metricToFormatter["LATENCY"](1000)).to.equal('1.000 s');
|
||||
expect(metricToFormatter["LATENCY"](9999)).to.equal('9.999 s');
|
||||
expect(metricToFormatter["LATENCY"](99999)).to.equal('99.999 s');
|
||||
});
|
||||
|
||||
it('formats success rate', () => {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ func main() {
|
|||
reload := flag.Bool("reload", true, "reloading set to true or false")
|
||||
webpackDevServer := flag.String("webpack-dev-server", "", "use webpack to serve static assets; frontend will use this instead of static-dir")
|
||||
logLevel := flag.String("log-level", log.InfoLevel.String(), "log level, must be one of: panic, fatal, error, warn, info, debug")
|
||||
controllerNamespace := flag.String("controller-namespace", "", "the k8s namespace in which Conduit is running")
|
||||
printVersion := version.VersionFlag()
|
||||
flag.Parse()
|
||||
|
||||
|
|
@ -51,7 +52,7 @@ func main() {
|
|||
stop := make(chan os.Signal, 1)
|
||||
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
server := srv.NewServer(*addr, *templateDir, *staticDir, *uuid, *webpackDevServer, *reload, client)
|
||||
server := srv.NewServer(*addr, *templateDir, *staticDir, *uuid, *controllerNamespace, *webpackDevServer, *reload, client)
|
||||
|
||||
go func() {
|
||||
log.Infof("starting HTTP server on %+v", *addr)
|
||||
|
|
|
|||
|
|
@ -13,15 +13,16 @@ type (
|
|||
serveFile func(http.ResponseWriter, string, string, interface{}) error
|
||||
|
||||
handler struct {
|
||||
render renderTemplate
|
||||
serveFile serveFile
|
||||
apiClient pb.ApiClient
|
||||
uuid string
|
||||
render renderTemplate
|
||||
serveFile serveFile
|
||||
apiClient pb.ApiClient
|
||||
uuid string
|
||||
controllerNamespace string
|
||||
}
|
||||
)
|
||||
|
||||
func (h *handler) handleIndex(w http.ResponseWriter, req *http.Request, p httprouter.Params) {
|
||||
params := appParams{UUID: h.uuid}
|
||||
params := appParams{UUID: h.uuid, ControllerNamespace: h.controllerNamespace}
|
||||
|
||||
version, err := h.apiClient.Version(req.Context(), &pb.Empty{}) // TODO: remove and call /api/version from web app
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -45,11 +45,18 @@ func TestHandleIndex(t *testing.T) {
|
|||
t.Errorf("Expected: %+v", header)
|
||||
}
|
||||
|
||||
expectedVersionDiv := "<div class=\"main\" id=\"main\" data-release-version=\"0.3.3\" data-go-version=\"the best one\" data-uuid=\"\">"
|
||||
|
||||
actualBody := recorder.Body.String()
|
||||
|
||||
if !strings.Contains(actualBody, expectedVersionDiv) {
|
||||
t.Fatalf("Expected string [%s] to be present in [%s]", expectedVersionDiv, actualBody)
|
||||
expectedSubstrings := []string{
|
||||
"<div class=\"main\" id=\"main\"",
|
||||
"data-release-version=\"0.3.3\"",
|
||||
"data-go-version=\"the best one\"",
|
||||
"data-controller-namespace=\"\"",
|
||||
"data-uuid=\"\"",
|
||||
}
|
||||
for _, expectedSubstring := range expectedSubstrings {
|
||||
if !strings.Contains(actualBody, expectedSubstring) {
|
||||
t.Fatalf("Expected string [%s] to be present in [%s]", expectedSubstring, actualBody)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,10 +37,11 @@ type (
|
|||
Contents interface{}
|
||||
}
|
||||
appParams struct {
|
||||
Data *pb.VersionInfo
|
||||
UUID string
|
||||
Error bool
|
||||
ErrorMessage string
|
||||
Data *pb.VersionInfo
|
||||
UUID string
|
||||
ControllerNamespace string
|
||||
Error bool
|
||||
ErrorMessage string
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -49,7 +50,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
s.router.ServeHTTP(w, req)
|
||||
}
|
||||
|
||||
func NewServer(addr, templateDir, staticDir, uuid, webpackDevServer string, reload bool, apiClient pb.ApiClient) *http.Server {
|
||||
func NewServer(addr, templateDir, staticDir, uuid, controllerNamespace, webpackDevServer string, reload bool, apiClient pb.ApiClient) *http.Server {
|
||||
server := &Server{
|
||||
templateDir: templateDir,
|
||||
staticDir: staticDir,
|
||||
|
|
@ -65,10 +66,11 @@ func NewServer(addr, templateDir, staticDir, uuid, webpackDevServer string, relo
|
|||
|
||||
wrappedServer := util.WithTelemetry(server)
|
||||
handler := &handler{
|
||||
apiClient: apiClient,
|
||||
render: server.RenderTemplate,
|
||||
serveFile: server.serveFile,
|
||||
uuid: uuid,
|
||||
apiClient: apiClient,
|
||||
render: server.RenderTemplate,
|
||||
serveFile: server.serveFile,
|
||||
uuid: uuid,
|
||||
controllerNamespace: controllerNamespace,
|
||||
}
|
||||
|
||||
httpServer := &http.Server{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
{{ define "content" }}
|
||||
<div class="main" id="main" data-release-version="{{.Data.ReleaseVersion}}" data-go-version="{{.Data.GoVersion}}" data-uuid="{{.UUID}}">
|
||||
<div class="main" id="main"
|
||||
data-release-version="{{.Data.ReleaseVersion}}"
|
||||
data-go-version="{{.Data.GoVersion}}"
|
||||
data-controller-namespace="{{.ControllerNamespace}}"
|
||||
data-uuid="{{.UUID}}">
|
||||
{{ if .Error }}
|
||||
<p>Failed to call public API: {{ .ErrorMessage }}</p>
|
||||
{{ end }}
|
||||
|
|
|
|||
Loading…
Reference in New Issue