mirror of https://github.com/rancher/dashboard.git
230 lines
8.0 KiB
Vue
230 lines
8.0 KiB
Vue
<script>
|
|
import isEmpty from 'lodash/isEmpty';
|
|
import Tabbed from '@shell/components/Tabbed';
|
|
import Tab from '@shell/components/Tabbed/Tab';
|
|
import DashboardMetrics from '@shell/components/DashboardMetrics';
|
|
import AlertTable from '@shell/components/AlertTable';
|
|
import { Banner } from '@components/Banner';
|
|
import { parse as parseUrl } from '@shell/utils/url';
|
|
import { ENDPOINTS } from '@shell/config/types';
|
|
import { allHash } from '@shell/utils/promise';
|
|
|
|
export default {
|
|
components: {
|
|
Tabbed,
|
|
Tab,
|
|
DashboardMetrics,
|
|
AlertTable,
|
|
Banner
|
|
},
|
|
props: {
|
|
value: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
},
|
|
async fetch() {
|
|
await this.fetchDeps();
|
|
},
|
|
data() {
|
|
return {
|
|
endpointsHash: [],
|
|
loadingEndpoints: true
|
|
};
|
|
},
|
|
computed: {
|
|
relativeDashboardValues() {
|
|
const { alertmanagerURL, grafanaURL, prometheusURL } = this?.value?.status?.dashboardValues;
|
|
|
|
return {
|
|
alertmanagerURL: this.makeRelativeURL(alertmanagerURL),
|
|
grafanaURL: this.makeRelativeURL(grafanaURL),
|
|
prometheusURL: this.makeRelativeURL(prometheusURL)
|
|
};
|
|
},
|
|
monitoringNamespace() {
|
|
// arbitrarily picking the alertmanagerURL here, they're all going to be the same.
|
|
return this.pullKeyFromUrl(this.relativeDashboardValues.alertmanagerURL, 'namespaces');
|
|
},
|
|
alertServiceEndpoint() {
|
|
return this.pullServiceEndpointFromUrl(this.relativeDashboardValues.alertmanagerURL);
|
|
},
|
|
alertServiceEndpointEnabled() {
|
|
return this.checkEndpointEnabled(this.alertServiceEndpoint);
|
|
},
|
|
grafanaServiceEndpoint() {
|
|
return this.pullServiceEndpointFromUrl(this.relativeDashboardValues.grafanaURL);
|
|
},
|
|
grafanaServiceEndpointEnabled() {
|
|
return this.checkEndpointEnabled(this.grafanaServiceEndpoint);
|
|
},
|
|
prometheusServiceEndpoint() {
|
|
return this.pullServiceEndpointFromUrl(this.relativeDashboardValues.prometheusURL);
|
|
},
|
|
prometheusServiceEndpointEnabled() {
|
|
return this.checkEndpointEnabled(this.prometheusServiceEndpoint);
|
|
}
|
|
},
|
|
methods: {
|
|
async fetchDeps() {
|
|
const hash = await allHash({ endpoints: this.$store.dispatch('cluster/findAll', { type: ENDPOINTS }) });
|
|
|
|
this.endpointsHash = hash.endpoints;
|
|
|
|
this.loadingEndpoints = false;
|
|
},
|
|
checkEndpointEnabled(endpointString) {
|
|
if (this.endpointsHash.length === 0) {
|
|
return false;
|
|
}
|
|
const endpoint = this.endpointsHash.find((endpoint) => {
|
|
return endpoint.id === `${ this.monitoringNamespace }/${ endpointString }`;
|
|
});
|
|
|
|
return !isEmpty(endpoint) && !isEmpty(endpoint?.subsets);
|
|
},
|
|
makeRelativeURL(url) {
|
|
if (!url) {
|
|
return '';
|
|
}
|
|
|
|
// most of the downstream components that use these URL expect the everything before and including the clusterid stripped out of the URL
|
|
const parsedUrl = parseUrl(url);
|
|
// we really just need the remaining bit of the url but the destructure makes it clear what we're leaving behind
|
|
// eslint-disable-next-line no-unused-vars
|
|
const [_empty, _k8s, _clusters, _clusterId, ...restUrl] = parsedUrl.relative.split('/');
|
|
// the above processing strips out the leading '/' which we need
|
|
const relativeUrl = `/${ restUrl.join('/') }`;
|
|
|
|
return relativeUrl;
|
|
},
|
|
pullKeyFromUrl(url = '', key) {
|
|
const splitUrl = url.split('/');
|
|
const keyIndex = splitUrl.indexOf(key);
|
|
|
|
// the value we're looking for is going to be at the index immediately after the key
|
|
return splitUrl[keyIndex + 1];
|
|
},
|
|
pullServiceEndpointFromUrl(url = '') {
|
|
const serviceString = this.pullKeyFromUrl(url, 'services');
|
|
|
|
// string is going to come back as "<protocol>:<serviceEndPoint>:<port>", we don't need the protocol or the port
|
|
return serviceString.split(':')[1];
|
|
},
|
|
showMenu(show) {
|
|
if (this.$refs.popover) {
|
|
if (show) {
|
|
this.$refs.popover.show();
|
|
} else {
|
|
this.$refs.popover.hide();
|
|
}
|
|
}
|
|
},
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<Tabbed v-if="value.status.dashboardValues" class="mt-30">
|
|
<Tab name="project-metrics" :label="t('monitoring.tabs.projectMetrics')" :weight="3">
|
|
<template #default="props">
|
|
<DashboardMetrics
|
|
v-if="props.active && grafanaServiceEndpointEnabled"
|
|
:detail-url="`${relativeDashboardValues.grafanaURL}/d/rancher-pod-1/rancher-pod?orgId=1&kiosk`"
|
|
:summary-url="`${relativeDashboardValues.grafanaURL}/d/rancher-workload-1/rancher-workload?orgId=1&kiosk`"
|
|
graph-height="825px"
|
|
project
|
|
>
|
|
</DashboardMetrics>
|
|
</template>
|
|
</Tab>
|
|
<Tab name="active-alarms" :label="t('monitoring.overview.alertsList.label')" :weight="2">
|
|
<template>
|
|
<AlertTable :monitoring-namespace="monitoringNamespace" :alert-service-endpoint="alertServiceEndpoint" />
|
|
</template>
|
|
</Tab>
|
|
<template #tab-row-extras>
|
|
<div class="tab-row-footer">
|
|
<div
|
|
class="resources-dropdown"
|
|
:class="{disabled: !prometheusServiceEndpointEnabled}"
|
|
tabindex="0"
|
|
@blur="showMenu(false)"
|
|
@click="showMenu(true)"
|
|
@focus.capture="showMenu(true)"
|
|
>
|
|
<v-popover
|
|
ref="popover"
|
|
placement="bottom-end"
|
|
offset="-10"
|
|
trigger="manual"
|
|
:delay="{show: 0, hide: 0}"
|
|
:popper-options="{modifiers: { flip: { enabled: false } } }"
|
|
:container="false"
|
|
>
|
|
<div class="meta-title">
|
|
{{ t('monitoring.tabs.prometheus') }} <i class="icon icon-chevron-down" />
|
|
</div>
|
|
<template slot="popover" class="resources-status-list">
|
|
<ul class="list-unstyled dropdown" @click.stop="showMenu(false)">
|
|
<li>
|
|
<a :href="`${value.status.dashboardValues.prometheusURL}/graph`" target="_blank">{{ t('monitoring.overview.linkedList.prometheusPromQl.label') }} <i class="icon icon-external-link" /></a>
|
|
</li>
|
|
<li>
|
|
<a :href="`${value.status.dashboardValues.prometheusURL}/rules`" target="_blank">{{ t('monitoring.overview.linkedList.prometheusRules.label') }} <i class="icon icon-external-link" /></a>
|
|
</li>
|
|
<li>
|
|
<a :href="`${value.status.dashboardValues.prometheusURL}/targets`" target="_blank">{{ t('monitoring.overview.linkedList.prometheusTargets.label') }} <i class="icon icon-external-link" /></a>
|
|
</li>
|
|
</ul>
|
|
</template>
|
|
</v-popover>
|
|
</div>
|
|
<a :class="{disabled: !grafanaServiceEndpointEnabled}" :href="value.status.dashboardValues.grafanaURL" target="_blank"> {{ t('monitoring.overview.linkedList.grafana.label') }} <i class="icon icon-external-link" /></a>
|
|
<a :class="{disabled: !alertServiceEndpointEnabled}" :href="value.status.dashboardValues.alertmanagerURL" target="_blank"> {{ t('monitoring.overview.linkedList.alertManager.label') }} <i class="icon icon-external-link" /></a>
|
|
</div>
|
|
</template>
|
|
</Tabbed>
|
|
<div v-else>
|
|
<Banner color="error">
|
|
{{ `${ t('monitoring.projectMonitoring.detail.error') }${ value.status.statusMessage || t('generic.unknown') }` }}
|
|
</Banner>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.tab-row-footer {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin-left: auto;
|
|
display: flex;
|
|
flex-direction: row;
|
|
|
|
A, .resources-dropdown {
|
|
color: var(--link);
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 10px 15px;
|
|
&.disabled {
|
|
cursor: not-allowed;
|
|
pointer-events: none;
|
|
filter: grayscale(1);
|
|
color: var(--muted);
|
|
}
|
|
}
|
|
}
|
|
.resources-dropdown {
|
|
li {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin: 10px 5px;
|
|
}
|
|
|
|
.list-count {
|
|
margin-left: 30px;
|
|
}
|
|
}
|
|
</style>
|