Monitoring UI Polish - Overview & Create Route

rancher/dashboard#1986

update receiver tabs and labeled tooltips

fix styling on receiver responder rows
This commit is contained in:
Westly Wright 2021-01-04 10:38:49 -07:00
parent a4aaf62034
commit 534e0a982b
No known key found for this signature in database
GPG Key ID: 4FAB3D8673DC54A3
13 changed files with 542 additions and 496 deletions

View File

@ -25,6 +25,7 @@
@import "./global/tooltip";
@import "./global/table";
@import "./global/select";
@import "./global/resource";
@import "./vendor/vue-select";
@import "./vendor/vue-js-modal";

View File

@ -0,0 +1,92 @@
.create-resource-container {
.subtypes-container {
display: flex;
flex-wrap: wrap;
width: 100%;
}
.subtype-content {
width: 100%;
}
.subtype-banner {
border-left: 5px solid var(--primary);
border-radius: var(--border-radius);
display: flex;
flex-basis: 40%;
margin: 10px;
min-height: 100px;
padding: 10px;
box-shadow: 0 0 20px var(--shadow);
&.disabled {
cursor: not-allowed !important;
background-color: var(--disabled-bg);
}
&.selected {
background-color: var(--accent-btn);
}
&.top {
background-image: linear-gradient(
-90deg,
var(--body-bg),
var(--accent-btn)
);
h2 {
margin: 0px;
}
}
.title {
align-items: center;
display: flex;
width: 100%;
h5 {
margin: 0;
}
.flex-right {
margin-left: auto;
}
}
.description {
color: var(--input-label);
display: flex;
flex-direction: column;
justify-content: center;
}
&:not(.top) {
align-items: top;
flex-direction: row;
justify-content: start;
&:hover {
cursor: pointer;
box-shadow: 0px 0px 1px var(--outline-width) var(--outline);
}
}
.round-image {
border-radius: 50%;
height: 50px;
margin-right: 10px;
width: 50px;
overflow: hidden;
}
.banner-abbrv {
align-items: center;
background-color: var(--primary);
color: white;
display: flex;
font-size: 2.5em;
height: 100%;
justify-content: center;
width: 100%;
}
}
}

View File

@ -1119,6 +1119,32 @@ monitoring:
block: Block
file: Filesystem
monitoringReceiver:
addButton: Add {type}
custom:
label: Custom
title: Custom Config
info: The YAML provided here will be directly appended to your receiver within the Alertmanager Config Secret.
email:
label: Email
title: Email Config
opsgenie:
label: Opsgenie
title: Opsgenie Config
pagerduty:
label: PagerDuty
title: PagerDuty Config
info: "You can find additional info on creating an Integration Key for PagerDuty <a href='https://www.pagerduty.com/docs/guides/prometheus-integration-guide/' target='_blank' rel='noopener nofollow' class='flex-right'>here</a>."
slack:
label: Slack
title: Slack Config
info: "You can find additional info on creating Incoming Webhooks for Slack <a href='https://rancher.slack.com/apps/A0F7XDUAZ-incoming-webhooks' target='_blank' rel='noopener noreferrer nofollow'>here</a> ."
webhook:
label: Webhook
title: Webhook Config
monitoringRoute:
groups:
label: Group By

View File

