mirror of https://github.com/rancher/dashboard.git
clusterscan detail
This commit is contained in:
parent
14767dbf88
commit
2e1c1a76fa
|
|
@ -255,6 +255,16 @@ cis:
|
|||
profile: Profile
|
||||
testID: Test ID
|
||||
testsToSkip: Tests to Skip
|
||||
scan:
|
||||
description: Description
|
||||
failed: Failed
|
||||
lastScanTime: Last Scan Time
|
||||
notApplicable: 'N/A'
|
||||
number: Number
|
||||
passed: Passed
|
||||
scanReport: Scan Report
|
||||
skipped: Skipped
|
||||
total: Total
|
||||
|
||||
cluster:
|
||||
nodeDriver:
|
||||
|
|
|
|||
|
|
@ -121,17 +121,19 @@ export default {
|
|||
return null;
|
||||
},
|
||||
parent() {
|
||||
if (this.parentOverride) {
|
||||
return this.parentOverride;
|
||||
}
|
||||
|
||||
const displayName = this.$store.getters['type-map/labelFor'](this.schema);
|
||||
const location = {
|
||||
name: 'c-cluster-product-resource',
|
||||
params: { resource: this.value.type }
|
||||
};
|
||||
|
||||
return { displayName, location };
|
||||
const out = { displayName, location };
|
||||
|
||||
if (this.parentOverride) {
|
||||
Object.assign(out, this.parentOverride);
|
||||
}
|
||||
|
||||
return out ;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -166,7 +168,7 @@ export default {
|
|||
<div v-if="mode==='view'" class="subheader">
|
||||
<span v-if="isNamespace && project">{{ t("resourceDetail.masthead.project") }}: {{ project.nameDisplay }}</span>
|
||||
<span v-else-if="namespace">{{ t("resourceDetail.masthead.namespace") }}: <nuxt-link :to="namespaceLocation">{{ namespace }}</nuxt-link></span>
|
||||
<span>{{ t("resourceDetail.masthead.age") }}: <LiveDate class="live-date" :value="get(value, 'metadata.creationTimestamp')" /></span>
|
||||
<span v-if="!parent.hideAge">{{ t("resourceDetail.masthead.age") }}: <LiveDate class="live-date" :value="get(value, 'metadata.creationTimestamp')" /></span>
|
||||
</div>
|
||||
</div>
|
||||
<slot name="right">
|
||||
|
|
@ -179,7 +181,7 @@ export default {
|
|||
</button>
|
||||
</div>
|
||||
</slot>
|
||||
<div v-if="banner" class="state-banner">
|
||||
<div v-if="banner && !parent.hideBanner" class="state-banner">
|
||||
<Banner class="state-banner" :color="banner.color" :label="banner.message" />
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
|||
|
|
@ -536,7 +536,7 @@ export default {
|
|||
<Checkbox class="selection-checkbox" :data-node-id="get(row,keyField)" :value="tableSelected.includes(row)" />
|
||||
</td>
|
||||
<td v-if="subExpandColumn" class="row-expand" align="middle">
|
||||
<i data-title="Toggle Expand" :class="{icon: true, 'icon-chevron-right': true, 'icon-chevron-down': !!expanded[get(row, keyField)]}" />
|
||||
<i data-title="Toggle Expand" :class="{icon: true, 'icon-chevron-right': true, 'icon-chevron-down': !!expanded[get(row, keyField)]}" @click="toggleExpand(row)" />
|
||||
</td>
|
||||
<template v-for="col in columns">
|
||||
<slot
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import BadgeState from '@/components/BadgeState';
|
||||
|
||||
import { colorForState, stateDisplay } from '@/plugins/steve/resource-instance';
|
||||
export default {
|
||||
components: { BadgeState },
|
||||
props: {
|
||||
|
|
@ -16,10 +16,28 @@ export default {
|
|||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
|
||||
arbitrary: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
const out = {};
|
||||
|
||||
if (this.arbitrary) {
|
||||
const color = colorForState(this.value);
|
||||
|
||||
out.stateDisplay = stateDisplay(this.value);
|
||||
out.stateBackground = color.replace('text-', 'bg-');
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BadgeState :value="row" />
|
||||
<BadgeState :value="arbitrary ? {stateDisplay, stateBackground} : row" />
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,211 @@
|
|||
<script>
|
||||
import Date from '@/components/formatter/Date';
|
||||
import SortableTable from '@/components/SortableTable';
|
||||
import { defaultAsyncData } from '@/components/ResourceDetail';
|
||||
import { CIS } from '@/config/types';
|
||||
import { STATE } from '@/config/table-headers';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Date,
|
||||
SortableTable
|
||||
},
|
||||
|
||||
props: {
|
||||
value: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async fetch() {
|
||||
this.owned = await this.value.getOwned();
|
||||
},
|
||||
|
||||
asyncData(ctx) {
|
||||
return defaultAsyncData(ctx, null, { hideBanner: true, hideAge: true });
|
||||
},
|
||||
|
||||
data() {
|
||||
return { owned: [] };
|
||||
},
|
||||
|
||||
computed: {
|
||||
clusterReport() {
|
||||
return this.owned.filter(each => each.type === CIS.REPORT)[0];
|
||||
},
|
||||
|
||||
parsedData() {
|
||||
if (!this.clusterReport) {
|
||||
return null;
|
||||
}
|
||||
const { spec:{ reportJSON } } = this.clusterReport;
|
||||
|
||||
return JSON.parse(reportJSON);
|
||||
},
|
||||
|
||||
reportNodes() {
|
||||
return this.parsedData ? this.parsedData.nodes : {};
|
||||
},
|
||||
|
||||
results() {
|
||||
return this.parsedData ? this.parsedData.results.reduce((all, result) => {
|
||||
if (result.checks) {
|
||||
result = result.checks.map((check) => {
|
||||
check.testStateSort = this.testStateSort(check.state);
|
||||
if (!!check.node_type) {
|
||||
check.nodes = check.node_type.reduce((names, type) => {
|
||||
if (this.reportNodes[type]) {
|
||||
names.push(...this.reportNodes[type]);
|
||||
}
|
||||
|
||||
return names;
|
||||
}, []);
|
||||
}
|
||||
|
||||
return check;
|
||||
});
|
||||
}
|
||||
|
||||
all.push(...result);
|
||||
|
||||
return all;
|
||||
}, []) : [];
|
||||
},
|
||||
|
||||
details() {
|
||||
if (!this.parsedData) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
label: this.t('cis.profile'),
|
||||
value: this.value.status.lastRunScanProfileName,
|
||||
to: {
|
||||
name: 'c-cluster-product-resource-id',
|
||||
params: {
|
||||
...this.$route.params,
|
||||
resource: CIS.CLUSTER_SCAN_PROFILE,
|
||||
id: this.value.status.lastRunScanProfileName
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this.t('cis.scan.total'),
|
||||
value: this.parsedData.total
|
||||
},
|
||||
{
|
||||
label: this.t('cis.scan.passed'),
|
||||
value: this.parsedData.pass
|
||||
},
|
||||
{
|
||||
label: this.t('cis.scan.skipped'),
|
||||
value: this.parsedData.skip
|
||||
},
|
||||
{
|
||||
label: this.t('cis.scan.failed'),
|
||||
value: this.parsedData.fail
|
||||
},
|
||||
{
|
||||
label: this.t('cis.scan.notApplicable'),
|
||||
value: this.parsedData.notApplicable
|
||||
},
|
||||
{
|
||||
label: this.t('cis.scan.lastScanTime'),
|
||||
value: this.value.status.lastRunTimestamp,
|
||||
component: 'Date'
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
reportCheckHeaders() {
|
||||
return [
|
||||
{
|
||||
...STATE,
|
||||
value: 'state',
|
||||
formatterOpts: { arbitrary: true },
|
||||
sort: 'testStateSort'
|
||||
},
|
||||
{
|
||||
name: 'number',
|
||||
label: this.t('cis.scan.number'),
|
||||
value: 'id',
|
||||
sort: ['id'],
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: this.t('cis.scan.description'),
|
||||
value: 'description'
|
||||
},
|
||||
{
|
||||
name: 'node',
|
||||
label: 'Nodes',
|
||||
value: 'nodes',
|
||||
formatter: 'List',
|
||||
sort: 'nodes'
|
||||
}
|
||||
|
||||
];
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
testStateSort(state) {
|
||||
const SORT_ORDER = {
|
||||
failed: 1,
|
||||
skipped: 2,
|
||||
notApplicable: 3,
|
||||
passed: 4,
|
||||
other: 5,
|
||||
};
|
||||
|
||||
return `${ SORT_ORDER[state] || SORT_ORDER['other'] } ${ state }`;
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="detail mb-20">
|
||||
<div v-for="item in details" :key="item.label">
|
||||
<span class="text-label">{{ item.label }}</span>:
|
||||
<component :is="item.component" v-if="item.component" :value="item.value" />
|
||||
<nuxt-link v-else-if="item.to" :to="item.to">
|
||||
{{ item.value }}
|
||||
</nuxt-link>
|
||||
<span v-else>{{ item.value }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="parsedData">
|
||||
<h3>{{ t('cis.scan.scanReport') }}</h3>
|
||||
<SortableTable
|
||||
default-sort-by="state"
|
||||
:search="false"
|
||||
:row-actions="false"
|
||||
:table-actions="false"
|
||||
:rows="results"
|
||||
:headers="reportCheckHeaders"
|
||||
key-field="id"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.detail {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
|
||||
& .div {
|
||||
padding: 0px 10px 0px 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -107,7 +107,7 @@ export default {
|
|||
</div>
|
||||
<div class="row">
|
||||
<div class="col span-6">
|
||||
<LabeledSelect v-model="value.spec.benchmarkVersion" :label="t('cis.benchmarkVersion')" :options="compatibleBenchmarkNames" />
|
||||
<LabeledSelect v-model="value.spec.benchmarkVersion" :mode="mode" :label="t('cis.benchmarkVersion')" :options="compatibleBenchmarkNames" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="spacer" />
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { CIS } from '@/config/types';
|
||||
import { downloadFile } from '@/utils/download';
|
||||
import { colorForState } from '@/plugins/steve/resource-instance';
|
||||
|
||||
export default {
|
||||
availableActions() {
|
||||
|
|
@ -25,5 +26,15 @@ export default {
|
|||
|
||||
downloadFile(`${ reportCRD.id }.json`, JSON, 'application/json');
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
testState() {
|
||||
return (state) => {
|
||||
const color = colorForState.call(this, this.state);
|
||||
|
||||
const bgColor = this.color.replace('text-', 'bg-');
|
||||
|
||||
return { color, bgColor };
|
||||
};
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ const STATES = {
|
|||
locked: { color: 'warning', icon: 'adjust' },
|
||||
migrating: { color: 'info', icon: 'info' },
|
||||
notapplied: { color: 'warning', icon: 'tag' },
|
||||
passed: { color: 'success', icon: 'dot-dotfill' },
|
||||
paused: { color: 'info', icon: 'info' },
|
||||
pending: { color: 'info', icon: 'tag' },
|
||||
provisioning: { color: 'info', icon: 'dot' },
|
||||
|
|
@ -146,6 +147,16 @@ export function colorForState(state) {
|
|||
return `text-${ color }`;
|
||||
}
|
||||
|
||||
export function stateDisplay(state) {
|
||||
// @TODO use translations
|
||||
|
||||
if ( REMAP_STATE[state] ) {
|
||||
return REMAP_STATE[state];
|
||||
}
|
||||
|
||||
return state.split(/-/).map(ucFirst).join('-');
|
||||
}
|
||||
|
||||
function maybeFn(val) {
|
||||
if ( isFunction(val) ) {
|
||||
return val(this);
|
||||
|
|
|
|||
Loading…
Reference in New Issue