mirror of https://github.com/rancher/dashboard.git
Merge branch 'master' into download.yaml.overview
This commit is contained in:
commit
aadb803bc5
|
|
@ -4,6 +4,7 @@ locale:
|
||||||
|
|
||||||
generic:
|
generic:
|
||||||
customize: Customize
|
customize: Customize
|
||||||
|
comingSoon: Coming Soon
|
||||||
|
|
||||||
header:
|
header:
|
||||||
backToRancher: "← Back to Rancher"
|
backToRancher: "← Back to Rancher"
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,6 @@ import {
|
||||||
_UNFLAG,
|
_UNFLAG,
|
||||||
} from '@/config/query-params';
|
} from '@/config/query-params';
|
||||||
|
|
||||||
// import { mapPref, DIFF } from '@/store/prefs';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Footer,
|
Footer,
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,17 @@ export default {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
|
optionKey: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
optionLabel: {
|
optionLabel: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'label'
|
default: 'label'
|
||||||
|
},
|
||||||
|
optionKey: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,11 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
containerClass() {
|
||||||
|
return this.displaySideBySide
|
||||||
|
? 'row'
|
||||||
|
: '';
|
||||||
|
},
|
||||||
sectionClass() {
|
sectionClass() {
|
||||||
return this.displaySideBySide
|
return this.displaySideBySide
|
||||||
? 'col span-6'
|
? 'col span-6'
|
||||||
|
|
@ -39,7 +44,7 @@ export default {
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="row">
|
<div :class="containerClass">
|
||||||
<div :class="sectionClass">
|
<div :class="sectionClass">
|
||||||
<KeyValue
|
<KeyValue
|
||||||
key="labels"
|
key="labels"
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,7 @@ export default {
|
||||||
v-model="name"
|
v-model="name"
|
||||||
label="Name"
|
label="Name"
|
||||||
:disabled="nameDisabled"
|
:disabled="nameDisabled"
|
||||||
:mode="nameMode"
|
:mode="mode"
|
||||||
:min-height="30"
|
:min-height="30"
|
||||||
/>
|
/>
|
||||||
</slot>
|
</slot>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,184 @@
|
||||||
|
<script>
|
||||||
|
import LabeledInput from '@/components/form/LabeledInput';
|
||||||
|
import LabeledSelect from '@/components/form/LabeledSelect';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { LabeledInput, LabeledSelect },
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mode: {
|
||||||
|
type: String,
|
||||||
|
default: 'edit'
|
||||||
|
},
|
||||||
|
|
||||||
|
// weighted selector terms are nested differently
|
||||||
|
isWeighted: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
|
||||||
|
// whether or not to show removal 'x' button in upper right (probably shouldn't show if node selectors are required)
|
||||||
|
showRemove: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
const ops = [{ label: '<', value: 'Lt' }, { label: '>', value: 'Gt' }, { label: 'is set', value: 'Exists' }, { label: 'is not set', value: 'DoesNotExist' }, { label: 'in list', value: 'In' }, { label: 'not in list', value: 'NotIn' }];
|
||||||
|
|
||||||
|
let rules;
|
||||||
|
|
||||||
|
rules = this.value.matchExpressions || [];
|
||||||
|
|
||||||
|
if (this.isWeighted) {
|
||||||
|
rules = this.value?.preference?.matchExpressions || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
rules = rules.map((rule) => {
|
||||||
|
if (rule.values) {
|
||||||
|
rule.values.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
return rule;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!rules.length) {
|
||||||
|
rules.push({ values: '' });
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
ops, rules, custom: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
isView() {
|
||||||
|
return this.mode === 'view';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
removeRule(idx) {
|
||||||
|
this.rules.splice(idx, 1);
|
||||||
|
this.update();
|
||||||
|
},
|
||||||
|
|
||||||
|
addRule() {
|
||||||
|
this.rules.push({ values: '' });
|
||||||
|
},
|
||||||
|
|
||||||
|
update() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const matchExpressions = [
|
||||||
|
...this.rules.map((rule) => {
|
||||||
|
const matchExpression = { key: rule.key };
|
||||||
|
|
||||||
|
if (rule.operator) {
|
||||||
|
matchExpression.operator = rule.operator;
|
||||||
|
}
|
||||||
|
if (rule.values && rule.operator !== 'Exists' && rule.operator !== 'DoesNotExist') {
|
||||||
|
matchExpression.values = (rule.values || []).split(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchExpression;
|
||||||
|
})];
|
||||||
|
|
||||||
|
let out = { matchExpressions };
|
||||||
|
|
||||||
|
if (this.isWeighted) {
|
||||||
|
out = { preference: { matchExpressions }, weight: this.value.weight || 1 };
|
||||||
|
}
|
||||||
|
this.$emit('input', out);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :style="{'position':'relative'}" @input="update">
|
||||||
|
<button v-if="showRemove" id="remove-btn" class="btn btn-lg role-link">
|
||||||
|
<i class="icon icon-lg icon-x" @click="$emit('remove')" />
|
||||||
|
</button>
|
||||||
|
<div class="rule-row headers">
|
||||||
|
<span>Key</span>
|
||||||
|
<span>Operator</span>
|
||||||
|
<span>Value</span>
|
||||||
|
</div>
|
||||||
|
<div v-for="(rule, i) in rules" :key="i">
|
||||||
|
<div class="rule-row">
|
||||||
|
<div class="">
|
||||||
|
<LabeledInput v-model="rule.key" :mode="mode" />
|
||||||
|
</div>
|
||||||
|
<LabeledSelect
|
||||||
|
v-model="rule.operator"
|
||||||
|
class=""
|
||||||
|
:options="ops"
|
||||||
|
:mode="mode"
|
||||||
|
@input="update"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<LabeledInput v-model="rule.values" :mode="mode" :disabled="rule.operator==='Exists' && rule.operator==='DoesNotExist'" />
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
v-if="!isView"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm role-link col remove-rule-button"
|
||||||
|
:style="{padding:'0px'}"
|
||||||
|
:disabled="mode==='view'"
|
||||||
|
@click="removeRule(i)"
|
||||||
|
>
|
||||||
|
<i class="icon icon-minus icon-lg" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button v-if="!isView" type="button" class="btn role-tertiary add" @click="addRule">
|
||||||
|
Add Rule
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang='scss'>
|
||||||
|
#operator {
|
||||||
|
& .vs__dropdown-option{
|
||||||
|
padding: 3px 6px 3px 6px !important
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#remove-btn{
|
||||||
|
padding: 8px;
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
top: 0px;
|
||||||
|
& .icon-x {
|
||||||
|
transform: scale(1.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-row {
|
||||||
|
display:grid;
|
||||||
|
grid-template-columns: auto 20% auto 3%;
|
||||||
|
grid-column-gap:10px;
|
||||||
|
margin-bottom:10px;
|
||||||
|
|
||||||
|
&.headers>* {
|
||||||
|
padding:0px 10px 0px 10px;
|
||||||
|
color: var(--input-label)
|
||||||
|
}
|
||||||
|
|
||||||
|
& .labeled-input INPUT[type='text']:not(.view){
|
||||||
|
padding: 9px 0px 9px 0px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-rule-button{
|
||||||
|
justify-content:center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
<script>
|
||||||
|
import NodeSelectorTerm from './NodeSelectorTerm';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { NodeSelectorTerm },
|
||||||
|
|
||||||
|
props: {
|
||||||
|
// value should be NodeAffinity or VolumeNodeAffinity
|
||||||
|
value: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mode: {
|
||||||
|
type: String,
|
||||||
|
default: 'create'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
// VolumeNodeAffinity only has 'required' field
|
||||||
|
if (this.value.required) {
|
||||||
|
return { nodeSelectorTerms: this.value.required.nodeSelectorTerms, multipleSelectors: true };
|
||||||
|
} else {
|
||||||
|
const { preferredDuringSchedulingIgnoredDuringExecution = [], requiredDuringSchedulingIgnoredDuringExecution = {} } = this.value;
|
||||||
|
const { nodeSelectorTerms = [] } = requiredDuringSchedulingIgnoredDuringExecution;
|
||||||
|
|
||||||
|
if (!nodeSelectorTerms.length) {
|
||||||
|
nodeSelectorTerms.push({ });
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
nodeSelectorTerms,
|
||||||
|
weightedNodeSelectorTerms: preferredDuringSchedulingIgnoredDuringExecution,
|
||||||
|
multipleSelectors: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
hasWeighted() {
|
||||||
|
return !!this.weightedNodeSelectorTerms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="row">
|
||||||
|
<div :class="{'col span-6':hasWeighted, 'col span-12':!hasWeighted}">
|
||||||
|
<slot name="title" />
|
||||||
|
<template v-for="(nodeSelector, i) in nodeSelectorTerms">
|
||||||
|
<div :key="i" class="row">
|
||||||
|
<div :key="i" class="col span-12">
|
||||||
|
<NodeSelectorTerm
|
||||||
|
:mode="mode"
|
||||||
|
:show-remove="false"
|
||||||
|
class="node-selector container"
|
||||||
|
:value="nodeSelector"
|
||||||
|
@remove="e=>nodeSelectorTerms.splice(i, 1)"
|
||||||
|
@input="e=>$set(nodeSelectorTerms, i, e)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<button v-if="multipleSelectors" type="button" class="btn btn-sm role-primary" @click="e=>nodeSelectorTerms.push({matchExpressions:[]})">
|
||||||
|
Add Node Selector
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="hasWeighted" class="col span-6">
|
||||||
|
<slot name="title-weighted" />
|
||||||
|
<template v-for="(nodeSelector, i) in weightedNodeSelectorTerms">
|
||||||
|
<div :key="i" class="row">
|
||||||
|
<div :key="i" class="col span-12">
|
||||||
|
<NodeSelectorTerm
|
||||||
|
:mode="mode"
|
||||||
|
class="node-selector container"
|
||||||
|
is-weighted
|
||||||
|
:value="nodeSelector"
|
||||||
|
@remove="e=>weightedNodeSelectorTerms.splice(i, 1)"
|
||||||
|
@input="e=>$set(weightedNodeSelectorTerms, i, e)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<button type="button" class="btn btn-sm role-primary" @click="e=>weightedNodeSelectorTerms.push({matchExpressions:[], weight: 1})">
|
||||||
|
Add Node Selector
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.node-selector {
|
||||||
|
padding: 10px;
|
||||||
|
background-color: var(--body-bg);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -10,6 +10,14 @@ export const STATE = {
|
||||||
formatter: 'BadgeState',
|
formatter: 'BadgeState',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const DOWNLOAD = {
|
||||||
|
name: 'download',
|
||||||
|
label: 'Download',
|
||||||
|
value: 'download',
|
||||||
|
canBeVariable: true,
|
||||||
|
align: 'right',
|
||||||
|
};
|
||||||
|
|
||||||
export const NAME = {
|
export const NAME = {
|
||||||
name: 'name',
|
name: 'name',
|
||||||
label: 'Name',
|
label: 'Name',
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,16 @@
|
||||||
<script>
|
<script>
|
||||||
import { DESCRIPTION } from '@/config/labels-annotations';
|
import { DESCRIPTION } from '@/config/labels-annotations';
|
||||||
|
import CreateEditView from '@/mixins/create-edit-view';
|
||||||
import DetailTop from '@/components/DetailTop';
|
import DetailTop from '@/components/DetailTop';
|
||||||
|
import Labels from '@/components/form/Labels';
|
||||||
import SortableTable from '@/components/SortableTable';
|
import SortableTable from '@/components/SortableTable';
|
||||||
import VStack from '@/components/Layout/Stack/VStack';
|
import VStack from '@/components/Layout/Stack/VStack';
|
||||||
|
import { downloadFile } from '@/utils/download';
|
||||||
|
import Tab from '@/components/Tabbed/Tab';
|
||||||
|
import Tabbed from '@/components/Tabbed';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
DOWNLOAD,
|
||||||
KEY,
|
KEY,
|
||||||
VALUE,
|
VALUE,
|
||||||
STATE,
|
STATE,
|
||||||
|
|
@ -17,10 +24,15 @@ export default {
|
||||||
name: 'DetailConfigMap',
|
name: 'DetailConfigMap',
|
||||||
components: {
|
components: {
|
||||||
DetailTop,
|
DetailTop,
|
||||||
|
Labels,
|
||||||
SortableTable,
|
SortableTable,
|
||||||
|
Tab,
|
||||||
|
Tabbed,
|
||||||
VStack
|
VStack
|
||||||
},
|
},
|
||||||
|
|
||||||
|
mixins: [CreateEditView],
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
|
@ -29,12 +41,18 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
|
const valuesTableHeaders = [
|
||||||
|
{
|
||||||
|
...KEY, sort: false, width: 400
|
||||||
|
},
|
||||||
|
{ ...VALUE, sort: false },
|
||||||
|
];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
valuesTableHeaders: [
|
valuesTableHeaders,
|
||||||
{
|
binaryValuesTableHeaders: [
|
||||||
...KEY, sort: false, width: 400
|
...valuesTableHeaders,
|
||||||
},
|
DOWNLOAD
|
||||||
{ ...VALUE, sort: false },
|
|
||||||
],
|
],
|
||||||
relatedWorkloadsHeaders: [
|
relatedWorkloadsHeaders: [
|
||||||
STATE,
|
STATE,
|
||||||
|
|
@ -54,27 +72,15 @@ export default {
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
binaryValuesTableRows() {
|
||||||
|
return Object.entries(this.value.binaryData || {}).map(kvp => ({
|
||||||
|
key: kvp[0],
|
||||||
|
value: `${ kvp[1].length } byte${ kvp[1].length !== 1 ? 's' : '' }`
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
relatedWorkloadsRows() {
|
relatedWorkloadsRows() {
|
||||||
return [
|
return [];
|
||||||
{
|
|
||||||
stateDisplay: 'Success',
|
|
||||||
stateBackground: 'bg-success',
|
|
||||||
nameDisplay: 'Workload0',
|
|
||||||
detailUrl: '#',
|
|
||||||
scale: 4,
|
|
||||||
image: 'nginx',
|
|
||||||
created: '2020-01-20T09:00:00+00:00'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
stateDisplay: 'Success',
|
|
||||||
stateBackground: 'bg-success',
|
|
||||||
nameDisplay: 'Workload1',
|
|
||||||
detailUrl: '#',
|
|
||||||
scale: 44,
|
|
||||||
image: 'ubuntu',
|
|
||||||
created: '2020-01-20T11:00:00+00:00'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
},
|
},
|
||||||
|
|
||||||
detailTopColumns() {
|
detailTopColumns() {
|
||||||
|
|
@ -93,47 +99,74 @@ export default {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onDownloadClick(file, ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
downloadFile(file.key, file.value, 'application/octet-stream');
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VStack class="config-map">
|
<VStack class="config-map">
|
||||||
<DetailTop :columns="detailTopColumns" />
|
<DetailTop class="detail-top" :columns="detailTopColumns" />
|
||||||
<div>
|
<div>
|
||||||
<div class="title">
|
<h2>
|
||||||
Values
|
Related Workloads
|
||||||
</div>
|
</h2>
|
||||||
<SortableTable
|
|
||||||
key-field="_key"
|
|
||||||
:headers="valuesTableHeaders"
|
|
||||||
:rows="valuesTableRows"
|
|
||||||
:row-actions="false"
|
|
||||||
:search="false"
|
|
||||||
:table-actions="false"
|
|
||||||
:top-divider="false"
|
|
||||||
:emphasized-body="false"
|
|
||||||
:body-dividers="true"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div>Related Workloads</div>
|
|
||||||
<SortableTable
|
<SortableTable
|
||||||
key-field="_key"
|
key-field="_key"
|
||||||
:headers="relatedWorkloadsHeaders"
|
:headers="relatedWorkloadsHeaders"
|
||||||
:rows="relatedWorkloadsRows"
|
:rows="relatedWorkloadsRows"
|
||||||
:row-actions="false"
|
:row-actions="false"
|
||||||
:search="false"
|
:search="false"
|
||||||
|
no-rows-key="generic.comingSoon"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<Tabbed default-tab="values">
|
||||||
|
<Tab name="values" label="Values">
|
||||||
|
<SortableTable
|
||||||
|
key-field="_key"
|
||||||
|
:headers="valuesTableHeaders"
|
||||||
|
:rows="valuesTableRows"
|
||||||
|
:row-actions="false"
|
||||||
|
:search="false"
|
||||||
|
:table-actions="false"
|
||||||
|
:top-divider="false"
|
||||||
|
:emphasized-body="false"
|
||||||
|
:body-dividers="true"
|
||||||
|
/>
|
||||||
|
</Tab>
|
||||||
|
<Tab name="binary-values" label="Binary Values">
|
||||||
|
<SortableTable
|
||||||
|
key-field="_key"
|
||||||
|
:headers="binaryValuesTableHeaders"
|
||||||
|
:rows="binaryValuesTableRows"
|
||||||
|
:row-actions="false"
|
||||||
|
:search="false"
|
||||||
|
:table-actions="false"
|
||||||
|
:top-divider="false"
|
||||||
|
:emphasized-body="false"
|
||||||
|
:body-dividers="true"
|
||||||
|
>
|
||||||
|
<template #col:download="{row}">
|
||||||
|
<td data-title="Download:" align="right" class="col-click-expand">
|
||||||
|
<a href="#" @click="onDownloadClick(row, $event)">Download</a>
|
||||||
|
</td>
|
||||||
|
</template>
|
||||||
|
</SortableTable>
|
||||||
|
</Tab>
|
||||||
|
<Tab label="Labels and Annotations" name="labelsAndAnnotations">
|
||||||
|
<Labels :spec="value" :mode="mode" />
|
||||||
|
</Tab>
|
||||||
|
</Tabbed>
|
||||||
</VStack>
|
</VStack>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.config-map > * {
|
.detail-top {
|
||||||
margin-bottom: 50px;
|
margin-bottom: 50px;
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import Date from '@/components/formatter/Date';
|
||||||
import LoadDeps from '@/mixins/load-deps';
|
import LoadDeps from '@/mixins/load-deps';
|
||||||
import { allHash } from '@/utils/promise';
|
import { allHash } from '@/utils/promise';
|
||||||
import WorkloadPorts from '@/edit/workload/WorkloadPorts';
|
import WorkloadPorts from '@/edit/workload/WorkloadPorts';
|
||||||
|
import { DESCRIPTION } from '@/config/labels-annotations';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -126,10 +127,15 @@ export default {
|
||||||
|
|
||||||
detailTopColumns() {
|
detailTopColumns() {
|
||||||
return [
|
return [
|
||||||
|
|
||||||
{
|
{
|
||||||
title: 'Namespace',
|
title: 'Namespace',
|
||||||
content: get(this.value, 'metadata.namespace')
|
content: get(this.value, 'metadata.namespace')
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Description',
|
||||||
|
content: this.value?.metadata?.annotations[DESCRIPTION]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'Image',
|
title: 'Image',
|
||||||
content: this.container.image
|
content: this.container.image
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,13 @@ import CreateEditView from '@/mixins/create-edit-view';
|
||||||
import NameNsDescription from '@/components/form/NameNsDescription';
|
import NameNsDescription from '@/components/form/NameNsDescription';
|
||||||
import Footer from '@/components/form/Footer';
|
import Footer from '@/components/form/Footer';
|
||||||
import KeyValue from '@/components/form/KeyValue';
|
import KeyValue from '@/components/form/KeyValue';
|
||||||
|
import Labels from '@/components/form/Labels';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CruConfigMap',
|
name: 'CruConfigMap',
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
|
Labels,
|
||||||
NameNsDescription,
|
NameNsDescription,
|
||||||
KeyValue,
|
KeyValue,
|
||||||
Footer,
|
Footer,
|
||||||
|
|
@ -33,7 +35,7 @@ export default {
|
||||||
key="data"
|
key="data"
|
||||||
v-model="value.data"
|
v-model="value.data"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
title="Data"
|
title="Values"
|
||||||
protip="Use this area for anything that's UTF-8 text data"
|
protip="Use this area for anything that's UTF-8 text data"
|
||||||
:initial-empty-row="true"
|
:initial-empty-row="true"
|
||||||
/>
|
/>
|
||||||
|
|
@ -47,7 +49,7 @@ export default {
|
||||||
<KeyValue
|
<KeyValue
|
||||||
key="binaryData"
|
key="binaryData"
|
||||||
v-model="value.binaryData"
|
v-model="value.binaryData"
|
||||||
title="Binary Data"
|
title="Binary Values"
|
||||||
protip="Use this area for binary or other data that is not UTF-8 text"
|
protip="Use this area for binary or other data that is not UTF-8 text"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
:add-allowed="false"
|
:add-allowed="false"
|
||||||
|
|
@ -61,6 +63,8 @@ export default {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Labels :spec="value" :mode="mode" />
|
||||||
|
|
||||||
<Footer :mode="mode" :errors="errors" @save="save" @done="done" />
|
<Footer :mode="mode" :errors="errors" @save="save" @done="done" />
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
<script>
|
<script>
|
||||||
import { get } from '../../utils/object';
|
|
||||||
import RadioGroup from '@/components/form/RadioGroup';
|
import RadioGroup from '@/components/form/RadioGroup';
|
||||||
import LabeledSelect from '@/components/form/LabeledSelect';
|
import LabeledSelect from '@/components/form/LabeledSelect';
|
||||||
import Selectors from '@/edit/workload/Selectors';
|
import NodeAffinity from '@/components/form/NodeAffinity';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
RadioGroup,
|
RadioGroup,
|
||||||
Selectors,
|
LabeledSelect,
|
||||||
LabeledSelect
|
NodeAffinity
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
|
|
@ -26,9 +25,12 @@ export default {
|
||||||
const { affinity = {}, nodeName = '' } = this.value;
|
const { affinity = {}, nodeName = '' } = this.value;
|
||||||
const { nodeAffinity = {} } = affinity;
|
const { nodeAffinity = {} } = affinity;
|
||||||
|
|
||||||
const required = get(nodeAffinity, 'requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms') || [];
|
if (!nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution) {
|
||||||
const preferred = get(nodeAffinity, 'preferredDuringSchedulingIgnoredDuringExecution') || [];
|
this.$set(nodeAffinity, 'requiredDuringSchedulingIgnoredDuringExecution', { nodeSelectorTerms: [] } );
|
||||||
|
}
|
||||||
|
if (!nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution) {
|
||||||
|
this.$set(nodeAffinity, 'preferredDuringSchedulingIgnoredDuringExecution', []);
|
||||||
|
}
|
||||||
let selectNode = false;
|
let selectNode = false;
|
||||||
|
|
||||||
if (nodeName.length) {
|
if (nodeName.length) {
|
||||||
|
|
@ -36,44 +38,13 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
required, preferred, selectNode, nodeName
|
selectNode, nodeName, nodeAffinity
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
|
||||||
preferred() {
|
|
||||||
this.update();
|
|
||||||
},
|
|
||||||
required() {
|
|
||||||
this.update();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
update() {
|
update() {
|
||||||
const out = {
|
const out = { ...this.value, affinity: this.nodeAffinity };
|
||||||
...this.value,
|
|
||||||
affinity: {
|
|
||||||
nodeAffinity: {
|
|
||||||
preferredDuringSchedulingIgnoredDuringExecution: this.preferred.map((rule) => {
|
|
||||||
let weight = 1;
|
|
||||||
|
|
||||||
if (!!rule.weight) {
|
|
||||||
weight = rule.weight;
|
|
||||||
}
|
|
||||||
delete rule.weight;
|
|
||||||
|
|
||||||
return { preference: { matchExpressions: [rule] }, weight };
|
|
||||||
}),
|
|
||||||
requiredDuringSchedulingIgnoredDuringExecution: {
|
|
||||||
nodeSelectorTerms: this.required.map((rule) => {
|
|
||||||
return { matchExpressions: [rule] };
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.selectNode) {
|
if (this.selectNode) {
|
||||||
this.$set(out, 'nodeName', this.nodeName);
|
this.$set(out, 'nodeName', this.nodeName);
|
||||||
|
|
@ -98,22 +69,18 @@ export default {
|
||||||
<LabeledSelect v-model="nodeName" :options="nodes" :mode="mode" />
|
<LabeledSelect v-model="nodeName" :options="nodes" :mode="mode" />
|
||||||
</div>
|
</div>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="row">
|
<NodeAffinity :value="nodeAffinity" :mode="mode">
|
||||||
<div class="col span-6">
|
<template #title>
|
||||||
<h5 class="mb-10">
|
<h5 class="mb-10">
|
||||||
Require all of:
|
Require all of:
|
||||||
</h5>
|
</h5>
|
||||||
<span v-if="mode==='view' && !required.length">n/a </span>
|
</template>
|
||||||
<Selectors v-model="required" :mode="mode" />
|
<template #title-weighted>
|
||||||
</div>
|
|
||||||
<div class="col span-6">
|
|
||||||
<h5 class="mb-10">
|
<h5 class="mb-10">
|
||||||
Prefer any of:
|
Prefer any of:
|
||||||
</h5>
|
</h5>
|
||||||
<span v-if="mode==='view' && !preferred.length">n/a </span>
|
</template>
|
||||||
<Selectors v-else v-model="preferred" is-weighted :mode="mode" />
|
</NodeAffinity>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,190 +0,0 @@
|
||||||
<script>
|
|
||||||
import LabeledInput from '@/components/form/LabeledInput';
|
|
||||||
import LabeledSelect from '@/components/form/LabeledSelect';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: { LabeledInput, LabeledSelect },
|
|
||||||
props: {
|
|
||||||
value: {
|
|
||||||
type: Array,
|
|
||||||
default: () => []
|
|
||||||
},
|
|
||||||
|
|
||||||
mode: {
|
|
||||||
type: String,
|
|
||||||
default: 'edit'
|
|
||||||
},
|
|
||||||
|
|
||||||
isWeighted: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
const ops = [{ label: '<', value: 'Lt' }, { label: '>', value: 'Gt' }, { label: 'is set', value: 'Exists' }, { label: 'is not set', value: 'DoesNotExist' }, { label: 'in list', value: 'In' }, { label: 'not in list', value: 'NotIn' }];
|
|
||||||
|
|
||||||
// if node affinity has weighted rules, the matchExpressions are nested further in 'preference' field
|
|
||||||
const rules = this.isWeighted ? this.value.map((rule) => {
|
|
||||||
return { ...rule.preference.matchExpressions[0], weight: rule.weight };
|
|
||||||
})
|
|
||||||
: this.value.map(rule => rule.matchExpressions[0]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
ops, rules, custom: []
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
isView() {
|
|
||||||
return this.mode === 'view';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
parseRuleString(rule) {
|
|
||||||
const all = rule.split(' ');
|
|
||||||
let key; let op; let value;
|
|
||||||
|
|
||||||
if (all[0].length) {
|
|
||||||
if (all.length > 1) {
|
|
||||||
key = all[0];
|
|
||||||
op = all[1];
|
|
||||||
value = all[2].startsWith('(') ? all[2].slice(1, all[2].length - 1) : all[2];
|
|
||||||
} else if (all[0].startsWith('!')) {
|
|
||||||
op = '!';
|
|
||||||
key = all[0].slice(1);
|
|
||||||
|
|
||||||
return { key, op };
|
|
||||||
} else {
|
|
||||||
key = all[0];
|
|
||||||
op = ' ';
|
|
||||||
|
|
||||||
return { key, op };
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
key, op, value
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
removeRule(idx) {
|
|
||||||
this.rules.splice(idx, 1);
|
|
||||||
this.update();
|
|
||||||
},
|
|
||||||
|
|
||||||
removeCustom(idx) {
|
|
||||||
this.custom.splice(idx, 1);
|
|
||||||
this.update();
|
|
||||||
},
|
|
||||||
|
|
||||||
addRule() {
|
|
||||||
this.rules.push({ values: [] });
|
|
||||||
},
|
|
||||||
|
|
||||||
addCustomRule() {
|
|
||||||
this.custom.push('');
|
|
||||||
},
|
|
||||||
|
|
||||||
updateCustom(idx, rule) {
|
|
||||||
this.$set(this.custom, idx, rule);
|
|
||||||
},
|
|
||||||
|
|
||||||
update() {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
const out = [
|
|
||||||
...this.rules.map((rule) => {
|
|
||||||
const matchExpression = { key: rule.key };
|
|
||||||
|
|
||||||
if (rule.operator) {
|
|
||||||
matchExpression.operator = rule.operator;
|
|
||||||
}
|
|
||||||
if (rule.values.length && rule.operator !== 'Exists' && rule.operator !== 'DoesNotExist') {
|
|
||||||
matchExpression.values = rule.values;
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchExpression;
|
|
||||||
}),
|
|
||||||
...this.custom.map((rule) => {
|
|
||||||
return {
|
|
||||||
key: rule,
|
|
||||||
operator: 'Exists'
|
|
||||||
};
|
|
||||||
})];
|
|
||||||
|
|
||||||
this.$emit('input', out);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div @input="update">
|
|
||||||
<div v-for="(rule, i) in rules" :key="i">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col span-4">
|
|
||||||
<LabeledInput v-model="rule.key" label="Key" :mode="mode" />
|
|
||||||
</div>
|
|
||||||
<LabeledSelect
|
|
||||||
id="operator"
|
|
||||||
v-model="rule.operator"
|
|
||||||
class="col span-2"
|
|
||||||
:options="ops"
|
|
||||||
:mode="mode"
|
|
||||||
label="Op"
|
|
||||||
@input="update"
|
|
||||||
/>
|
|
||||||
<!-- use conditional rendering here to avoid this v-model breaking the page if rule.values doesn't exist -->
|
|
||||||
<div v-if="rule.operator!=='Exists'&&rule.operator!=='DoesNotExist'" class="col span-4">
|
|
||||||
<LabeledInput v-model="rule.values[0]" label="Value" :mode="mode" />
|
|
||||||
</div>
|
|
||||||
<div v-if="isWeighted" class="col span-2">
|
|
||||||
<LabeledInput v-model="rule.weight" label="Weight" :mode="mode" />
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
v-if="!isView"
|
|
||||||
type="button"
|
|
||||||
class="btn btn-sm role-link"
|
|
||||||
:style="{padding:'0px'}"
|
|
||||||
:disabled="mode==='view'"
|
|
||||||
@click="removeRule(i)"
|
|
||||||
>
|
|
||||||
<!-- REMOVE -->
|
|
||||||
<i class="icon icon-minus icon-lg" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-for="(rule, i) in custom" :key="i" class="row">
|
|
||||||
<div class="col span-10">
|
|
||||||
<LabeledInput :multiline="false" :value="rule" placeholder="e.g. foo > 42 && bar != baz" :mode="mode" @input="e=>updateCustom(i, e)" />
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
v-if="!isView"
|
|
||||||
type="button"
|
|
||||||
class="btn btn-sm role-link"
|
|
||||||
:style="{padding:'0px'}"
|
|
||||||
:disabled="mode==='view'"
|
|
||||||
@click="removeCustom(i)"
|
|
||||||
>
|
|
||||||
<!-- REMOVE -->
|
|
||||||
<i class="icon icon-minus icon-lg" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<button v-if="!isView" type="button" class="btn role-tertiary add" @click="addRule">
|
|
||||||
Add Rule
|
|
||||||
</button>
|
|
||||||
<!-- <button v-if="!isView" type="button" class="btn role-tertiary add" @click="addCustomRule">
|
|
||||||
Add custom rule
|
|
||||||
</button> -->
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang='scss'>
|
|
||||||
#operator {
|
|
||||||
& .vs__dropdown-option{
|
|
||||||
padding: 3px 6px 3px 6px !important
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -22,6 +22,23 @@ import WorkloadPorts from '@/edit/workload/WorkloadPorts';
|
||||||
import { defaultAsyncData } from '@/components/ResourceDetail.vue';
|
import { defaultAsyncData } from '@/components/ResourceDetail.vue';
|
||||||
import { _EDIT } from '@/config/query-params';
|
import { _EDIT } from '@/config/query-params';
|
||||||
|
|
||||||
|
const workloadTypeOptions = [
|
||||||
|
{ value: WORKLOAD_TYPES.DEPLOYMENT, label: 'Deployment' },
|
||||||
|
|
||||||
|
{ value: WORKLOAD_TYPES.DAEMON_SET, label: 'Daemon Set' },
|
||||||
|
|
||||||
|
{ value: WORKLOAD_TYPES.STATEFUL_SET, label: 'Stateful Set' },
|
||||||
|
|
||||||
|
{ value: WORKLOAD_TYPES.REPLICA_SET, label: 'Replica Set' },
|
||||||
|
|
||||||
|
{ value: WORKLOAD_TYPES.JOB, label: 'Job' },
|
||||||
|
|
||||||
|
{ value: WORKLOAD_TYPES.CRON_JOB, label: 'Cron Job' },
|
||||||
|
|
||||||
|
{ value: WORKLOAD_TYPES.REPLICATION_CONTROLLER, label: 'Replication Controller' }
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CruWorkload',
|
name: 'CruWorkload',
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -39,15 +56,17 @@ export default {
|
||||||
Networking,
|
Networking,
|
||||||
Footer,
|
Footer,
|
||||||
Job,
|
Job,
|
||||||
WorkloadPorts
|
WorkloadPorts,
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [CreateEditView, LoadDeps],
|
mixins: [CreateEditView, LoadDeps],
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
mode: {
|
mode: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'create'
|
default: 'create'
|
||||||
|
|
@ -55,23 +74,6 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
const typeOpts = [];
|
|
||||||
const workloadMap = {
|
|
||||||
[WORKLOAD_TYPES.DEPLOYMENT]: 'Deployment',
|
|
||||||
[WORKLOAD_TYPES.DAEMON_SET]: 'Daemon Set',
|
|
||||||
[WORKLOAD_TYPES.STATEFUL_SET]: 'Stateful Set',
|
|
||||||
[WORKLOAD_TYPES.REPLICA_SET]: 'Replica Set',
|
|
||||||
[WORKLOAD_TYPES.JOB]: 'Job',
|
|
||||||
[WORKLOAD_TYPES.CRON_JOB]: 'Cron Job',
|
|
||||||
[WORKLOAD_TYPES.REPLICATION_CONTROLLER]: 'Replication Controller'
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const key in workloadMap) {
|
|
||||||
typeOpts.push({ value: key, label: workloadMap[key] });
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectNode = false;
|
|
||||||
|
|
||||||
let type = this.value._type || this.value.type || WORKLOAD_TYPES.DEPLOYMENT;
|
let type = this.value._type || this.value.type || WORKLOAD_TYPES.DEPLOYMENT;
|
||||||
|
|
||||||
if (type === 'workload') {
|
if (type === 'workload') {
|
||||||
|
|
@ -90,45 +92,62 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
selectNode,
|
|
||||||
spec,
|
spec,
|
||||||
type,
|
type,
|
||||||
typeOpts,
|
workloadTypeOptions,
|
||||||
allConfigMaps: null,
|
allConfigMaps: null,
|
||||||
allSecrets: null,
|
allSecrets: null,
|
||||||
allNodes: null,
|
allNodes: null,
|
||||||
showTabs: false
|
showTabs: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
schema() {
|
|
||||||
return this.$store.getters['cluster/schemaFor']( this.type );
|
isEdit() {
|
||||||
|
return this.mode === _EDIT;
|
||||||
|
},
|
||||||
|
|
||||||
|
isJob() {
|
||||||
|
return this.type === WORKLOAD_TYPES.JOB || this.isCronJob;
|
||||||
|
},
|
||||||
|
|
||||||
|
isCronJob() {
|
||||||
|
return this.type === WORKLOAD_TYPES.CRON_JOB;
|
||||||
|
},
|
||||||
|
|
||||||
|
isReplicable() {
|
||||||
|
return (this.type === WORKLOAD_TYPES.DEPLOYMENT || this.type === WORKLOAD_TYPES.REPLICA_SET || this.type === WORKLOAD_TYPES.REPLICATION_CONTROLLER || this.type === WORKLOAD_TYPES.STATEFUL_SET);
|
||||||
|
},
|
||||||
|
|
||||||
|
// if this is a cronjob, grab pod spec from within job template spec
|
||||||
|
podTemplateSpec: {
|
||||||
|
get() {
|
||||||
|
return this.isCronJob ? this.spec.jobTemplate.spec.template.spec : this.spec.template.spec;
|
||||||
|
},
|
||||||
|
set(neu) {
|
||||||
|
if (this.isJob) {
|
||||||
|
this.$set(this.spec.jobTemplate.spec.template, 'spec', neu);
|
||||||
|
} else {
|
||||||
|
this.$set(this.spec.template, 'spec', neu);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
container: {
|
container: {
|
||||||
get() {
|
get() {
|
||||||
let template = this.spec.template;
|
const { containers } = this.podTemplateSpec;
|
||||||
|
|
||||||
if (this.isCronJob) {
|
|
||||||
template = this.spec.jobTemplate.spec.template;
|
|
||||||
}
|
|
||||||
const { containers } = template.spec;
|
|
||||||
|
|
||||||
if (!containers) {
|
if (!containers) {
|
||||||
this.$set(template.spec, 'containers', [{ name: this.value.metadata.name }]);
|
this.$set(this.podTemplateSpec, 'containers', [{ name: this.value.metadata.name }]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return template.spec.containers[0];
|
// TODO account for multiple containers (sidecar)
|
||||||
|
return this.podTemplateSpec.containers[0];
|
||||||
},
|
},
|
||||||
|
|
||||||
set(neu) {
|
set(neu) {
|
||||||
let template = this.spec.template;
|
this.$set(this.podTemplateSpec.containers, 0, { ...neu, name: this.value.metadata.name });
|
||||||
|
|
||||||
if (this.isCronJob) {
|
|
||||||
template = this.spec.jobTemplate.spec.template;
|
|
||||||
}
|
|
||||||
this.$set(template.spec.containers, 0, { ...neu, name: this.value.metadata.name });
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -150,18 +169,11 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
canReplicate() {
|
schema() {
|
||||||
return (this.type === WORKLOAD_TYPES.DEPLOYMENT || this.type === WORKLOAD_TYPES.REPLICA_SET || this.type === WORKLOAD_TYPES.REPLICATION_CONTROLLER || this.type === WORKLOAD_TYPES.STATEFUL_SET);
|
return this.$store.getters['cluster/schemaFor']( this.type );
|
||||||
},
|
|
||||||
|
|
||||||
isJob() {
|
|
||||||
return this.type === WORKLOAD_TYPES.JOB || this.isCronJob;
|
|
||||||
},
|
|
||||||
|
|
||||||
isCronJob() {
|
|
||||||
return this.type === WORKLOAD_TYPES.CRON_JOB;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// show cron schedule in human-readable format
|
||||||
cronLabel() {
|
cronLabel() {
|
||||||
const { schedule } = this.spec;
|
const { schedule } = this.spec;
|
||||||
|
|
||||||
|
|
@ -182,9 +194,6 @@ export default {
|
||||||
return { 'workload.user.cattle.io/workloadselector': `${ 'deployment' }-${ this.value.metadata.namespace }-${ this.value.metadata.name }` };
|
return { 'workload.user.cattle.io/workloadselector': `${ 'deployment' }-${ this.value.metadata.namespace }-${ this.value.metadata.name }` };
|
||||||
},
|
},
|
||||||
|
|
||||||
isEdit() {
|
|
||||||
return this.mode === _EDIT;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
|
|
@ -198,7 +207,7 @@ export default {
|
||||||
|
|
||||||
this.$set(template.spec, 'restartPolicy', restartPolicy);
|
this.$set(template.spec, 'restartPolicy', restartPolicy);
|
||||||
|
|
||||||
if (!this.canReplicate) {
|
if (!this.isReplicable) {
|
||||||
delete this.spec.replicas;
|
delete this.spec.replicas;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -273,7 +282,7 @@ export default {
|
||||||
<slot :value="value" name="top">
|
<slot :value="value" name="top">
|
||||||
<NameNsDescription :value="value" :mode="mode" :extra-columns="['type']">
|
<NameNsDescription :value="value" :mode="mode" :extra-columns="['type']">
|
||||||
<template v-slot:type>
|
<template v-slot:type>
|
||||||
<LabeledSelect v-model="type" label="Type" :disabled="isEdit" :options="typeOpts" />
|
<LabeledSelect v-model="type" label="Type" :disabled="isEdit" :options="workloadTypeOptions" />
|
||||||
</template>
|
</template>
|
||||||
</NameNsDescription>
|
</NameNsDescription>
|
||||||
|
|
||||||
|
|
@ -288,7 +297,7 @@ export default {
|
||||||
<span class="cron-hint text-small">{{ cronLabel }}</span>
|
<span class="cron-hint text-small">{{ cronLabel }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="canReplicate">
|
<template v-if="isReplicable">
|
||||||
<div class="col span-4">
|
<div class="col span-4">
|
||||||
<LabeledInput v-model.number="spec.replicas" label="Replicas" />
|
<LabeledInput v-model.number="spec.replicas" label="Replicas" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -315,20 +324,17 @@ export default {
|
||||||
/>
|
/>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab label="Networking" name="networking">
|
<Tab label="Networking" name="networking">
|
||||||
<Networking v-if="isCronJob" v-model="spec.jobTemplate.spec.template.spec" :mode="mode" />
|
<Networking v-model="podTemplateSpec" :mode="mode" />
|
||||||
<Networking v-else v-model="spec.template.spec" :mode="mode" />
|
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab label="Health" name="health">
|
<Tab label="Health" name="health">
|
||||||
<HealthCheck :spec="container" :mode="mode" />
|
<HealthCheck :spec="container" :mode="mode" />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab label="Security" name="security">
|
<Tab label="Security" name="security">
|
||||||
<Security v-if="isCronJob" v-model="spec.jobTemplate.spec.template.spec" :mode="mode" />
|
<Security v-model="podTemplateSpec" :mode="mode" />
|
||||||
<Security v-else v-model="spec.template.spec" :mode="mode" />
|
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab label="Node Scheduling" name="scheduling">
|
<Tab label="Node Scheduling" name="scheduling">
|
||||||
<Scheduling v-if="isCronJob" v-model="spec.jobTemplate.spec.template.spec" :nodes="allNodes" :mode="mode" />
|
<Scheduling v-model="podTemplateSpec" :mode="mode" />
|
||||||
<Scheduling v-else v-model="spec.template.spec" :mode="mode" />
|
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab label="Scaling/Upgrade Policy" name="upgrading">
|
<Tab label="Scaling/Upgrade Policy" name="upgrading">
|
||||||
<Upgrading v-model="spec" :mode="mode" />
|
<Upgrading v-model="spec" :mode="mode" />
|
||||||
|
|
@ -338,7 +344,7 @@ export default {
|
||||||
<Labels :spec="value" :mode="mode" />
|
<Labels :spec="value" :mode="mode" />
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabbed>
|
</Tabbed>
|
||||||
<Footer :errors="errors" :mode="mode" @save="saveWorkload" @done="done" />
|
<Footer v-if="mode!= 'view'" :errors="errors" :mode="mode" @save="saveWorkload" @done="done" />
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
export default {
|
export default {
|
||||||
// remove clone as yaml/edit as yaml until API supported
|
// remove clone as yaml/edit as yaml until API supported
|
||||||
|
|
||||||
_availableActions() {
|
_availableActions() {
|
||||||
let out = this._standardActions;
|
let out = this._standardActions;
|
||||||
|
|
||||||
|
|
@ -19,5 +18,5 @@ export default {
|
||||||
});
|
});
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "./node_modules/.bin/eslint --ext .js,.vue .",
|
"lint": "./node_modules/.bin/eslint --max-warnings 0 --ext .js,.vue .",
|
||||||
"test": "./node_modules/.bin/nyc ava --serial --verbose",
|
"test": "./node_modules/.bin/nyc ava --serial --verbose",
|
||||||
"nuxt": "./node_modules/.bin/nuxt",
|
"nuxt": "./node_modules/.bin/nuxt",
|
||||||
"dev": "./node_modules/.bin/nuxt dev",
|
"dev": "./node_modules/.bin/nuxt dev",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue