mirror of https://github.com/linkerd/linkerd2.git
Add Jaeger links to the Linkerd dashboard (#4177)
* Add Jaeger reverse proxy * add jaegerLink to the metrics table * update MetricsTable tests * Add optional jaeger link * rename grafana_proxy to reverse_proxy Signed-off-by: Tarun Pothulapati <tarunpothulapati@outlook.com>
This commit is contained in:
parent
2a95d373c4
commit
2be43a5f9d
|
@ -206,7 +206,9 @@ spec:
|
|||
{{- include "partials.proxy.labels" .Values.global.proxy | nindent 8}}
|
||||
spec:
|
||||
containers:
|
||||
- image: {{.Values.jaeger.image}}
|
||||
- args:
|
||||
- --query.base-path=/jaeger
|
||||
image: {{.Values.jaeger.image}}
|
||||
imagePullPolicy: {{.Values.global.imagePullPolicy}}
|
||||
name: jaeger
|
||||
ports:
|
||||
|
|
|
@ -64,6 +64,9 @@ spec:
|
|||
- args:
|
||||
- -api-addr=linkerd-controller-api.{{.Values.global.namespace}}.svc.{{.Values.global.clusterDomain}}:8085
|
||||
- -grafana-addr=linkerd-grafana.{{.Values.global.namespace}}.svc.{{.Values.global.clusterDomain}}:3000
|
||||
{{- if .Values.tracing.enabled }}
|
||||
- -jaeger-addr={{.Values.tracing.jaeger.name}}.{{.Values.global.namespace}}.svc.{{.Values.global.clusterDomain}}:16686
|
||||
{{- end}}
|
||||
- -controller-namespace={{.Values.global.namespace}}
|
||||
- -log-level={{.Values.controllerLogLevel}}
|
||||
{{- if .Values.enforcedHostRegexp }}
|
||||
|
|
|
@ -801,6 +801,7 @@ spec:
|
|||
- args:
|
||||
- -api-addr=linkerd-controller-api.linkerd.svc.cluster.local:8085
|
||||
- -grafana-addr=linkerd-grafana.linkerd.svc.cluster.local:3000
|
||||
- -jaeger-addr=linkerd-jaeger.linkerd.svc.cluster.local:16686
|
||||
- -controller-namespace=linkerd
|
||||
- -log-level=info
|
||||
- -enforced-host=^(localhost|127\.0\.0\.1|linkerd-web\.linkerd\.svc\.cluster\.local|linkerd-web\.linkerd\.svc|\[::1\])(:\d+)?$
|
||||
|
@ -2615,7 +2616,9 @@ spec:
|
|||
linkerd.io/proxy-deployment: linkerd-jaeger
|
||||
spec:
|
||||
containers:
|
||||
- image: jaegertracing/all-in-one:1.17.1
|
||||
- args:
|
||||
- --query.base-path=/jaeger
|
||||
image: jaegertracing/all-in-one:1.17.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: jaeger
|
||||
ports:
|
||||
|
|
|
@ -1714,6 +1714,7 @@ spec:
|
|||
- args:
|
||||
- -api-addr=linkerd-controller-api.linkerd.svc.cluster.local:8085
|
||||
- -grafana-addr=linkerd-grafana.linkerd.svc.cluster.local:3000
|
||||
- -jaeger-addr=linkerd-jaeger.linkerd.svc.cluster.local:16686
|
||||
- -controller-namespace=linkerd
|
||||
- -log-level=info
|
||||
- -enforced-host=^(localhost|127\.0\.0\.1|linkerd-web\.linkerd\.svc\.cluster\.local|linkerd-web\.linkerd\.svc|\[::1\])(:\d+)?$
|
||||
|
@ -3512,7 +3513,9 @@ spec:
|
|||
linkerd.io/proxy-deployment: linkerd-jaeger
|
||||
spec:
|
||||
containers:
|
||||
- image: jaegertracing/all-in-one:1.17.1
|
||||
- args:
|
||||
- --query.base-path=/jaeger
|
||||
image: jaegertracing/all-in-one:1.17.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: jaeger
|
||||
ports:
|
||||
|
|
|
@ -1641,6 +1641,7 @@ spec:
|
|||
- args:
|
||||
- -api-addr=linkerd-controller-api.linkerd.svc.cluster.local:8085
|
||||
- -grafana-addr=linkerd-grafana.linkerd.svc.cluster.local:3000
|
||||
- -jaeger-addr=linkerd-jaeger.linkerd.svc.cluster.local:16686
|
||||
- -controller-namespace=linkerd
|
||||
- -log-level=info
|
||||
- -enforced-host=^(localhost|127\.0\.0\.1|linkerd-web\.linkerd\.svc\.cluster\.local|linkerd-web\.linkerd\.svc|\[::1\])(:\d+)?$
|
||||
|
@ -3481,7 +3482,9 @@ spec:
|
|||
linkerd.io/proxy-deployment: linkerd-jaeger
|
||||
spec:
|
||||
containers:
|
||||
- image: jaegertracing/all-in-one:1.17.1
|
||||
- args:
|
||||
- --query.base-path=/jaeger
|
||||
image: jaegertracing/all-in-one:1.17.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: jaeger
|
||||
ports:
|
||||
|
|
|
@ -807,6 +807,7 @@ spec:
|
|||
- args:
|
||||
- -api-addr=linkerd-controller-api.linkerd.svc.cluster.local:8085
|
||||
- -grafana-addr=linkerd-grafana.linkerd.svc.cluster.local:3000
|
||||
- -jaeger-addr=linkerd-jaeger.linkerd.svc.cluster.local:16686
|
||||
- -controller-namespace=linkerd
|
||||
- -log-level=info
|
||||
- -enforced-host=^(localhost|127\.0\.0\.1|linkerd-web\.linkerd\.svc\.cluster\.local|linkerd-web\.linkerd\.svc|\[::1\])(:\d+)?$
|
||||
|
@ -2635,7 +2636,9 @@ spec:
|
|||
linkerd.io/proxy-deployment: linkerd-jaeger
|
||||
spec:
|
||||
containers:
|
||||
- image: jaegertracing/all-in-one:1.17.1
|
||||
- args:
|
||||
- --query.base-path=/jaeger
|
||||
image: jaegertracing/all-in-one:1.17.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: jaeger
|
||||
ports:
|
||||
|
|
|
@ -1647,6 +1647,7 @@ spec:
|
|||
- args:
|
||||
- -api-addr=linkerd-controller-api.linkerd.svc.cluster.local:8085
|
||||
- -grafana-addr=linkerd-grafana.linkerd.svc.cluster.local:3000
|
||||
- -jaeger-addr=linkerd-jaeger.linkerd.svc.cluster.local:16686
|
||||
- -controller-namespace=linkerd
|
||||
- -log-level=info
|
||||
- -enforced-host=^(localhost|127\.0\.0\.1|linkerd-web\.linkerd\.svc\.cluster\.local|linkerd-web\.linkerd\.svc|\[::1\])(:\d+)?$
|
||||
|
@ -3499,7 +3500,9 @@ spec:
|
|||
linkerd.io/proxy-deployment: linkerd-jaeger
|
||||
spec:
|
||||
containers:
|
||||
- image: jaegertracing/all-in-one:1.17.1
|
||||
- args:
|
||||
- --query.base-path=/jaeger
|
||||
image: jaegertracing/all-in-one:1.17.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: jaeger
|
||||
ports:
|
||||
|
|
|
@ -1647,6 +1647,7 @@ spec:
|
|||
- args:
|
||||
- -api-addr=linkerd-controller-api.linkerd.svc.cluster.local:8085
|
||||
- -grafana-addr=linkerd-grafana.linkerd.svc.cluster.local:3000
|
||||
- -jaeger-addr=linkerd-jaeger.linkerd.svc.cluster.local:16686
|
||||
- -controller-namespace=linkerd
|
||||
- -log-level=info
|
||||
- -enforced-host=^(localhost|127\.0\.0\.1|linkerd-web\.linkerd\.svc\.cluster\.local|linkerd-web\.linkerd\.svc|\[::1\])(:\d+)?$
|
||||
|
@ -3501,7 +3502,9 @@ spec:
|
|||
linkerd.io/proxy-deployment: linkerd-jaeger
|
||||
spec:
|
||||
containers:
|
||||
- image: jaegertracing/all-in-one:1.17.1
|
||||
- args:
|
||||
- --query.base-path=/jaeger
|
||||
image: jaegertracing/all-in-one:1.17.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: jaeger
|
||||
ports:
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import _isEmpty from 'lodash/isEmpty';
|
||||
import { jaegerIcon } from './util/SvgWrappers.jsx';
|
||||
|
||||
function jaegerQuery(name, namespace, resource) {
|
||||
if (_isEmpty(namespace)) {
|
||||
return `{"linkerd.io/workload-ns"%3A"${name}"}`;
|
||||
} else if (resource === 'pod') {
|
||||
return `{"hostname"%3A"${name}"%2C"linkerd.io/workload-ns"%3A"${namespace}"}`;
|
||||
} else {
|
||||
return `{"linkerd.io%2Fproxy-${resource}"%3A"${name}"%2C"linkerd.io/workload-ns"%3A"${namespace}"}`;
|
||||
}
|
||||
}
|
||||
|
||||
const JaegerLink = ({ PrefixedLink, name, namespace, resource }) => {
|
||||
const link = `/jaeger/search?service=linkerd-proxy&tags=${jaegerQuery(name, namespace, resource)}`;
|
||||
|
||||
return (
|
||||
<PrefixedLink
|
||||
to={link}
|
||||
targetBlank>
|
||||
|
||||
{jaegerIcon}
|
||||
</PrefixedLink>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
JaegerLink.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
namespace: PropTypes.string,
|
||||
PrefixedLink: PropTypes.func.isRequired,
|
||||
resource: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
JaegerLink.defaultProps = {
|
||||
namespace: '',
|
||||
};
|
||||
|
||||
export default JaegerLink;
|
|
@ -3,6 +3,7 @@ import BaseTable from './BaseTable.jsx';
|
|||
import ErrorModal from './ErrorModal.jsx';
|
||||
import GrafanaLink from './GrafanaLink.jsx';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import JaegerLink from './JaegerLink.jsx';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import SuccessRateMiniChart from './util/SuccessRateMiniChart.jsx';
|
||||
|
@ -110,7 +111,7 @@ const trafficSplitDetailColumns = [
|
|||
},
|
||||
];
|
||||
|
||||
const columnDefinitions = (resource, showNamespaceColumn, showNameColumn, PrefixedLink, isTcpTable, grafana) => {
|
||||
const columnDefinitions = (resource, showNamespaceColumn, showNameColumn, PrefixedLink, isTcpTable, grafana, jaeger) => {
|
||||
const isAuthorityTable = resource === 'authority';
|
||||
const isTrafficSplitTable = resource === 'trafficsplit';
|
||||
const isMultiResourceTable = resource === 'multi_resource';
|
||||
|
@ -154,6 +155,26 @@ const columnDefinitions = (resource, showNamespaceColumn, showNameColumn, Prefix
|
|||
},
|
||||
};
|
||||
|
||||
|
||||
const jaegerColumn = {
|
||||
title: 'Jaeger',
|
||||
key: 'JaegerDashboard',
|
||||
isNumeric: true,
|
||||
render: row => {
|
||||
if (!isAuthorityTable && (!row.added || _get(row, 'pods.totalPods') === '0')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<JaegerLink
|
||||
name={row.name}
|
||||
namespace={row.namespace}
|
||||
resource={row.type}
|
||||
PrefixedLink={PrefixedLink} />
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
const nameColumn = {
|
||||
title: isMultiResourceTable ? 'Resource' : friendlyTitle(resource).singular,
|
||||
dataIndex: 'name',
|
||||
|
@ -200,8 +221,13 @@ const columnDefinitions = (resource, showNamespaceColumn, showNameColumn, Prefix
|
|||
columns.splice(1, 0, meshedColumn);
|
||||
}
|
||||
|
||||
if (!isTrafficSplitTable && grafana !== '') {
|
||||
columns = columns.concat(grafanaColumn);
|
||||
if (!isTrafficSplitTable) {
|
||||
if (grafana !== '') {
|
||||
columns = columns.concat(grafanaColumn);
|
||||
}
|
||||
if (jaeger !== '') {
|
||||
columns = columns.concat(jaegerColumn);
|
||||
}
|
||||
}
|
||||
|
||||
if (!showNamespaceColumn) {
|
||||
|
@ -224,12 +250,12 @@ const preprocessMetrics = metrics => {
|
|||
return tableData;
|
||||
};
|
||||
|
||||
const MetricsTable = ({ metrics, resource, showNamespaceColumn, showName, title, api, isTcpTable, selectedNamespace, grafana }) => {
|
||||
const MetricsTable = ({ metrics, resource, showNamespaceColumn, showName, title, api, isTcpTable, selectedNamespace, grafana, jaeger }) => {
|
||||
const showNsColumn = resource === 'namespace' || selectedNamespace !== '_all' ? false : showNamespaceColumn;
|
||||
const showNameColumn = resource !== 'trafficsplit' ? true : showName;
|
||||
let orderBy = 'name';
|
||||
if (resource === 'trafficsplit' && !showNameColumn) { orderBy = 'leaf'; }
|
||||
const columns = columnDefinitions(resource, showNsColumn, showNameColumn, api.PrefixedLink, isTcpTable, grafana);
|
||||
const columns = columnDefinitions(resource, showNsColumn, showNameColumn, api.PrefixedLink, isTcpTable, grafana, jaeger);
|
||||
const rows = preprocessMetrics(metrics);
|
||||
return (
|
||||
<BaseTable
|
||||
|
@ -251,6 +277,7 @@ MetricsTable.propTypes = {
|
|||
metrics: PropTypes.arrayOf(processedMetricsPropType),
|
||||
resource: PropTypes.string.isRequired,
|
||||
selectedNamespace: PropTypes.string.isRequired,
|
||||
jaeger: PropTypes.string,
|
||||
showName: PropTypes.bool,
|
||||
showNamespaceColumn: PropTypes.bool,
|
||||
title: PropTypes.string,
|
||||
|
@ -261,6 +288,7 @@ MetricsTable.defaultProps = {
|
|||
showNamespaceColumn: true,
|
||||
showName: true,
|
||||
title: '',
|
||||
jaeger: '',
|
||||
isTcpTable: false,
|
||||
metrics: [],
|
||||
};
|
||||
|
|
|
@ -84,6 +84,20 @@ describe('Tests for <MetricsTable>', () => {
|
|||
expect(table.props().tableColumns).toHaveLength(8);
|
||||
});
|
||||
|
||||
it('render table with all columens including jaeger', () => {
|
||||
let extraProps = _merge({}, defaultProps, {
|
||||
metrics: [],
|
||||
resource: "deployment",
|
||||
showNamespaceColumn: false,
|
||||
jaeger: 'jaeger.xyz'
|
||||
});
|
||||
const component = mount(routerWrap(MetricsTable, extraProps));
|
||||
const table = component.find("BaseTable");
|
||||
|
||||
expect(table).toBeDefined();
|
||||
expect(table.props().tableColumns).toHaveLength(9);
|
||||
});
|
||||
|
||||
it('adds apex, leaf and weight columns, and omits meshed and grafana column, for a trafficsplit resource', () => {
|
||||
let extraProps = _merge({}, defaultProps, { metrics: [], resource: "trafficsplit"});
|
||||
const component = mount(routerWrap(MetricsTable, extraProps));
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -29,6 +29,7 @@ func main() {
|
|||
metricsAddr := cmd.String("metrics-addr", ":9994", "address to serve scrapable metrics on")
|
||||
apiAddr := cmd.String("api-addr", "127.0.0.1:8085", "address of the linkerd-controller-api service")
|
||||
grafanaAddr := cmd.String("grafana-addr", "", "address of the linkerd-grafana service")
|
||||
jaegerAddr := cmd.String("jaeger-addr", "", "address of the jaeger service")
|
||||
templateDir := cmd.String("template-dir", "templates", "directory to search for template files")
|
||||
staticDir := cmd.String("static-dir", "app/dist", "directory to search for static files")
|
||||
reload := cmd.Bool("reload", true, "reloading set to true or false")
|
||||
|
@ -97,7 +98,7 @@ func main() {
|
|||
log.Fatalf("invalid --enforced-host parameter: %s", err)
|
||||
}
|
||||
|
||||
server := srv.NewServer(*addr, *grafanaAddr, *templateDir, *staticDir, uuid,
|
||||
server := srv.NewServer(*addr, *grafanaAddr, *jaegerAddr, *templateDir, *staticDir, uuid,
|
||||
*controllerNamespace, clusterDomain, *reload, reHost, client, k8sAPI, hc)
|
||||
|
||||
go func() {
|
||||
|
|
|
@ -28,7 +28,9 @@ type (
|
|||
controllerNamespace string
|
||||
clusterDomain string
|
||||
grafana string
|
||||
grafanaProxy *grafanaProxy
|
||||
jaeger string
|
||||
grafanaProxy *reverseProxy
|
||||
jaegerProxy *reverseProxy
|
||||
hc healthChecker
|
||||
statCache *cache.Cache
|
||||
}
|
||||
|
@ -46,6 +48,7 @@ func (h *handler) handleIndex(w http.ResponseWriter, req *http.Request, p httpro
|
|||
ControllerNamespace: h.controllerNamespace,
|
||||
PathPrefix: pathPfx,
|
||||
Grafana: h.grafana,
|
||||
Jaeger: h.jaeger,
|
||||
}
|
||||
|
||||
version, err := h.apiClient.Version(req.Context(), &pb.Empty{}) // TODO: remove and call /api/version from web app
|
||||
|
@ -95,3 +98,7 @@ func (h *handler) handleProfileDownload(w http.ResponseWriter, req *http.Request
|
|||
func (h *handler) handleGrafana(w http.ResponseWriter, req *http.Request, p httprouter.Params) {
|
||||
h.grafanaProxy.ServeHTTP(w, req)
|
||||
}
|
||||
|
||||
func (h *handler) handleJaeger(w http.ResponseWriter, req *http.Request, p httprouter.Params) {
|
||||
h.jaegerProxy.ServeHTTP(w, req)
|
||||
}
|
||||
|
|
|
@ -6,19 +6,19 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// grafanaProxy is an HTTP reverse proxy that forwards all web requests
|
||||
// containing paths prefixed with "/grafana" to the grafana service. The proxy
|
||||
// strips the "/grafana" prefix and rewrites the Host header before sending.
|
||||
type grafanaProxy struct {
|
||||
// reverseProxy is an HTTP reverse proxy that forwards all web requests
|
||||
// containing paths prefixed to the corresponding service. The proxy
|
||||
// strips the prefix and rewrites the Host header before sending.
|
||||
type reverseProxy struct {
|
||||
*httputil.ReverseProxy
|
||||
}
|
||||
|
||||
func newGrafanaProxy(addr string) *grafanaProxy {
|
||||
func newReverseProxy(addr string, prefix string) *reverseProxy {
|
||||
director := func(req *http.Request) {
|
||||
req.Host = addr
|
||||
req.URL.Host = addr
|
||||
req.URL.Scheme = "http"
|
||||
req.URL.Path = strings.TrimPrefix(req.URL.Path, "/grafana")
|
||||
req.URL.Path = strings.TrimPrefix(req.URL.Path, prefix)
|
||||
|
||||
// the default director implementation does this, so we will too
|
||||
if _, ok := req.Header["User-Agent"]; !ok {
|
||||
|
@ -27,7 +27,7 @@ func newGrafanaProxy(addr string) *grafanaProxy {
|
|||
}
|
||||
}
|
||||
|
||||
return &grafanaProxy{
|
||||
return &reverseProxy{
|
||||
ReverseProxy: &httputil.ReverseProxy{Director: director},
|
||||
}
|
||||
}
|
|
@ -52,6 +52,7 @@ type (
|
|||
Error bool
|
||||
ErrorMessage string
|
||||
PathPrefix string
|
||||
Jaeger string
|
||||
Grafana string
|
||||
}
|
||||
|
||||
|
@ -84,6 +85,7 @@ Please see https://linkerd.io/dns-rebinding for an explanation of what is happen
|
|||
func NewServer(
|
||||
addr string,
|
||||
grafanaAddr string,
|
||||
jaegerAddr string,
|
||||
templateDir string,
|
||||
staticDir string,
|
||||
uuid string,
|
||||
|
@ -115,8 +117,10 @@ func NewServer(
|
|||
uuid: uuid,
|
||||
controllerNamespace: controllerNamespace,
|
||||
clusterDomain: clusterDomain,
|
||||
grafanaProxy: newReverseProxy(grafanaAddr, "/grafana"),
|
||||
jaegerProxy: newReverseProxy(jaegerAddr, ""),
|
||||
grafana: grafanaAddr,
|
||||
grafanaProxy: newGrafanaProxy(grafanaAddr),
|
||||
jaeger: jaegerAddr,
|
||||
hc: hc,
|
||||
statCache: cache.New(statExpiration, statCleanupInterval),
|
||||
}
|
||||
|
@ -191,13 +195,10 @@ func NewServer(
|
|||
server.router.GET("/api/resource-definition", handler.handleAPIResourceDefinition)
|
||||
|
||||
// grafana proxy
|
||||
server.router.DELETE("/grafana/*grafanapath", handler.handleGrafana)
|
||||
server.router.GET("/grafana/*grafanapath", handler.handleGrafana)
|
||||
server.router.HEAD("/grafana/*grafanapath", handler.handleGrafana)
|
||||
server.router.OPTIONS("/grafana/*grafanapath", handler.handleGrafana)
|
||||
server.router.PATCH("/grafana/*grafanapath", handler.handleGrafana)
|
||||
server.router.POST("/grafana/*grafanapath", handler.handleGrafana)
|
||||
server.router.PUT("/grafana/*grafanapath", handler.handleGrafana)
|
||||
server.handleAllOperationsForPath("/grafana/*grafanapath", handler.handleGrafana)
|
||||
|
||||
// jaeger proxy
|
||||
server.handleAllOperationsForPath("/jaeger/*jaegerpath", handler.handleJaeger)
|
||||
|
||||
return httpServer
|
||||
}
|
||||
|
@ -243,6 +244,16 @@ func (s *Server) loadTemplate(templateFile string) (template *template.Template,
|
|||
return template, err
|
||||
}
|
||||
|
||||
func (s *Server) handleAllOperationsForPath(path string, handle httprouter.Handle) {
|
||||
s.router.DELETE(path, handle)
|
||||
s.router.GET(path, handle)
|
||||
s.router.HEAD(path, handle)
|
||||
s.router.OPTIONS(path, handle)
|
||||
s.router.PATCH(path, handle)
|
||||
s.router.POST(path, handle)
|
||||
s.router.PUT(path, handle)
|
||||
}
|
||||
|
||||
func safelyJoinPath(rootPath, userPath string) string {
|
||||
return filepath.Join(rootPath, path.Clean("/"+userPath))
|
||||
}
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
data-go-version="{{.Data.GoVersion}}"
|
||||
data-controller-namespace="{{.ControllerNamespace}}"
|
||||
data-uuid="{{.UUID}}"
|
||||
data-grafana="{{.Grafana}}">
|
||||
data-grafana="{{.Grafana}}"
|
||||
data-jaeger="{{.Jaeger}}">
|
||||
{{ if .Error }}
|
||||
<p>Failed to call public API: {{ .ErrorMessage }}</p>
|
||||
{{ end }}
|
||||
|
|
Loading…
Reference in New Issue