Commit f4dc6457 authored by Jose Ivan Vargas's avatar Jose Ivan Vargas

Merge branch '323771-redesign-policy-editor' into 'master'

Redesign policy editor page

See merge request gitlab-org/gitlab!62344
parents 5a4c667a 4ccc158d
......@@ -20,7 +20,6 @@ export default {
editorOptions() {
return {
lineNumbers: 'off',
minimap: { enabled: false },
folding: false,
// Investigate the necessity of `glyphMargin` with #326746
glyphMargin: false,
......
......@@ -149,18 +149,21 @@ export default {
handleAlertUpdate(includeAlert) {
this.policy.annotations = includeAlert ? { 'app.gitlab.com/alert': 'true' } : '';
},
isNotFirstRule(index) {
return index > 0;
},
updateEndpointMatchMode(mode) {
this.policy.endpointMatchMode = mode;
},
updateEndpointLabels(labels) {
this.policy.endpointLabels = labels;
},
updateRuleType(ruleIdx, ruleType) {
const rule = this.policy.rules[ruleIdx];
this.policy.rules.splice(ruleIdx, 1, buildRule(ruleType, rule));
updateRuleType(ruleIndex, ruleType) {
const rule = this.policy.rules[ruleIndex];
this.policy.rules.splice(ruleIndex, 1, buildRule(ruleType, rule));
},
removeRule(ruleIdx) {
this.policy.rules.splice(ruleIdx, 1);
removeRule(ruleIndex) {
this.policy.rules.splice(ruleIndex, 1);
},
loadYaml(manifest) {
this.yamlEditorValue = manifest;
......@@ -223,188 +226,162 @@ export default {
</script>
<template>
<section>
<header class="my-3">
<h2 class="h3 mb-1">
{{ s__('NetworkPolicies|Policy description') }}
</h2>
<section class="policy-editor">
<header class="gl-pb-5">
<h3>{{ s__('NetworkPolicies|Policy description') }}</h3>
</header>
<div class="row">
<div class="col-sm-6 col-md-4 col-lg-3 col-xl-2">
<gl-form-group :label="s__('NetworkPolicies|Policy type')" label-for="policyType">
<gl-form-select
id="policyType"
value="networkPolicy"
:options="$options.policyTypes"
disabled
/>
</gl-form-group>
</div>
<div class="col-sm-6 col-md-6 col-lg-5 col-xl-4">
<gl-form-group :label="s__('NetworkPolicies|Name')" label-for="policyName">
<gl-form-input id="policyName" v-model="policy.name" :disabled="hasParsingError" />
</gl-form-group>
</div>
</div>
<div class="row">
<div class="col-sm-12 col-md-10 col-lg-8 col-xl-6">
<gl-form-group :label="s__('NetworkPolicies|Description')" label-for="policyDescription">
<gl-form-textarea
id="policyDescription"
v-model="policy.description"
:disabled="hasParsingError"
/>
</gl-form-group>
</div>
</div>
<div class="row">
<div class="gl-display-flex">
<gl-form-group :label="s__('NetworkPolicies|Policy type')" label-for="policyType">
<gl-form-select
id="policyType"
value="networkPolicy"
:options="$options.policyTypes"
disabled
/>
</gl-form-group>
<environment-picker />
</div>
<div class="row">
<div class="col-md-auto">
<gl-form-group :disabled="hasParsingError" data-testid="policy-enable">
<gl-toggle v-model="policy.isEnabled" :label="$options.i18n.toggleLabel" />
</gl-form-group>
</div>
</div>
<div class="row">
<div class="col-md-auto">
<gl-form-group :label="s__('NetworkPolicies|Editor mode')" label-for="editorMode">
<gl-segmented-control
data-testid="editor-mode"
:options="$options.editorModes"
:checked="editorMode"
@input="changeEditorMode"
/>
</gl-form-group>
</div>
</div>
<hr />
<div v-if="shouldShowRuleEditor" class="row" data-testid="rule-editor">
<div class="col-sm-12 col-md-6 col-lg-7 col-xl-8">
<gl-alert
v-if="hasParsingError"
class="gl-z-index-1"
data-testid="parsing-alert"
:dismissible="false"
>{{ $options.i18n.PARSING_ERROR_MESSAGE }}</gl-alert
>
<dim-disable-container data-testid="rule-builder-container" :disabled="hasParsingError">
<template #title>
<h4>{{ s__('NetworkPolicies|Rules') }}</h4>
</template>
<div class="gl-mb-5 gl-border-1 gl-border-solid gl-border-gray-100 gl-rounded-base">
<gl-form-group
class="gl-px-5 gl-py-3 gl-mb-0 gl-bg-gray-10 gl-border-b-solid gl-border-b-gray-100 gl-border-b-1"
>
<gl-segmented-control
data-testid="editor-mode"
:options="$options.editorModes"
:checked="editorMode"
@input="changeEditorMode"
/>
</gl-form-group>
<div class="gl-display-flex gl-sm-flex-direction-column">
<section class="gl-w-full gl-p-5 gl-flex-fill-4 policy-table-left">
<div v-if="shouldShowRuleEditor" data-testid="rule-editor">
<gl-alert v-if="hasParsingError" data-testid="parsing-alert" :dismissible="false">
{{ $options.i18n.PARSING_ERROR_MESSAGE }}
</gl-alert>
<template #disabled>
<div
class="gl-bg-gray-10 gl-border-solid gl-border-1 gl-border-gray-100 gl-rounded-base gl-p-6"
></div>
</template>
<gl-form-group :label="s__('NetworkPolicies|Name')" label-for="policyName">
<gl-form-input id="policyName" v-model="policy.name" :disabled="hasParsingError" />
</gl-form-group>
<policy-rule-builder
v-for="(rule, idx) in policy.rules"
:key="idx"
class="gl-mb-4"
:rule="rule"
:endpoint-match-mode="policy.endpointMatchMode"
:endpoint-labels="policy.endpointLabels"
:endpoint-selector-disabled="idx > 0"
@rule-type-change="updateRuleType(idx, $event)"
@endpoint-match-mode-change="updateEndpointMatchMode"
@endpoint-labels-change="updateEndpointLabels"
@remove="removeRule(idx)"
/>
<gl-form-group
:label="s__('NetworkPolicies|Description')"
label-for="policyDescription"
>
<gl-form-textarea
id="policyDescription"
v-model="policy.description"
:disabled="hasParsingError"
/>
</gl-form-group>
<div
class="gl-p-3 gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100 gl-mb-5"
>
<gl-button variant="link" category="primary" data-testid="add-rule" @click="addRule">{{
s__('Network Policy|New rule')
}}</gl-button>
</div>
</dim-disable-container>
<gl-form-group :disabled="hasParsingError" data-testid="policy-enable">
<gl-toggle v-model="policy.isEnabled" :label="$options.i18n.toggleLabel" />
</gl-form-group>
<dim-disable-container data-testid="policy-action-container" :disabled="hasParsingError">
<template #title>
<h4>{{ s__('NetworkPolicies|Actions') }}</h4>
<p>
{{ s__('NetworkPolicies|Traffic that does not match any rule will be blocked.') }}
</p>
</template>
<dim-disable-container data-testid="rule-builder-container" :disabled="hasParsingError">
<template #title>
<h4>{{ s__('NetworkPolicies|Rules') }}</h4>
</template>
<template #disabled>
<div
class="gl-bg-gray-10 gl-border-solid gl-border-1 gl-border-gray-100 gl-rounded-base gl-p-6"
></div>
</template>
<template #disabled>
<div
class="gl-bg-gray-10 gl-border-solid gl-border-1 gl-border-gray-100 gl-rounded-base gl-p-6"
></div>
</template>
<policy-action-picker />
<policy-alert-picker :policy-alert="policyAlert" @update-alert="handleAlertUpdate" />
</dim-disable-container>
</div>
<div class="col-sm-12 col-md-6 col-lg-5 col-xl-4">
<dim-disable-container data-testid="policy-preview-container" :disabled="hasParsingError">
<template #title>
<h5>{{ s__('NetworkPolicies|Policy preview') }}</h5>
</template>
<policy-rule-builder
v-for="(rule, index) in policy.rules"
:key="index"
class="gl-mb-4"
:rule="rule"
:endpoint-match-mode="policy.endpointMatchMode"
:endpoint-labels="policy.endpointLabels"
:endpoint-selector-disabled="isNotFirstRule(index)"
@rule-type-change="updateRuleType(index, $event)"
@endpoint-match-mode-change="updateEndpointMatchMode"
@endpoint-labels-change="updateEndpointLabels"
@remove="removeRule(index)"
/>
<template #disabled>
<policy-preview
:policy-yaml="s__('NetworkPolicies|Unable to parse policy')"
policy-description=""
/>
</template>
<div
class="gl-p-3 gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100 gl-mb-5"
>
<gl-button variant="link" data-testid="add-rule" @click="addRule">{{
s__('Network Policy|New rule')
}}</gl-button>
</div>
</dim-disable-container>
<policy-preview :policy-yaml="policyYaml" :policy-description="humanizedPolicy" />
</dim-disable-container>
</div>
</div>
<div v-if="shouldShowYamlEditor" class="row" data-testid="yaml-editor">
<div class="col-sm-12 col-md-12 col-lg-10 col-xl-8">
<div class="gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100">
<h5
class="gl-m-0 gl-p-4 gl-bg-gray-10 gl-border-1 gl-border-b-solid gl-border-b-gray-100"
>
{{ s__('NetworkPolicies|YAML editor') }}
</h5>
<div class="gl-p-4">
<network-policy-editor
data-testid="network-policy-editor"
:value="yamlEditorValue"
:read-only="false"
@input="loadYaml"
/>
<dim-disable-container
data-testid="policy-action-container"
:disabled="hasParsingError"
>
<template #title>
<h4>{{ s__('NetworkPolicies|Actions') }}</h4>
<p>
{{ s__('NetworkPolicies|Traffic that does not match any rule will be blocked.') }}
</p>
</template>
<template #disabled>
<div
class="gl-bg-gray-10 gl-border-solid gl-border-1 gl-border-gray-100 gl-rounded-base gl-p-6"
></div>
</template>
<policy-action-picker />
<policy-alert-picker :policy-alert="policyAlert" @update-alert="handleAlertUpdate" />
</dim-disable-container>
</div>
</div>
</div>
</div>
<hr />
<div class="row">
<div class="col-md-auto">
<gl-button
type="submit"
category="primary"
variant="success"
data-testid="save-policy"
:loading="isUpdatingPolicy"
@click="savePolicy"
>{{ saveButtonText }}</gl-button
>
<gl-button
v-if="isEditing"
v-gl-modal="'delete-modal'"
category="secondary"
variant="danger"
data-testid="delete-policy"
:loading="isRemovingPolicy"
>{{ s__('NetworkPolicies|Delete policy') }}</gl-button
<network-policy-editor
v-if="shouldShowYamlEditor"
data-testid="network-policy-editor"
:value="yamlEditorValue"
:read-only="false"
@input="loadYaml"
/>
</section>
<section
v-if="shouldShowRuleEditor"
class="gl-w-30p gl-p-5 gl-border-l-gray-100 gl-border-l-1 gl-border-l-solid gl-flex-fill-2"
>
<gl-button category="secondary" variant="default" :href="threatMonitoringPath">{{
__('Cancel')
}}</gl-button>
<dim-disable-container data-testid="policy-preview-container" :disabled="hasParsingError">
<template #title>
<h5>{{ s__('NetworkPolicies|Policy preview') }}</h5>
</template>
<template #disabled>
<policy-preview :policy-yaml="s__('NetworkPolicies|Unable to parse policy')" />
</template>
<policy-preview :policy-yaml="policyYaml" :policy-description="humanizedPolicy" />
</dim-disable-container>
</section>
</div>
</div>
<div>
<gl-button
type="submit"
variant="success"
data-testid="save-policy"
:loading="isUpdatingPolicy"
@click="savePolicy"
>{{ saveButtonText }}</gl-button
>
<gl-button
v-if="isEditing"
v-gl-modal="'delete-modal'"
category="secondary"
variant="danger"
data-testid="delete-policy"
:loading="isRemovingPolicy"
>{{ s__('NetworkPolicies|Delete policy') }}</gl-button
>
<gl-button category="secondary" :href="threatMonitoringPath">{{ __('Cancel') }}</gl-button>
</div>
<gl-modal
modal-id="delete-modal"
:title="deleteModalTitle"
......
......@@ -9,3 +9,7 @@
.threat-monitoring-alert-drawer .gl-drawer-header {
align-items: flex-start;
}
.policy-editor .policy-table-left {
flex: 4;
}
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PolicyEditorApp component given .yaml editor mode is enabled renders yaml editor 1`] = `
<div
class="row"
data-testid="yaml-editor"
>
<div
class="col-sm-12 col-md-12 col-lg-10 col-xl-8"
>
<div
class="gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100"
>
<h5
class="gl-m-0 gl-p-4 gl-bg-gray-10 gl-border-1 gl-border-b-solid gl-border-b-gray-100"
>
YAML editor
</h5>
<div
class="gl-p-4"
>
<networkpolicyeditor-stub
data-testid="network-policy-editor"
value=""
/>
</div>
</div>
</div>
</div>
`;
exports[`PolicyEditorApp component renders the policy editor layout 1`] = `
<section>
<header
class="my-3"
>
<h2
class="h3 mb-1"
>
Policy description
</h2>
</header>
<div
class="row"
>
<div
class="col-sm-6 col-md-4 col-lg-3 col-xl-2"
>
<gl-form-group-stub
label="Policy type"
label-for="policyType"
>
<gl-form-select-stub
disabled=""
id="policyType"
options="[object Object]"
value="networkPolicy"
/>
</gl-form-group-stub>
</div>
<div
class="col-sm-6 col-md-6 col-lg-5 col-xl-4"
>
<gl-form-group-stub
label="Name"
label-for="policyName"
>
<gl-form-input-stub
id="policyName"
value=""
/>
</gl-form-group-stub>
</div>
</div>
<div
class="row"
>
<div
class="col-sm-12 col-md-10 col-lg-8 col-xl-6"
>
<gl-form-group-stub
label="Description"
label-for="policyDescription"
>
<gl-form-textarea-stub
id="policyDescription"
noresize="true"
value=""
/>
</gl-form-group-stub>
</div>
</div>
<div
class="row"
>
<environment-picker-stub />
</div>
<div
class="row"
>
<div
class="col-md-auto"
>
<gl-form-group-stub
data-testid="policy-enable"
>
<gl-toggle-stub
label="Policy status"
labelposition="top"
/>
</gl-form-group-stub>
</div>
</div>
<div
class="row"
>
<div
class="col-md-auto"
>
<gl-form-group-stub
label="Editor mode"
label-for="editorMode"
>
<gl-segmented-control-stub
checked="rule"
data-testid="editor-mode"
options="[object Object],[object Object]"
/>
</gl-form-group-stub>
</div>
</div>
<hr />
<div
class="row"
data-testid="rule-editor"
>
<div
class="col-sm-12 col-md-6 col-lg-7 col-xl-8"
>
<!---->
<dim-disable-container-stub
data-testid="rule-builder-container"
>
<policy-rule-builder-stub
class="gl-mb-4"
endpointlabels=""
endpointmatchmode="any"
rule="[object Object]"
/>
<div
class="gl-p-3 gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100 gl-mb-5"
>
<gl-button-stub
buttontextclasses=""
category="primary"
data-testid="add-rule"
icon=""
size="medium"
variant="link"
>
New rule
</gl-button-stub>
</div>
</dim-disable-container-stub>
<dim-disable-container-stub
data-testid="policy-action-container"
>
<policy-action-picker-stub />
<policy-alert-picker-stub />
</dim-disable-container-stub>
</div>
<div
class="col-sm-12 col-md-6 col-lg-5 col-xl-4"
>
<dim-disable-container-stub
data-testid="policy-preview-container"
>
<policy-preview-stub
initialtab="0"
policydescription="Allow all inbound traffic to <strong>all</strong> pods from <strong>all</strong> pods on <strong>any</strong> port"
policyyaml="apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: ''
labels:
app.gitlab.com/proj: '21'
spec:
endpointSelector:
matchLabels:
network-policy.gitlab.com/disabled_by: gitlab
ingress:
- fromEndpoints:
- matchLabels: {}
"
/>
</dim-disable-container-stub>
</div>
</div>
<!---->
<hr />
<div
class="row"
>
<div
class="col-md-auto"
>
<gl-button-stub
buttontextclasses=""
category="primary"
data-testid="save-policy"
icon=""
size="medium"
type="submit"
variant="success"
>
Create policy
</gl-button-stub>
<!---->
<gl-button-stub
buttontextclasses=""
category="secondary"
href="/threat-monitoring"
icon=""
size="medium"
variant="default"
>
Cancel
</gl-button-stub>
</div>
</div>
<gl-modal-stub
actioncancel="[object Object]"
actionsecondary="[object Object]"
dismisslabel="Close"
modalclass=""
modalid="delete-modal"
size="md"
title="Delete policy: "
titletag="h4"
>
Are you sure you want to delete this policy? This action cannot be undone.
</gl-modal-stub>
</section>
`;
......@@ -72,12 +72,11 @@ spec:
};
const findRuleEditor = () => wrapper.findByTestId('rule-editor');
const findYamlEditor = () => wrapper.findByTestId('yaml-editor');
const findPreview = () => wrapper.find(PolicyPreview);
const findPreview = () => wrapper.findComponent(PolicyPreview);
const findAddRuleButton = () => wrapper.findByTestId('add-rule');
const findYAMLParsingAlert = () => wrapper.findByTestId('parsing-alert');
const findNetworkPolicyEditor = () => wrapper.findByTestId('network-policy-editor');
const findPolicyAlertPicker = () => wrapper.find(PolicyAlertPicker);
const findPolicyAlertPicker = () => wrapper.findComponent(PolicyAlertPicker);
const findPolicyDescription = () => wrapper.find("[id='policyDescription']");
const findPolicyEnableContainer = () => wrapper.findByTestId('policy-enable');
const findPolicyName = () => wrapper.find("[id='policyName']");
......@@ -104,12 +103,10 @@ spec:
wrapper = null;
});
it('renders the policy editor layout', () => {
expect(wrapper.find('section').element).toMatchSnapshot();
});
it('renders toggle with label', () => {
expect(wrapper.findComponent(GlToggle).props('label')).toBe(PolicyEditorApp.i18n.toggleLabel);
const policyEnableToggle = findPolicyEnableContainer().findComponent(GlToggle);
expect(policyEnableToggle.exists()).toBe(true);
expect(policyEnableToggle.props('label')).toBe(PolicyEditorApp.i18n.toggleLabel);
});
it('renders a default rule with label', () => {
......@@ -120,20 +117,19 @@ spec:
});
});
it('renders the policy alert picker', () => {
expect(findPolicyAlertPicker().exists()).toBe(true);
});
it('does not render yaml editor', () => {
expect(findYamlEditor().exists()).toBe(false);
});
it('does not render parsing error alert', () => {
expect(findYAMLParsingAlert().exists()).toBe(false);
});
it('does not render delete button', () => {
expect(findDeletePolicy().exists()).toBe(false);
it.each`
component | status | findComponent | state
${'policy alert picker'} | ${'does display'} | ${findPolicyAlertPicker} | ${true}
${'editor mode toggle'} | ${'does display'} | ${findEditorModeToggle} | ${true}
${'policy name input'} | ${'does display'} | ${findPolicyName} | ${true}
${'rule editor'} | ${'does display'} | ${findRuleEditor} | ${true}
${'add rule button'} | ${'does display'} | ${findAddRuleButton} | ${true}
${'policy preview'} | ${'does display'} | ${findPreview} | ${true}
${'yaml editor'} | ${'does not display'} | ${findNetworkPolicyEditor} | ${false}
${'parsing error alert'} | ${'does not display'} | ${findYAMLParsingAlert} | ${false}
${'delete button'} | ${'does not display'} | ${findDeletePolicy} | ${false}
`('$status the $component', async ({ findComponent, state }) => {
expect(findComponent().exists()).toBe(state);
});
describe('given .yaml editor mode is enabled', () => {
......@@ -145,14 +141,13 @@ spec:
});
});
it('does not render rule editor', () => {
expect(findRuleEditor().exists()).toBe(false);
});
it('renders yaml editor', () => {
const editor = findYamlEditor();
expect(editor.exists()).toBe(true);
expect(editor.element).toMatchSnapshot();
it.each`
component | status | findComponent | state
${'editor mode toggle'} | ${'does display'} | ${findEditorModeToggle} | ${true}
${'rule editor'} | ${'does not display'} | ${findRuleEditor} | ${false}
${'yaml editor'} | ${'does display'} | ${findNetworkPolicyEditor} | ${true}
`('$status the $component', async ({ findComponent, state }) => {
expect(findComponent().exists()).toBe(state);
});
it('updates policy on yaml editor value change', async () => {
......@@ -418,7 +413,7 @@ spec:
});
it('removes policy and redirects to a threat monitoring path on secondary modal button click', async () => {
wrapper.find(GlModal).vm.$emit('secondary');
wrapper.findComponent(GlModal).vm.$emit('secondary');
await wrapper.vm.$nextTick();
expect(store.dispatch).toHaveBeenCalledWith('networkPolicies/deletePolicy', {
......
......@@ -21803,9 +21803,6 @@ msgstr ""
msgid "NetworkPolicies|Edit policy"
msgstr ""
msgid "NetworkPolicies|Editor mode"
msgstr ""
msgid "NetworkPolicies|Enforcement status"
msgstr ""
......@@ -21905,9 +21902,6 @@ msgstr ""
msgid "NetworkPolicies|Unable to parse policy"
msgstr ""
msgid "NetworkPolicies|YAML editor"
msgstr ""
msgid "NetworkPolicies|all DNS names"
msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment