mirror of https://github.com/rancher/dashboard.git
300 lines
8.7 KiB
Vue
300 lines
8.7 KiB
Vue
<script>
|
|
import throttle from 'lodash/throttle';
|
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
|
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
|
import Tab from '@shell/components/Tabbed/Tab';
|
|
import Tabbed from '@shell/components/Tabbed';
|
|
import CruResource from '@shell/components/CruResource';
|
|
import { Banner } from '@components/Banner';
|
|
import Labels from '@shell/components/form/Labels';
|
|
import { NAMESPACE, POD } from '@shell/config/types';
|
|
import { convert, matching, simplify } from '@shell/utils/selector';
|
|
import { Checkbox } from '@components/Form/Checkbox';
|
|
import { addObject, removeObject } from '@shell/utils/array';
|
|
import MatchExpressions from '@shell/components/form/MatchExpressions';
|
|
import PolicyRules from '@shell/edit/networking.k8s.io.networkpolicy/PolicyRules';
|
|
import ResourceTable from '@shell/components/ResourceTable';
|
|
import { allHash } from '@shell/utils/promise';
|
|
|
|
const POLICY_TYPES = {
|
|
INGRESS: 'Ingress',
|
|
EGRESS: 'Egress',
|
|
};
|
|
|
|
export default {
|
|
emits: ['input'],
|
|
// Props are found in CreateEditView
|
|
// props: {},
|
|
inheritAttrs: false,
|
|
components: {
|
|
Banner,
|
|
Checkbox,
|
|
CruResource,
|
|
Labels,
|
|
MatchExpressions,
|
|
NameNsDescription,
|
|
PolicyRules,
|
|
ResourceTable,
|
|
Tab,
|
|
Tabbed,
|
|
},
|
|
|
|
mixins: [CreateEditView],
|
|
|
|
async fetch() {
|
|
const hash = await allHash({
|
|
allPods: this.$store.dispatch('cluster/findAll', { type: POD }), // Used in conjunction with `matches/match/label selectors`. Requires https://github.com/rancher/dashboard/issues/10417 to fix
|
|
allNamespaces: this.$store.dispatch('cluster/findAll', { type: NAMESPACE }), // Used in conjunction with `matches/match/label selectors`. Requires https://github.com/rancher/dashboard/issues/10417 to fix
|
|
});
|
|
|
|
this.allPods = hash.allPods; // Used in matchingPods, and PolicyRules --> PolicyRule --> PolicyRuleTarget
|
|
this.allNamespaces = hash.allNamespaces; // Used in PolicyRules --> PolicyRule --> PolicyRuleTarget
|
|
|
|
this.updateMatchingPods();
|
|
},
|
|
|
|
data() {
|
|
if ( !this.value.spec ) {
|
|
this.value['spec'] = {
|
|
policyTypes: [],
|
|
podSelector: {
|
|
matchExpressions: [],
|
|
matchLabels: {},
|
|
}
|
|
};
|
|
}
|
|
|
|
const matchingPods = {
|
|
matched: 0,
|
|
matches: [],
|
|
none: true,
|
|
sample: null,
|
|
total: 0,
|
|
};
|
|
|
|
return {
|
|
POD,
|
|
matchingPods,
|
|
allPods: [],
|
|
allNamespaces: [],
|
|
podTableHeaders: this.$store.getters['type-map/headersFor'](
|
|
this.$store.getters['cluster/schemaFor'](POD)
|
|
),
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
podSchema() {
|
|
return this.$store.getters['cluster/schemaFor'](POD);
|
|
},
|
|
hasIngressPolicies: {
|
|
get() {
|
|
return this.value.spec.policyTypes.includes(POLICY_TYPES.INGRESS);
|
|
},
|
|
set(hasIngressPolicies) {
|
|
let policyTypes = this.value.spec.policyTypes;
|
|
|
|
if (hasIngressPolicies) {
|
|
addObject(policyTypes, POLICY_TYPES.INGRESS);
|
|
if (!this.value.spec.ingress) {
|
|
this.value.spec['ingress'] = [];
|
|
}
|
|
} else {
|
|
policyTypes = removeObject(policyTypes, POLICY_TYPES.INGRESS);
|
|
delete this.value.spec['ingress'];
|
|
}
|
|
|
|
this.value.spec['policyTypes'] = policyTypes;
|
|
}
|
|
},
|
|
hasEgressPolicies: {
|
|
get() {
|
|
return this.value.spec.policyTypes.includes(POLICY_TYPES.EGRESS);
|
|
},
|
|
set(hasEgressPolicies) {
|
|
let policyTypes = this.value.spec.policyTypes;
|
|
|
|
if (hasEgressPolicies) {
|
|
addObject(policyTypes, POLICY_TYPES.EGRESS);
|
|
if (!this.value.spec.egress) {
|
|
this.value.spec['egress'] = [];
|
|
}
|
|
} else {
|
|
policyTypes = removeObject(policyTypes, POLICY_TYPES.EGRESS);
|
|
delete this.value.spec['egress'];
|
|
}
|
|
|
|
this.value.spec['policyTypes'] = policyTypes;
|
|
}
|
|
},
|
|
podSelectorExpressions: {
|
|
get() {
|
|
return convert(
|
|
this.value.spec.podSelector.matchLabels || {},
|
|
this.value.spec.podSelector.matchExpressions || []
|
|
);
|
|
},
|
|
set(podSelectorExpressions) {
|
|
this.value.spec['podSelector'] = simplify(podSelectorExpressions);
|
|
}
|
|
},
|
|
},
|
|
|
|
watch: {
|
|
'value.metadata.namespace': 'updateMatchingPods',
|
|
'value.spec.podSelector': 'updateMatchingPods',
|
|
},
|
|
|
|
methods: {
|
|
updateMatchingPods: throttle(function() {
|
|
const allInNamespace = this.allPods.filter((pod) => pod.metadata.namespace === this.value.metadata.namespace);
|
|
const match = matching(allInNamespace, this.podSelectorExpressions);
|
|
const matched = match.length || 0;
|
|
const sample = match[0]?.nameDisplay;
|
|
|
|
this.matchingPods = {
|
|
matched,
|
|
matches: match,
|
|
none: matched === 0,
|
|
sample,
|
|
total: allInNamespace.length,
|
|
};
|
|
}, 250, { leading: true }),
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<CruResource
|
|
:done-route="doneRoute"
|
|
:mode="mode"
|
|
:resource="value"
|
|
:subtypes="[]"
|
|
:validation-passed="true"
|
|
:errors="errors"
|
|
@error="(e) => (errors = e)"
|
|
@finish="save"
|
|
@cancel="done"
|
|
>
|
|
<NameNsDescription
|
|
v-if="!isView"
|
|
:value="value"
|
|
:mode="mode"
|
|
/>
|
|
|
|
<div class="row mb-40">
|
|
<div class="col span-12">
|
|
<Tabbed :side-tabs="true">
|
|
<Tab
|
|
name="ingress"
|
|
label-key="networkpolicy.ingress.label"
|
|
:show-header="false"
|
|
:weight="3"
|
|
>
|
|
<h2 class="">
|
|
{{ t('networkpolicy.ingress.label') }}
|
|
</h2>
|
|
<Checkbox
|
|
v-model:value="hasIngressPolicies"
|
|
class="mt-20 mb-10"
|
|
:mode="mode"
|
|
:label="t('networkpolicy.ingress.enable')"
|
|
data-testid="network-policy-ingress-enable-checkbox"
|
|
/>
|
|
<PolicyRules
|
|
v-if="hasIngressPolicies"
|
|
:value="value"
|
|
type="ingress"
|
|
:mode="mode"
|
|
:all-namespaces="allNamespaces"
|
|
:all-pods="allPods"
|
|
@update:value="$emit('input', $event)"
|
|
/>
|
|
</Tab>
|
|
<Tab
|
|
name="egress"
|
|
label-key="networkpolicy.egress.label"
|
|
:show-header="false"
|
|
:weight="2"
|
|
>
|
|
<h2>
|
|
{{ t('networkpolicy.egress.label') }}
|
|
</h2>
|
|
<Checkbox
|
|
v-model:value="hasEgressPolicies"
|
|
class="mt-20 mb-10"
|
|
:mode="mode"
|
|
:label="t('networkpolicy.egress.enable')"
|
|
/>
|
|
<PolicyRules
|
|
v-if="hasEgressPolicies"
|
|
:value="value"
|
|
type="egress"
|
|
:mode="mode"
|
|
:all-namespaces="allNamespaces"
|
|
:all-pods="allPods"
|
|
@update:value="$emit('input', $event)"
|
|
/>
|
|
</Tab>
|
|
<Tab
|
|
name="selectors"
|
|
label-key="networkpolicy.selectors.label"
|
|
:show-header="false"
|
|
:weight="1"
|
|
>
|
|
<h2>
|
|
{{ t('networkpolicy.selectors.label') }}
|
|
<i
|
|
v-clean-tooltip="t('networkpolicy.selectors.hint')"
|
|
class="icon icon-info"
|
|
/>
|
|
</h2>
|
|
<div class="row">
|
|
<div class="col span-12">
|
|
<MatchExpressions
|
|
v-model:value="podSelectorExpressions"
|
|
:mode="mode"
|
|
:show-remove="false"
|
|
:type="POD"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col span-12">
|
|
<Banner color="success">
|
|
<span v-clean-html="t('networkpolicy.selectors.matchingPods.matchesSome', matchingPods)" />
|
|
</Banner>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col span-12">
|
|
<ResourceTable
|
|
:rows="matchingPods.matches"
|
|
:headers="podTableHeaders"
|
|
key-field="id"
|
|
:table-actions="false"
|
|
:schema="podSchema"
|
|
:groupable="false"
|
|
:search="false"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</Tab>
|
|
<Tab
|
|
name="labels-and-annotations"
|
|
:label="t('networkpolicy.labelsAnnotations.label', {}, true)"
|
|
:weight="-1"
|
|
>
|
|
<Labels
|
|
:default-container-class="'labels-and-annotations-container'"
|
|
:value="value"
|
|
:mode="mode"
|
|
:display-side-by-side="false"
|
|
/>
|
|
</Tab>
|
|
</Tabbed>
|
|
</div>
|
|
</div>
|
|
</CruResource>
|
|
</template>
|