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 { ...@@ -20,7 +20,6 @@ export default {
editorOptions() { editorOptions() {
return { return {
lineNumbers: 'off', lineNumbers: 'off',
minimap: { enabled: false },
folding: false, folding: false,
// Investigate the necessity of `glyphMargin` with #326746 // Investigate the necessity of `glyphMargin` with #326746
glyphMargin: false, glyphMargin: false,
......
...@@ -149,18 +149,21 @@ export default { ...@@ -149,18 +149,21 @@ export default {
handleAlertUpdate(includeAlert) { handleAlertUpdate(includeAlert) {
this.policy.annotations = includeAlert ? { 'app.gitlab.com/alert': 'true' } : ''; this.policy.annotations = includeAlert ? { 'app.gitlab.com/alert': 'true' } : '';
}, },
isNotFirstRule(index) {
return index > 0;
},
updateEndpointMatchMode(mode) { updateEndpointMatchMode(mode) {
this.policy.endpointMatchMode = mode; this.policy.endpointMatchMode = mode;
}, },
updateEndpointLabels(labels) { updateEndpointLabels(labels) {
this.policy.endpointLabels = labels; this.policy.endpointLabels = labels;
}, },
updateRuleType(ruleIdx, ruleType) { updateRuleType(ruleIndex, ruleType) {
const rule = this.policy.rules[ruleIdx]; const rule = this.policy.rules[ruleIndex];
this.policy.rules.splice(ruleIdx, 1, buildRule(ruleType, rule)); this.policy.rules.splice(ruleIndex, 1, buildRule(ruleType, rule));
}, },
removeRule(ruleIdx) { removeRule(ruleIndex) {
this.policy.rules.splice(ruleIdx, 1); this.policy.rules.splice(ruleIndex, 1);
}, },
loadYaml(manifest) { loadYaml(manifest) {
this.yamlEditorValue = manifest; this.yamlEditorValue = manifest;
...@@ -223,188 +226,162 @@ export default { ...@@ -223,188 +226,162 @@ export default {
</script> </script>
<template> <template>
<section> <section class="policy-editor">
<header class="my-3"> <header class="gl-pb-5">
<h2 class="h3 mb-1"> <h3>{{ s__('NetworkPolicies|Policy description') }}</h3>
{{ s__('NetworkPolicies|Policy description') }}
</h2>
</header> </header>
<div class="gl-display-flex">
<div class="row"> <gl-form-group :label="s__('NetworkPolicies|Policy type')" label-for="policyType">
<div class="col-sm-6 col-md-4 col-lg-3 col-xl-2"> <gl-form-select
<gl-form-group :label="s__('NetworkPolicies|Policy type')" label-for="policyType"> id="policyType"
<gl-form-select value="networkPolicy"
id="policyType" :options="$options.policyTypes"
value="networkPolicy" disabled
:options="$options.policyTypes" />
disabled </gl-form-group>
/>
</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">
<environment-picker /> <environment-picker />
</div> </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"> <div class="gl-mb-5 gl-border-1 gl-border-solid gl-border-gray-100 gl-rounded-base">
<template #title> <gl-form-group
<h4>{{ s__('NetworkPolicies|Rules') }}</h4> 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"
</template> >
<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> <gl-form-group :label="s__('NetworkPolicies|Name')" label-for="policyName">
<div <gl-form-input id="policyName" v-model="policy.name" :disabled="hasParsingError" />
class="gl-bg-gray-10 gl-border-solid gl-border-1 gl-border-gray-100 gl-rounded-base gl-p-6" </gl-form-group>
></div>
</template>
<policy-rule-builder <gl-form-group
v-for="(rule, idx) in policy.rules" :label="s__('NetworkPolicies|Description')"
:key="idx" label-for="policyDescription"
class="gl-mb-4" >
:rule="rule" <gl-form-textarea
:endpoint-match-mode="policy.endpointMatchMode" id="policyDescription"
:endpoint-labels="policy.endpointLabels" v-model="policy.description"
:endpoint-selector-disabled="idx > 0" :disabled="hasParsingError"
@rule-type-change="updateRuleType(idx, $event)" />
@endpoint-match-mode-change="updateEndpointMatchMode" </gl-form-group>
@endpoint-labels-change="updateEndpointLabels"
@remove="removeRule(idx)"
/>
<div <gl-form-group :disabled="hasParsingError" data-testid="policy-enable">
class="gl-p-3 gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100 gl-mb-5" <gl-toggle v-model="policy.isEnabled" :label="$options.i18n.toggleLabel" />
> </gl-form-group>
<gl-button variant="link" category="primary" data-testid="add-rule" @click="addRule">{{
s__('Network Policy|New rule')
}}</gl-button>
</div>
</dim-disable-container>
<dim-disable-container data-testid="policy-action-container" :disabled="hasParsingError"> <dim-disable-container data-testid="rule-builder-container" :disabled="hasParsingError">
<template #title> <template #title>
<h4>{{ s__('NetworkPolicies|Actions') }}</h4> <h4>{{ s__('NetworkPolicies|Rules') }}</h4>
<p> </template>
{{ s__('NetworkPolicies|Traffic that does not match any rule will be blocked.') }}
</p>
</template>
<template #disabled> <template #disabled>
<div <div
class="gl-bg-gray-10 gl-border-solid gl-border-1 gl-border-gray-100 gl-rounded-base gl-p-6" class="gl-bg-gray-10 gl-border-solid gl-border-1 gl-border-gray-100 gl-rounded-base gl-p-6"
></div> ></div>
</template> </template>
<policy-action-picker /> <policy-rule-builder
<policy-alert-picker :policy-alert="policyAlert" @update-alert="handleAlertUpdate" /> v-for="(rule, index) in policy.rules"
</dim-disable-container> :key="index"
</div> class="gl-mb-4"
<div class="col-sm-12 col-md-6 col-lg-5 col-xl-4"> :rule="rule"
<dim-disable-container data-testid="policy-preview-container" :disabled="hasParsingError"> :endpoint-match-mode="policy.endpointMatchMode"
<template #title> :endpoint-labels="policy.endpointLabels"
<h5>{{ s__('NetworkPolicies|Policy preview') }}</h5> :endpoint-selector-disabled="isNotFirstRule(index)"
</template> @rule-type-change="updateRuleType(index, $event)"
@endpoint-match-mode-change="updateEndpointMatchMode"
@endpoint-labels-change="updateEndpointLabels"
@remove="removeRule(index)"
/>
<template #disabled> <div
<policy-preview class="gl-p-3 gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100 gl-mb-5"
:policy-yaml="s__('NetworkPolicies|Unable to parse policy')" >
policy-description="" <gl-button variant="link" data-testid="add-rule" @click="addRule">{{
/> s__('Network Policy|New rule')
</template> }}</gl-button>
</div>
</dim-disable-container>
<policy-preview :policy-yaml="policyYaml" :policy-description="humanizedPolicy" /> <dim-disable-container
</dim-disable-container> data-testid="policy-action-container"
</div> :disabled="hasParsingError"
</div> >
<div v-if="shouldShowYamlEditor" class="row" data-testid="yaml-editor"> <template #title>
<div class="col-sm-12 col-md-12 col-lg-10 col-xl-8"> <h4>{{ s__('NetworkPolicies|Actions') }}</h4>
<div class="gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100"> <p>
<h5 {{ s__('NetworkPolicies|Traffic that does not match any rule will be blocked.') }}
class="gl-m-0 gl-p-4 gl-bg-gray-10 gl-border-1 gl-border-b-solid gl-border-b-gray-100" </p>
> </template>
{{ s__('NetworkPolicies|YAML editor') }}
</h5> <template #disabled>
<div class="gl-p-4"> <div
<network-policy-editor class="gl-bg-gray-10 gl-border-solid gl-border-1 gl-border-gray-100 gl-rounded-base gl-p-6"
data-testid="network-policy-editor" ></div>
:value="yamlEditorValue" </template>
:read-only="false"
@input="loadYaml" <policy-action-picker />
/> <policy-alert-picker :policy-alert="policyAlert" @update-alert="handleAlertUpdate" />
</dim-disable-container>
</div> </div>
</div> <network-policy-editor
</div> v-if="shouldShowYamlEditor"
</div> data-testid="network-policy-editor"
<hr /> :value="yamlEditorValue"
<div class="row"> :read-only="false"
<div class="col-md-auto"> @input="loadYaml"
<gl-button />
type="submit" </section>
category="primary"
variant="success" <section
data-testid="save-policy" v-if="shouldShowRuleEditor"
:loading="isUpdatingPolicy" class="gl-w-30p gl-p-5 gl-border-l-gray-100 gl-border-l-1 gl-border-l-solid gl-flex-fill-2"
@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" variant="default" :href="threatMonitoringPath">{{ <dim-disable-container data-testid="policy-preview-container" :disabled="hasParsingError">
__('Cancel') <template #title>
}}</gl-button> <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> </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 <gl-modal
modal-id="delete-modal" modal-id="delete-modal"
:title="deleteModalTitle" :title="deleteModalTitle"
......
...@@ -9,3 +9,7 @@ ...@@ -9,3 +9,7 @@
.threat-monitoring-alert-drawer .gl-drawer-header { .threat-monitoring-alert-drawer .gl-drawer-header {
align-items: flex-start; 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: ...@@ -72,12 +72,11 @@ spec:
}; };
const findRuleEditor = () => wrapper.findByTestId('rule-editor'); const findRuleEditor = () => wrapper.findByTestId('rule-editor');
const findYamlEditor = () => wrapper.findByTestId('yaml-editor'); const findPreview = () => wrapper.findComponent(PolicyPreview);
const findPreview = () => wrapper.find(PolicyPreview);
const findAddRuleButton = () => wrapper.findByTestId('add-rule'); const findAddRuleButton = () => wrapper.findByTestId('add-rule');
const findYAMLParsingAlert = () => wrapper.findByTestId('parsing-alert'); const findYAMLParsingAlert = () => wrapper.findByTestId('parsing-alert');
const findNetworkPolicyEditor = () => wrapper.findByTestId('network-policy-editor'); const findNetworkPolicyEditor = () => wrapper.findByTestId('network-policy-editor');
const findPolicyAlertPicker = () => wrapper.find(PolicyAlertPicker); const findPolicyAlertPicker = () => wrapper.findComponent(PolicyAlertPicker);
const findPolicyDescription = () => wrapper.find("[id='policyDescription']"); const findPolicyDescription = () => wrapper.find("[id='policyDescription']");
const findPolicyEnableContainer = () => wrapper.findByTestId('policy-enable'); const findPolicyEnableContainer = () => wrapper.findByTestId('policy-enable');
const findPolicyName = () => wrapper.find("[id='policyName']"); const findPolicyName = () => wrapper.find("[id='policyName']");
...@@ -104,12 +103,10 @@ spec: ...@@ -104,12 +103,10 @@ spec:
wrapper = null; wrapper = null;
}); });
it('renders the policy editor layout', () => {
expect(wrapper.find('section').element).toMatchSnapshot();
});
it('renders toggle with label', () => { 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', () => { it('renders a default rule with label', () => {
...@@ -120,20 +117,19 @@ spec: ...@@ -120,20 +117,19 @@ spec:
}); });
}); });
it('renders the policy alert picker', () => { it.each`
expect(findPolicyAlertPicker().exists()).toBe(true); component | status | findComponent | state
}); ${'policy alert picker'} | ${'does display'} | ${findPolicyAlertPicker} | ${true}
${'editor mode toggle'} | ${'does display'} | ${findEditorModeToggle} | ${true}
it('does not render yaml editor', () => { ${'policy name input'} | ${'does display'} | ${findPolicyName} | ${true}
expect(findYamlEditor().exists()).toBe(false); ${'rule editor'} | ${'does display'} | ${findRuleEditor} | ${true}
}); ${'add rule button'} | ${'does display'} | ${findAddRuleButton} | ${true}
${'policy preview'} | ${'does display'} | ${findPreview} | ${true}
it('does not render parsing error alert', () => { ${'yaml editor'} | ${'does not display'} | ${findNetworkPolicyEditor} | ${false}
expect(findYAMLParsingAlert().exists()).toBe(false); ${'parsing error alert'} | ${'does not display'} | ${findYAMLParsingAlert} | ${false}
}); ${'delete button'} | ${'does not display'} | ${findDeletePolicy} | ${false}
`('$status the $component', async ({ findComponent, state }) => {
it('does not render delete button', () => { expect(findComponent().exists()).toBe(state);
expect(findDeletePolicy().exists()).toBe(false);
}); });
describe('given .yaml editor mode is enabled', () => { describe('given .yaml editor mode is enabled', () => {
...@@ -145,14 +141,13 @@ spec: ...@@ -145,14 +141,13 @@ spec:
}); });
}); });
it('does not render rule editor', () => { it.each`
expect(findRuleEditor().exists()).toBe(false); component | status | findComponent | state
}); ${'editor mode toggle'} | ${'does display'} | ${findEditorModeToggle} | ${true}
${'rule editor'} | ${'does not display'} | ${findRuleEditor} | ${false}
it('renders yaml editor', () => { ${'yaml editor'} | ${'does display'} | ${findNetworkPolicyEditor} | ${true}
const editor = findYamlEditor(); `('$status the $component', async ({ findComponent, state }) => {
expect(editor.exists()).toBe(true); expect(findComponent().exists()).toBe(state);
expect(editor.element).toMatchSnapshot();
}); });
it('updates policy on yaml editor value change', async () => { it('updates policy on yaml editor value change', async () => {
...@@ -418,7 +413,7 @@ spec: ...@@ -418,7 +413,7 @@ spec:
}); });
it('removes policy and redirects to a threat monitoring path on secondary modal button click', async () => { 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(); await wrapper.vm.$nextTick();
expect(store.dispatch).toHaveBeenCalledWith('networkPolicies/deletePolicy', { expect(store.dispatch).toHaveBeenCalledWith('networkPolicies/deletePolicy', {
......
...@@ -21803,9 +21803,6 @@ msgstr "" ...@@ -21803,9 +21803,6 @@ msgstr ""
msgid "NetworkPolicies|Edit policy" msgid "NetworkPolicies|Edit policy"
msgstr "" msgstr ""
msgid "NetworkPolicies|Editor mode"
msgstr ""
msgid "NetworkPolicies|Enforcement status" msgid "NetworkPolicies|Enforcement status"
msgstr "" msgstr ""
...@@ -21905,9 +21902,6 @@ msgstr "" ...@@ -21905,9 +21902,6 @@ msgstr ""
msgid "NetworkPolicies|Unable to parse policy" msgid "NetworkPolicies|Unable to parse policy"
msgstr "" msgstr ""
msgid "NetworkPolicies|YAML editor"
msgstr ""
msgid "NetworkPolicies|all DNS names" msgid "NetworkPolicies|all DNS names"
msgstr "" 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