revert NameNsDescription and filter annotations, labels in create-edit-view

This commit is contained in:
Nancy Butler 2020-04-22 17:21:18 -07:00
parent 6249c766ab
commit b8a996c43f
18 changed files with 147 additions and 84 deletions

View File

@ -1,6 +1,7 @@
<script> <script>
import { sortBy } from '@/utils/sort'; import { sortBy } from '@/utils/sort';
import { NAMESPACE } from '@/config/types'; import { NAMESPACE } from '@/config/types';
import { DESCRIPTION } from '@/config/labels-annotations';
import { _VIEW, _EDIT } from '@/config/query-params'; import { _VIEW, _EDIT } from '@/config/query-params';
import LabeledInput from '@/components/form/LabeledInput'; import LabeledInput from '@/components/form/LabeledInput';
import InputWithSelect from '@/components/form/InputWithSelect'; import InputWithSelect from '@/components/form/InputWithSelect';
@ -12,52 +13,38 @@ export default {
}, },
props: { props: {
/* metadata or any object with name, namespace, fields to be modified */
value: { value: {
type: Object, type: Object,
required: true, required: true,
}, },
description: {
type: String,
default: null
},
mode: { mode: {
type: String, type: String,
required: true, required: true,
}, },
namespaced: { namespaced: {
type: Boolean, type: Boolean,
default: true, default: true,
}, },
extraColumns: { extraColumns: {
type: Array, type: Array,
default: () => [] default: () => []
}, },
extraDetailColumns: { extraDetailColumns: {
type: Array, type: Array,
default: () => [] default: () => []
}, },
nameEditable: { nameEditable: {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
nameLabel: { nameLabel: {
type: String, type: String,
default: 'Name' default: 'Name'
}, },
namePlaceholder: { namePlaceholder: {
type: String, type: String,
default: '' default: ''
}, },
descriptionPlaceholder: { descriptionPlaceholder: {
type: String, type: String,
default: 'Any text you want that better describes this resource' default: 'Any text you want that better describes this resource'
@ -65,15 +52,23 @@ export default {
}, },
data() { data() {
const metadata = { ...this.value }; let metadata = this.value.metadata;
if ( !metadata ) {
metadata = {};
this.value.metadata = metadata;
}
if ( this.namespaced && !metadata.namespace ) { if ( this.namespaced && !metadata.namespace ) {
metadata.namespace = this.$store.getters['defaultNamespace']; metadata.namespace = this.$store.getters['defaultNamespace'];
} }
const description = metadata.annotations?.[DESCRIPTION];
return { return {
namespace: metadata.namespace, namespace: metadata.namespace,
name: metadata.name, name: metadata.name,
description,
}; };
}, },
@ -83,21 +78,24 @@ export default {
}, },
detailTopColumns() { detailTopColumns() {
const { metadata = {} } = this.value;
const { annotations = {} } = metadata;
return [ return [
this.namespace metadata.namespace
? { ? {
title: 'Namespace', title: 'Namespace',
content: this.namespace content: metadata.namespace
} : null, } : null,
this.name metadata.name
? { ? {
title: 'Name', title: 'Name',
content: this.name content: metadata.name
} : null, } : null,
this.description annotations[DESCRIPTION]
? { ? {
title: 'Description', title: 'Description',
content: this.description content: annotations[DESCRIPTION]
} : null, } : null,
...this.extraDetailColumns ...this.extraDetailColumns
].filter(c => c); ].filter(c => c);
@ -128,6 +126,20 @@ export default {
}, },
}, },
watch: {
name(val) {
this.value.metadata.name = val;
},
namespace(val) {
this.value.metadata.namespace = val;
},
description(val) {
this.value.setAnnotation(DESCRIPTION, val);
},
},
mounted() { mounted() {
this.$nextTick(() => { this.$nextTick(() => {
if (this.$refs.name) { if (this.$refs.name) {
@ -140,22 +152,6 @@ export default {
changeNameAndNamespace(e) { changeNameAndNamespace(e) {
this.name = (e.text || '').toLowerCase(); this.name = (e.text || '').toLowerCase();
this.namespace = e.selected; this.namespace = e.selected;
this.$nextTick(() => {
this.update();
});
},
update() {
const out = {
...this.value,
name: this.name,
};
if (this.namespaced) {
out.namespace = this.namespace;
}
this.$emit('input', out);
} }
} }
}; };
@ -188,19 +184,17 @@ export default {
:disabled="nameDisabled" :disabled="nameDisabled"
:mode="mode" :mode="mode"
:min-height="30" :min-height="30"
@input="update"
/> />
</slot> </slot>
</div> </div>
<div :class="{col: true, [colSpan]: true}"> <div :class="{col: true, [colSpan]: true}">
<LabeledInput <LabeledInput
key="description" key="description"
:value="description" v-model="description"
label="Description" label="Description"
:mode="mode" :mode="mode"
:placeholder="descriptionPlaceholder" :placeholder="descriptionPlaceholder"
:min-height="30" :min-height="30"
@input="e=>$emit('update:description', e)"
/> />
</div> </div>
<div v-for="slot in extraColumns" :key="slot" :class="{col: true, [colSpan]: true}"> <div v-for="slot in extraColumns" :key="slot" :class="{col: true, [colSpan]: true}">

View File

@ -14,7 +14,7 @@ export default {
return { typeDisplay: this.value.typeDisplay, serviceAccountLink: null }; return { typeDisplay: this.value.typeDisplay, serviceAccountLink: null };
} }
return { typeDisplay: this.value }; return { typeDisplay: this.value, serviceAccountLink: null };
}, },
methods: { methods: {
async findServiceAccount() { async findServiceAccount() {

View File

@ -15,3 +15,33 @@ export const NODE_ROLES = {
WORKER: 'node-role.kubernetes.io/worker', WORKER: 'node-role.kubernetes.io/worker',
ETCD: 'node-role.kubernetes.io/etcd', ETCD: 'node-role.kubernetes.io/etcd',
}; };
export const LABEL_PREFIX_TO_IGNORE = [
'io.cattle.lifecycle.',
'beta.kubernetes.io/',
'failure-domain.beta.kubernetes.io/',
'node-role.kubernetes.io/',
'kubernetes.io/',
'cattle.io/',
'authz.management.cattle.io',
'rke.cattle.io',
'field.cattle.io',
'workload.user.cattle.io/',
'k3s.io',
'node.kubernetes.io',
];
export const ANNOTATIONS_TO_IGNORE_CONTAINS = [
'coreos.com/',
'cattle.io/',
'k3s.io',
];
export const ANNOTATIONS_TO_IGNORE_PREFIX = [
'kubernetes.io/',
'beta.kubernetes.io/',
'node.alpha.kubernetes.io/',
'volumes.kubernetes.io/',
'k3s.io',
]
;

View File

@ -21,8 +21,7 @@ export default {
<template> <template>
<form> <form>
<NameNsDescription <NameNsDescription
v-model="value.metadata" :value="value"
:description.sync="description"
:mode="mode" :mode="mode"
name-label="Name" name-label="Name"
:register-before-hook="registerBeforeHook" :register-before-hook="registerBeforeHook"

View File

@ -72,8 +72,7 @@ export default {
<div> <div>
<form> <form>
<NameNsDescription <NameNsDescription
v-model="value.metadata" :value="value"
:description.sync="description"
:namespaced="false" :namespaced="false"
:mode="mode" :mode="mode"
:extra-columns="extraColumns" :extra-columns="extraColumns"

View File

@ -179,7 +179,7 @@ export default {
<template> <template>
<form> <form>
<NameNsDescription v-model="metadata" :mode="mode" :description.sync="description" /> <NameNsDescription :value="value" :mode="mode" />
<div> <div>
<h3> <h3>
Rules Rules

View File

@ -34,11 +34,10 @@ export default {
<template> <template>
<div class="node"> <div class="node">
<NameNsDescription <NameNsDescription
v-model="value.metadata" :value="value"
:namespaced="false" :namespaced="false"
:mode="mode" :mode="mode"
name-label="Name" name-label="Name"
:description.sync="description"
/> />
<div class="row"> <div class="row">
<Labels :spec="value" :mode="mode" :display-side-by-side="true" /> <Labels :spec="value" :mode="mode" :display-side-by-side="true" />

View File

@ -49,11 +49,10 @@ export default {
<template> <template>
<form> <form>
<NameNsDescription <NameNsDescription
v-model="value.metadata" :value="value"
:mode="mode" :mode="mode"
name-label="Name" name-label="Name"
:register-before-hook="registerBeforeHook" :register-before-hook="registerBeforeHook"
:description.sync="description"
> >
<template v-slot:namespace> <template v-slot:namespace>
<LabeledInput <LabeledInput

View File

@ -47,11 +47,10 @@ export default {
<template> <template>
<form> <form>
<NameNsDescription <NameNsDescription
v-model="value.metadata" :value="value"
:mode="mode" :mode="mode"
name-label="Name" name-label="Name"
:register-before-hook="registerBeforeHook" :register-before-hook="registerBeforeHook"
:description.sync="description"
/> />
<div class="spacer"></div> <div class="spacer"></div>

View File

@ -72,11 +72,10 @@ export default {
</div> </div>
<template v-else> <template v-else>
<NameNsDescription <NameNsDescription
v-model="value.metadata" :value="value"
:mode="mode" :mode="mode"
name-label="Name" name-label="Name"
:register-before-hook="registerBeforeHook" :register-before-hook="registerBeforeHook"
:description.sync="description"
/> />
<div class="spacer"></div> <div class="spacer"></div>

View File

@ -65,11 +65,10 @@ export default {
<form> <form>
<div class="row"> <div class="row">
<NameNsDescription <NameNsDescription
v-model="value.metadata" :value="value"
class="col span-12" class="col span-12"
:mode="mode" :mode="mode"
:register-before-hook="registerBeforeHook" :register-before-hook="registerBeforeHook"
:description.sync="description"
/> />
</div> </div>
<h2>Rules</h2> <h2>Rules</h2>

View File

@ -246,13 +246,12 @@ export default {
<template> <template>
<div> <div>
<NameNsDescription <NameNsDescription
v-model="value.metadata" :value="value"
:mode="mode" :mode="mode"
:name-label="isSidecar ? 'Container Name' : 'Service Name'" :name-label="isSidecar ? 'Container Name' : 'Service Name'"
:extra-columns="extraColumns" :extra-columns="extraColumns"
:namespaced="showNamespace" :namespaced="showNamespace"
:register-before-hook="registerBeforeHook" :register-before-hook="registerBeforeHook"
:description.sync="description"
> >
<template v-if="isSidecar" #name> <template v-if="isSidecar" #name>
<LabeledInput <LabeledInput

View File

@ -75,11 +75,10 @@ export default {
<div v-if="loading" /> <div v-if="loading" />
<form v-else> <form v-else>
<NameNsDescription <NameNsDescription
v-model="value.metadata" :value="value"
:mode="mode" :mode="mode"
name-label="Stack Name" name-label="Stack Name"
:register-before-hook="registerBeforeHook" :register-before-hook="registerBeforeHook"
:description.sync="description"
/> />
<div class="spacer"></div> <div class="spacer"></div>

View File

@ -173,7 +173,7 @@ export default {
<template> <template>
<form> <form>
<NameNsDescription v-model="value.metadata" :description.sync="description" :mode="mode" :extra-columns="['type']"> <NameNsDescription :value="value" :mode="mode" :extra-columns="['type']">
<template v-slot:type> <template v-slot:type>
<LabeledSelect <LabeledSelect
v-model="value._type" v-model="value._type"

View File

@ -280,7 +280,7 @@ export default {
<template> <template>
<form> <form>
<slot :value="value" name="top"> <slot :value="value" name="top">
<NameNsDescription v-model="value.metadata" :mode="mode" :extra-columns="['type']" :description.sync="description"> <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="workloadTypeOptions" /> <LabeledSelect v-model="type" label="Type" :disabled="isEdit" :options="workloadTypeOptions" />
</template> </template>

View File

@ -1,7 +1,33 @@
import omitBy from 'lodash/omitBy';
import pickBy from 'lodash/pickBy';
import ChildHook, { BEFORE_SAVE_HOOKS, AFTER_SAVE_HOOKS } from './child-hook'; import ChildHook, { BEFORE_SAVE_HOOKS, AFTER_SAVE_HOOKS } from './child-hook';
import { _CREATE, _EDIT, _VIEW } from '@/config/query-params'; import { _CREATE, _EDIT, _VIEW } from '@/config/query-params';
import { LAST_NAMESPACE } from '@/store/prefs'; import { LAST_NAMESPACE } from '@/store/prefs';
import { DESCRIPTION } from '@/config/labels-annotations'; import { DESCRIPTION, LABEL_PREFIX_TO_IGNORE, ANNOTATIONS_TO_IGNORE_CONTAINS, ANNOTATIONS_TO_IGNORE_PREFIX } from '@/config/labels-annotations';
// return true if the string starts with one of the values in prefixes array
const matchesSomePrefix = (string, prefixes) => {
for (const prefix of prefixes) {
const regex = new RegExp(`^${ prefix }`);
if (string.match(regex)) {
return true;
}
}
return false;
};
// return true if string includes at least one of the strings in matchStrings array
const containsSomeString = (string, matchStrings) => {
for (const matchString of matchStrings) {
if (string.includes(matchString)) {
return true;
}
}
return false;
};
export default { export default {
mixins: [ChildHook], mixins: [ChildHook],
@ -49,18 +75,22 @@ export default {
v.metadata = {}; v.metadata = {};
} }
// track description separately from the rest of annotations because it appears separately in UI if ( !v.metadata.annotations ) {
let description; v.metadata.annotations = {};
if ((v.metadata.annotations || {})[DESCRIPTION]) {
description = v.metadata.annotations[DESCRIPTION];
if (this.mode !== 'view') {
// remove description from annotations so it is not displayed/tracked in annotation component as well as NameNsDescription
delete v.metadata.annotations[DESCRIPTION];
}
} }
return { errors: null, description }; if ( !v.metadata.labels ) {
v.metadata.labels = {};
}
// keep label and annotation filters in data so each resource CRUD page can alter individiaully
return {
errors: null,
labelPrefixToIgnore: LABEL_PREFIX_TO_IGNORE,
annotationsToIgnoreContains: ANNOTATIONS_TO_IGNORE_CONTAINS,
annotationsToIgnorePrefix: ANNOTATIONS_TO_IGNORE_PREFIX
};
}, },
computed: { computed: {
@ -82,19 +112,39 @@ export default {
labels: { labels: {
get() { get() {
return this.value?.metadata?.labels || {}; const all = this.value?.metadata?.labels || {};
return omitBy(all, (value, key) => {
return matchesSomePrefix(key, this.labelPrefixToIgnore);
});
}, },
set(neu) { set(neu) {
this.$set(this.value.metadata, 'labels', neu); const all = this.value?.metadata?.labels || {};
const wasIgnored = pickBy(all, (value, key) => {
return matchesSomePrefix(key, this.labelPrefixToIgnore);
});
this.$set(this.value.metadata, 'labels', { ...neu, ...wasIgnored });
} }
}, },
annotations: { annotations: {
get() { get() {
return this.value?.metadata?.annotations || {}; const all = this.value?.metadata?.annotations || {};
return omitBy(all, (value, key) => {
return (matchesSomePrefix(key, this.annotationsToIgnorePrefix) || containsSomeString(key, this.annotationsToIgnoreContains));
});
}, },
set(neu) { set(neu) {
this.$set(this.value.metadata, 'annotations', neu); const all = this.value?.metadata?.annotations || {};
const wasIgnored = pickBy(all, (value, key) => {
return (matchesSomePrefix(key, this.annotationsToIgnorePrefix) || containsSomeString(key, this.annotationsToIgnoreContains));
});
this.$set(this.value.metadata, 'annotations', { ...neu, ...wasIgnored });
} }
} }

View File

@ -102,11 +102,10 @@ export default {
v-if="editAsYaml" v-if="editAsYaml"
> >
<NameNsDescription <NameNsDescription
v-model="localValue.metadata" :value="value"
:mode="mode" :mode="mode"
:namespaced="false" :namespaced="false"
:extra-columns="['template']" :extra-columns="['template']"
:description.sync="description"
> >
<template v-slot:template> <template v-slot:template>
<LabeledSelect <LabeledSelect

View File

@ -185,12 +185,11 @@ export default {
<div v-if="value.save" class="gatekeeper-constraint"> <div v-if="value.save" class="gatekeeper-constraint">
<div> <div>
<NameNsDescription <NameNsDescription
v-model="value.metadata" :value="value"
:mode="mode" :mode="mode"
:namespaced="false" :namespaced="false"
:extra-columns="['template']" :extra-columns="['template']"
:extra-detail-columns="extraDetailColumns" :extra-detail-columns="extraDetailColumns"
:description.sync="description"
> >
<template v-slot:template> <template v-slot:template>
<LabeledSelect <LabeledSelect