@ -359,7 +359,7 @@ export default {
</section>
</template>
<style lang='scss'>
<style lang='scss' scoped>
.cru-resource-yaml-container {
.resource-yaml {
.yaml-editor {
@ -367,96 +367,12 @@ export default {
}
}
}
.subtypes-container {
display: flex;
flex-wrap: wrap;
width: 100%;
.create-resource-container {
.subtype-banner {
.round-image {
background-color: var(--primary);
}
}
}
.subtype-content {
width: 100%;
}
.subtype-banner {
border-left: 5px solid var(--primary);
border-radius: var(--border-radius);
display: flex;
flex-basis: 40%;
margin: 10px;
min-height: 100px;
padding: 10px;
box-shadow: 0 0 20px var(--shadow);
&.selected {
background-color: var(--accent-btn);
}
&.top {
background-image: linear-gradient(
-90deg,
var(--body-bg),
var(--accent-btn)
);
H2 {
margin: 0px;
}
}
.title {
align-items: center;
display: flex;
width: 100%;
// flex-basis: 10%;
h5 {
margin: 0;
}
.flex-right {
margin-left: auto;
}
}
.description {
color: var(--input-label);
display: flex;
flex-direction: column;
justify-content: center;
}
.description {
color: var(--input-label);
}
&:not(.top) {
align-items: top;
flex-direction: row;
justify-content: start;
&:hover {
cursor: pointer;
box-shadow: 0px 0px 1px var(--outline-width) var(--outline);
}
}
.round-image {
background-color: var(--primary);
border-radius: 50%;
height: 50px;
margin-right: 10px;
width: 50px;
overflow: hidden;
}
.banner-abbrv {
align-items: center;
background-color: var(--primary);
color: white;
display: flex;
font-size: 2.5em;
height: 100%;
justify-content: center;
width: 100%;
}
}
</style>

View File

@ -22,8 +22,8 @@ export default {
},
tooltip: {
type: String,
default: null,
type: [String, Object]
},
hoverTooltip: {

View File

@ -72,7 +72,7 @@ export default {
},
tooltip: {
default: null,
type: String
type: [String, Object]
},
value: {
default: null,

View File

@ -2,7 +2,7 @@
export default {
props: {
value: {
type: String,
type: [String, Object],
default: null
},
@ -22,7 +22,7 @@ export default {
<template>
<div ref="container" class="labeled-tooltip" :class="{[status]: true, hoverable: hover}">
<template v-if="hover">
<i v-tooltip="{content: value, classes: [`tooltip-${status}`]}" :class="{'hover':!value}" class="icon icon-info status-icon" />
<i v-tooltip="value.content ? { ...{content: value.content, classes: [`tooltip-${status}`]}, ...value } : value" :class="{'hover':!value}" class="icon icon-info status-icon" />
</template>
<template v-else>
<i :class="{'hover':!value}" class="icon icon-info status-icon" />

View File

@ -165,7 +165,7 @@ export default {
<img :src="receiverType.logo" />
</div>
<h4 class="name ml-10">
{{ receiverType.label }}
<t :k="receiverType.label" />
</h4>
</div>
<div v-if="receiverType.name !== 'custom'" class="right">
@ -175,14 +175,13 @@ export default {
</GradientBox>
</div>
</Tab>
<Tab v-for="(receiverType, i) in receiverTypes" :key="i" :label="receiverType.label" :name="receiverType.name" :weight="receiverTypes.length - i">
<Banner v-if="receiverType.name === 'slack'" color="info">
Here's how you create <a href="https://rancher.slack.com/apps/A0F7XDUAZ-incoming-webhooks" target="_blank" rel="noopener noreferrer nofollow">Incoming Webhooks</a> for Slack.
</Banner>
<Banner v-if="!isView && receiverType.name === 'custom'" color="info" label="The YAML provided here will be directly appended to your receiver within the Alertmanager Config Secret" />
<Banner v-if="!isView && receiverType.name === 'pagerduty'" color="info">
Here's how you create an <a href="https://www.pagerduty.com/docs/guides/prometheus-integration-guide/" target="_blank" rel="noopener nofollow" class="flex-right">Integration Key</a> for PagerDuty
</Banner>
<Tab
v-for="(receiverType, i) in receiverTypes"
:key="i"
:label="t(receiverType.label)"
:name="receiverType.name"
:weight="receiverTypes.length - i"
>
<YamlEditor
v-if="receiverType.name === 'custom'"
ref="customEditor"
@ -196,8 +195,7 @@ export default {
class="namespace-list"
:mode="mode"
:default-add-value="{}"
:disabled="disabled"
:add-label="'Add ' + receiverType.label"
:add-label="t('monitoringReceiver.addButton', { type: t(receiverType.label) })"
>
<template #default="props">
<div :class="{'pt-30': !isView}">

View File

@ -1,191 +1,198 @@
<script>
import ArrayList from '@/components/form/ArrayList';
import LabeledInput from '@/components/form/LabeledInput';
import LabeledSelect from '@/components/form/LabeledSelect';
import Checkbox from '@/components/form/Checkbox';
import InputWithSelect from '@/components/form/InputWithSelect';
import { _VIEW } from '@/config/query-params';
export const TARGETS = [
{
label: 'Id',
value: 'id'
},
{
label: 'Name',
value: 'name'
},
{
label: 'Username',
value: 'username'
}
];
export const TYPES = [
{
label: 'Team',
value: 'team'
},
{
label: 'User',
value: 'user'
},
{
label: 'Escalation',
value: 'escalation'
},
{
label: 'Schedule',
value: 'schedule'
}
];
export default {
components: {
ArrayList, Checkbox, InputWithSelect, LabeledInput, LabeledSelect
},
props: {
mode: {
type: String,
required: true,
},
value: {
type: Object,
required: true
}
},
data() {
this.$set(this.value, 'http_config', this.value.http_config || {});
this.$set(this.value, 'send_resolved', typeof this.value.send_resolved === 'boolean' ? this.value.send_resolved : true);
this.$set(this.value, 'responders', this.value.responders || []);
const responders = this.value.responders.map((responder) => {
const target = TARGETS.find(target => responder[target.value]);
return {
type: responder.type,
target: target.value,
value: responder[target.value]
};
});
return {
defaultResponder: {
type: TYPES[0].value,
target: TARGETS[0].value,
value: ''
},
responders,
TARGETS,
TYPES
};
},
computed: {
isView() {
return this.mode === _VIEW;
}
},
watch: {
responders: {
deep: true,
handler() {
const responders = this.responders.map((responder) => {
return {
type: responder.type,
[responder.target]: responder.value
};
});
this.$set(this.value, 'responders', responders);
}
}
},
methods: {
updateResponder({ selected, text }, row) {
row.target = selected;
row.value = text;
},
typeLabel(type) {
return TYPES.find(t => t.value === type).label;
},
targetLabel(target) {
return TARGETS.find(t => t.value === target).label;
}
}
};
</script>
<template>
<div>
<div class="row mb-20">
<div class="col span-12">
<LabeledInput v-model="value.api_key" :mode="mode" label="API Key" />
</div>
</div>
<div class="row mb-20">
<div class="col span-12">
<LabeledInput v-model="value.http_config.proxy_url" :mode="mode" label="Proxy URL" placeholder="e.g. http://my-proxy/" />
</div>
</div>
<div class="row mb-20">
<Checkbox v-model="value.send_resolved" :mode="mode" label="Enable send resolved alerts" />
</div>
<div class="row">
<div class="col span-12">
<h3>Responders</h3>
<ArrayList v-model="responders" :mode="mode" :default-add-value="defaultResponder" :show-header="true">
<template v-slot:column-headers>
<div class="row" :class="{'mb-15': isView}">
<div class="col span-6">
<span class="text-label">Type</span>
</div>
<div class="col span-6">
<span class="text-label">Send To</span>
</div>
</div>
</template>
<template v-slot:columns="scope">
<div class="row responder">
<div class="col span-6">
<span v-if="isView">{{ typeLabel(scope.row.value.type) }}</span>
<LabeledSelect v-else v-model="scope.row.value.type" :mode="mode" label="Type" :options="TYPES" />
</div>
<div class="col-span-6 target">
<span v-if="isView">{{ targetLabel(scope.row.value.target) }}: {{ scope.row.value.value }}</span>
<InputWithSelect
v-else
:mode="mode"
:options="TARGETS"
:select-value="scope.row.value.target"
:text-value="scope.row.value.value"
@input="updateResponder($event, scope.row.value)"
/>
</div>
</div>
</template>
</ArrayList>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.responder {
&, .target {
width: 100%;
}
.target ::v-deep {
& .input-container {
height: $input-height;
}
& .unlabeled-select {
min-width: 35%;
}
}
}
</style>
<script>
import ArrayList from '@/components/form/ArrayList';
import LabeledInput from '@/components/form/LabeledInput';
import Select from '@/components/form/Select';
import Checkbox from '@/components/form/Checkbox';
import InputWithSelect from '@/components/form/InputWithSelect';
import { _VIEW } from '@/config/query-params';
export const TARGETS = [
{
label: 'Id',
value: 'id'
},
{
label: 'Name',
value: 'name'
},
{
label: 'Username',
value: 'username'
}
];
export const TYPES = [
{
label: 'Team',
value: 'team'
},
{
label: 'User',
value: 'user'
},
{
label: 'Escalation',
value: 'escalation'
},
{
label: 'Schedule',
value: 'schedule'
}
];
export default {
components: {
ArrayList, Checkbox, InputWithSelect, LabeledInput, Select
},
props: {
mode: {
type: String,
required: true,
},
value: {
type: Object,
required: true
}
},
data() {
this.$set(this.value, 'http_config', this.value.http_config || {});
this.$set(this.value, 'send_resolved', typeof this.value.send_resolved === 'boolean' ? this.value.send_resolved : true);
this.$set(this.value, 'responders', this.value.responders || []);
const responders = this.value.responders.map((responder) => {
const target = TARGETS.find(target => responder[target.value]);
return {
type: responder.type,
target: target.value,
value: responder[target.value]
};
});
return {
defaultResponder: {
type: TYPES[0].value,
target: TARGETS[0].value,
value: ''
},
responders,
TARGETS,
TYPES
};
},
computed: {
isView() {
return this.mode === _VIEW;
}
},
watch: {
responders: {
deep: true,
handler() {
const responders = this.responders.map((responder) => {
return {
type: responder.type,
[responder.target]: responder.value
};
});
this.$set(this.value, 'responders', responders);
}
}
},
methods: {
updateResponder({ selected, text }, row) {
row.target = selected;
row.value = text;
},
typeLabel(type) {
return TYPES.find(t => t.value === type).label;
},
targetLabel(target) {
return TARGETS.find(t => t.value === target).label;
}
}
};
</script>
<template>
<div>
<div class="row mb-20">
<div class="col span-12">
<LabeledInput v-model="value.api_key" :mode="mode" label="API Key" />
</div>
</div>
<div class="row mb-20">
<div class="col span-12">
<LabeledInput v-model="value.http_config.proxy_url" :mode="mode" label="Proxy URL" placeholder="e.g. http://my-proxy/" />
</div>
</div>
<div class="row mb-20">
<Checkbox v-model="value.send_resolved" :mode="mode" label="Enable send resolved alerts" />
</div>
<div class="row">
<div class="col span-12">
<h3>Responders</h3>
<ArrayList v-model="responders" :mode="mode" :default-add-value="defaultResponder" :show-header="true">
<template v-slot:column-headers>
<div class="row mb-10">
<div class="col span-6">
<span class="text-label">Type</span>
</div>
<div class="col span-6 send-to">
<span class="text-label">Send To</span>
</div>
</div>
</template>
<template v-slot:columns="scope">
<div class="row responder">
<div class="col span-6">
<span v-if="isView">{{ typeLabel(scope.row.value.type) }}</span>
<Select v-else v-model="scope.row.value.type" :mode="mode" label="Type" :options="TYPES" />
</div>
<div class="col-span-6 target">
<span v-if="isView">{{ targetLabel(scope.row.value.target) }}: {{ scope.row.value.value }}</span>
<InputWithSelect
v-else
:mode="mode"
:options="TARGETS"
:select-value="scope.row.value.target"
:text-value="scope.row.value.value"
@input="updateResponder($event, scope.row.value)"
/>
</div>
</div>
</template>
</ArrayList>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.responder {
&, .target {
width: 100%;
}
.send-to {
margin-left: -35px;
}
.unlabeled-select ::v-deep {
height: $input-height;
}
.target ::v-deep {
& .input-container {
height: $input-height;
}
& .unlabeled-select {
min-width: 35%;
}
}
}
</style>

View File

@ -1,66 +1,88 @@
<script>
import LabeledInput from '@/components/form/LabeledInput';
import LabeledSelect from '@/components/form/LabeledSelect';
import Checkbox from '@/components/form/Checkbox';
export default {
components: {
Checkbox, LabeledInput, LabeledSelect
},
props: {
mode: {
type: String,
required: true,
},
value: {
type: Object,
required: true
}
},
data() {
this.$set(this.value, 'http_config', this.value.http_config || {});
this.$set(this.value, 'send_resolved', typeof this.value.send_resolved === 'boolean' ? this.value.send_resolved : true);
const integrationMapping = {
'Events API v2': 'routing_key',
Prometheus: 'service_key'
};
const integrationTypeOptions = Object.keys(integrationMapping);
return {
integrationMapping,
integrationTypeOptions,
integrationType: this.value.routing_key ? integrationTypeOptions[0] : integrationTypeOptions[1]
};
},
watch: {
integrationType() {
this.integrationTypeOptions.forEach((option) => {
this.value[this.integrationMapping[option]] = null;
});
}
}
};
</script>
<template>
<div>
<div class="row mb-20">
<div class="col span-6">
<LabeledSelect v-model="integrationType" :options="integrationTypeOptions" :mode="mode" label="Integration Type" />
</div>
<div class="col span-6">
<LabeledInput v-model="value[integrationMapping[integrationType]]" :mode="mode" label="Default Integration Key" />
</div>
</div>
<div class="row mb-20">
<div class="col span-12">
<LabeledInput v-model="value.http_config.proxy_url" :mode="mode" label="Proxy URL" placeholder="e.g. http://my-proxy/" />
</div>
</div>
<div class="row">
<Checkbox v-model="value.send_resolved" :mode="mode" label="Enable send resolved alerts" />
</div>
</div>
</template>
<script>
import LabeledInput from '@/components/form/LabeledInput';
import LabeledSelect from '@/components/form/LabeledSelect';
import Checkbox from '@/components/form/Checkbox';
export default {
components: {
Checkbox,
LabeledInput,
LabeledSelect,
},
props: {
mode: {
type: String,
required: true,
},
value: {
type: Object,
required: true,
},
},
data() {
this.$set(this.value, 'http_config', this.value.http_config || {});
this.$set(this.value, 'send_resolved', typeof this.value.send_resolved === 'boolean' ? this.value.send_resolved : true);
const integrationMapping = {
'Events API v2': 'routing_key',
Prometheus: 'service_key',
};
const integrationTypeOptions = Object.keys(integrationMapping);
return {
integrationMapping,
integrationTypeOptions,
integrationType: this.value.routing_key ? integrationTypeOptions[0] : integrationTypeOptions[1]
};
},
watch: {
integrationType() {
this.integrationTypeOptions.forEach((option) => {
this.value[this.integrationMapping[option]] = null;
});
},
},
};
</script>
<template>
<div>
<div class="row mb-20">
<div class="col span-6">
<LabeledSelect
v-model="integrationType"
:options="integrationTypeOptions"
:mode="mode"
:tooltip="{ content: t('monitoringReceiver.pagerduty.info', {}, raw=true), autoHide: false}"
:hover-tooltip="true"
label="Integration Type"
/>
</div>
<div class="col span-6">
<LabeledInput
v-model="value[integrationMapping[integrationType]]"
:mode="mode"
label="Default Integration Key"
/>
</div>
</div>
<div class="row mb-20">
<div class="col span-12">
<LabeledInput
v-model="value.http_config.proxy_url"
:mode="mode"
label="Proxy URL"
placeholder="e.g. http://my-proxy/"
/>
</div>
</div>
<div class="row">
<Checkbox
v-model="value.send_resolved"
:mode="mode"
label="Enable send resolved alerts"
/>
</div>
</div>
</template>

View File

@ -12,15 +12,19 @@ export default {
},
value: {
type: Object,
required: true
}
required: true,
},
},
data() {
this.$set(this.value, 'http_config', this.value.http_config || {});
this.$set(this.value, 'send_resolved', this.value.send_resolved || false);
if (this.mode === _CREATE) {
this.$set(this.value, 'text', this.value.text || '{{ template "slack.rancher.text" . }}');
this.$set(
this.value,
'text',
this.value.text || '{{ template "slack.rancher.text" . }}'
);
}
return {};
@ -32,19 +36,39 @@ export default {
<div>
<div class="row mb-20">
<div class="col span-12">
<LabeledInput v-model="value.api_url" :mode="mode" label="URL" placeholder="e.g. https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX" />
<LabeledInput
v-model="value.api_url"
:mode="mode"
label="Webhook URL"
:tooltip="{ content: t('monitoringReceiver.slack.info', {}, raw=true), autoHide: false}"
placeholder="e.g. https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX"
/>
</div>
</div>
<div class="row mb-20">
<div class="col span-6">
<LabeledInput v-model="value.channel" :mode="mode" label="Default Channel" placeholder="e.g. #example" />
<LabeledInput
v-model="value.channel"
:mode="mode"
label="Default Channel"
placeholder="e.g. #example"
/>
</div>
<div class="col span-6">
<LabeledInput v-model="value.http_config.proxy_url" :mode="mode" label="Proxy URL" placeholder="e.g. http://my-proxy/" />
<LabeledInput
v-model="value.http_config.proxy_url"
:mode="mode"
label="Proxy URL"
placeholder="e.g. http://my-proxy/"
/>
</div>
</div>
<div class="row">
<Checkbox v-model="value.send_resolved" :mode="mode" label="Enable send resolved alerts" />
<Checkbox
v-model="value.send_resolved"
:mode="mode"
label="Enable send resolved alerts"
/>
</div>
</div>
</template>

View File

@ -6,43 +6,46 @@ import jsyaml from 'js-yaml';
export const RECEIVERS_TYPES = [
{
name: 'slack',
label: 'Slack',
title: 'Slack Config',
label: 'monitoringReceiver.slack.label',
title: 'monitoringReceiver.slack.title',
info: 'monitoringReceiver.slack.info',
key: 'slack_configs',
logo: require(`~/assets/images/vendor/slack.svg`)
},
{
name: 'email',
label: 'Email',
title: 'Email Config',
label: 'monitoringReceiver.email.label',
title: 'monitoringReceiver.email.title',
key: 'email_configs',
logo: require(`~/assets/images/vendor/email.svg`)
},
{
name: 'pagerduty',
label: 'PagerDuty',
title: 'PagerDuty Config',
label: 'monitoringReceiver.pagerduty.label',
title: 'monitoringReceiver.pagerduty.title',
info: 'monitoringReceiver.pagerduty.info',
key: 'pagerduty_configs',
logo: require(`~/assets/images/vendor/pagerduty.svg`)
},
{
name: 'opsgenie',
label: 'Opsgenie',
title: 'Opsgenie Config',
label: 'monitoringReceiver.opsgenie.label',
title: 'monitoringReceiver.opsgenie.title',
key: 'opsgenie_configs',
logo: require(`~/assets/images/vendor/email.svg`)
},
{
name: 'webhook',
label: 'Webhook',
title: 'Webhook Config',
label: 'monitoringReceiver.webhook.label',
title: 'monitoringReceiver.webhook.title',
key: 'webhook_configs',
logo: require(`~/assets/images/vendor/webhook.svg`)
},
{
name: 'custom',
label: 'Custom',
title: 'Custom Config',
label: 'monitoringReceiver.custom.label',
title: 'monitoringReceiver.custom.title',
info: 'monitoringReceiver.custom.info',
key: 'webhook_configs',
logo: require(`~/assets/images/vendor/custom.svg`)
},

View File

@ -59,24 +59,27 @@ export default {
group: 'prometheus',
iconSrc: this.prometheusSrc,
label: 'monitoring.overview.linkedList.prometheusPromQl.label',
description: 'monitoring.overview.linkedList.prometheusPromQl.description',
link: `/k8s/clusters/${ this.currentCluster.id }/api/v1/namespaces/cattle-monitoring-system/services/http:rancher-monitoring-prometheus:9090/proxy/graph`,
description:
'monitoring.overview.linkedList.prometheusPromQl.description',
link: `/k8s/clusters/${ this.currentCluster.id }/api/v1/namespaces/cattle-monitoring-system/services/http:rancher-monitoring-prometheus:9090/proxy/graph`,
},
{
enabled: false,
group: 'prometheus',
iconSrc: this.prometheusSrc,
label: 'monitoring.overview.linkedList.prometheusRules.label',
description: 'monitoring.overview.linkedList.prometheusRules.description',
link: `/k8s/clusters/${ this.currentCluster.id }/api/v1/namespaces/cattle-monitoring-system/services/http:rancher-monitoring-prometheus:9090/proxy/rules`,
description:
'monitoring.overview.linkedList.prometheusRules.description',
link: `/k8s/clusters/${ this.currentCluster.id }/api/v1/namespaces/cattle-monitoring-system/services/http:rancher-monitoring-prometheus:9090/proxy/rules`,
},
{
enabled: false,
group: 'prometheus',
iconSrc: this.prometheusSrc,
label: 'monitoring.overview.linkedList.prometheusTargets.label',
description: 'monitoring.overview.linkedList.prometheusTargets.description',
link: `/k8s/clusters/${ this.currentCluster.id }/api/v1/namespaces/cattle-monitoring-system/services/http:rancher-monitoring-prometheus:9090/proxy/targets`,
description:
'monitoring.overview.linkedList.prometheusTargets.description',
link: `/k8s/clusters/${ this.currentCluster.id }/api/v1/namespaces/cattle-monitoring-system/services/http:rancher-monitoring-prometheus:9090/proxy/targets`,
},
];
@ -87,13 +90,17 @@ export default {
async fetchDeps() {
const { $store, externalLinks } = this;
const workloads = await Promise.all(Object.values(WORKLOAD_TYPES).map(type => this.$store.dispatch('cluster/findAll', { type })));
const workloads = await Promise.all(
Object.values(WORKLOAD_TYPES).map(type => this.$store.dispatch('cluster/findAll', { type })
)
);
workloads.flat().forEach((workload) => {
if (
!isEmpty(workload?.spec?.template?.spec?.containers) &&
(workload.spec.template.spec.containers.find(c => c.image.includes('quay.io/coreos/prometheus-operator') ||
c.image.includes('rancher/coreos-prometheus-operator'))
workload.spec.template.spec.containers.find(
c => c.image.includes('quay.io/coreos/prometheus-operator') ||
c.image.includes('rancher/coreos-prometheus-operator')
) &&
workload?.metadata?.namespace !== CATTLE_MONITORING_NAMESPACE
) {
@ -108,10 +115,24 @@ export default {
if (!isEmpty(hash.endpoints)) {
const amMatch = findBy(externalLinks, 'group', 'alertmanager');
const grafanaMatch = findBy(externalLinks, 'group', 'grafana');
const promeMatch = externalLinks.filter(el => el.group === 'prometheus');
const alertmanager = findBy(hash.endpoints, 'id', `${ CATTLE_MONITORING_NAMESPACE }/rancher-monitoring-alertmanager`);
const grafana = findBy(hash.endpoints, 'id', `${ CATTLE_MONITORING_NAMESPACE }/rancher-monitoring-grafana`);
const prometheus = findBy(hash.endpoints, 'id', `${ CATTLE_MONITORING_NAMESPACE }/rancher-monitoring-prometheus`);
const promeMatch = externalLinks.filter(
el => el.group === 'prometheus'
);
const alertmanager = findBy(
hash.endpoints,
'id',
`${ CATTLE_MONITORING_NAMESPACE }/rancher-monitoring-alertmanager`
);
const grafana = findBy(
hash.endpoints,
'id',
`${ CATTLE_MONITORING_NAMESPACE }/rancher-monitoring-grafana`
);
const prometheus = findBy(
hash.endpoints,
'id',
`${ CATTLE_MONITORING_NAMESPACE }/rancher-monitoring-prometheus`
);
if (!isEmpty(alertmanager) && !isEmpty(alertmanager.subsets)) {
amMatch.enabled = true;
@ -126,7 +147,7 @@ export default {
}
}
},
}
},
};
</script>
@ -142,112 +163,48 @@ export default {
</div>
</div>
</header>
<div class="links">
<div>
<Banner v-if="v1Installed" color="warning">
<template #default>
<t k="monitoring.v1Warning" :raw="true" />
</template>
</Banner>
<div v-for="fel in externalLinks" :key="fel.label" class="link-container">
<a v-if="fel.enabled" :href="fel.link" target="_blank" rel="noopener noreferrer">
<div class="link-logo">
<LazyImage :src="fel.iconSrc" />
</div>
<div class="link-content">
<t :k="fel.label" />
<i class="icon icon-external-link pull-right" />
<hr />
<div class="description"><t :k="fel.description" /></div>
</div>
</a>
<a v-else v-tooltip="t('monitoring.overview.linkedList.na')" href="javascript:void(0)" :disabled="!fel.enabled">
<div class="link-logo">
<LazyImage :src="fel.iconSrc" />
</div>
<div class="link-content">
<t :k="fel.label" />
<i class="icon icon-external-link pull-right" />
<hr />
<div class="description"><t :k="fel.description" /></div>
</div>
</a>
<div class="create-resource-container">
<div class="subtypes-container">
<a
v-for="fel in externalLinks"
:key="fel.label"
v-tooltip="!fel.enabled ? t('monitoring.overview.linkedList.na') : undefined"
:href="fel.enabled ? fel.link : (void 0)"
:disabled="!fel.enabled"
target="_blank"
rel="noopener noreferrer"
:class="{ 'subtype-banner': true, disabled: !fel.enabled}"
>
<div class="subtype-content">
<div class="title">
<div class="subtype-logo round-image">
<LazyImage :src="fel.iconSrc" />
</div>
<h5>
<span>
<t :k="fel.label" />
</span>
</h5>
<div class="flex-right">
<i class="icon icon-external-link" />
</div>
</div>
<hr />
<div class="description">
<span>
<t :k="fel.description" />
</span>
</div>
</div>
</a>
</div>
</div>
</div>
</section>
</template>
<style lang="scss">
.links {
display: flex;
flex-wrap: wrap;
width: 100%;
.link-container {
background-color: var(--input-bg);
border-radius: var(--border-radius);
border: solid 1px var(--input-border);
display: flex;
flex-basis: 40%;
margin: 0 10px 10px 0;
max-width: 325px;
min-height: 100px;
border-left: solid 10px var(--primary);
a[disabled] {
cursor: not-allowed;
background-color: var(---disabled-bg);
}
&:hover {
box-shadow: 0px 0px 1px var(--outline-width) var(--outline);
}
> a {
align-items: center;
display: flex;
flex: 1 0;
padding: 10px;
.link-logo,
.link-content {
display: inline-block;
}
.link-logo {
text-align: center;
// position: absolute;
// left: 25px;
// top: 25px;
width: 60px;
height: 60px;
border-radius: calc(2 * var(--border-radius));
background-color: white;
img {
width: 56px;
height: 56px;
-o-object-fit: contain;
object-fit: contain;
position: relative;
top: 2px;
}
}
.link-content {
width: 100%;
margin-left: 10px;
}
.description {
margin-top: 10px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
line-clamp: 3;
text-overflow: ellipsis;
color: var(--secondary);
}
}
}
}
</style>