Commit 146c43c9 authored by Kushal Pandya's avatar Kushal Pandya
Browse files

Merge branch 'network-policy-editor-create' into 'master'

Implement creation flow in policy editor

See merge request gitlab-org/gitlab!40566
parents 161fc228 e3b9e8db
......@@ -3,6 +3,8 @@ export const EditorModeYAML = 'yaml';
 
export const RuleTypeNetwork = 'network';
 
export const RuleActionTypeAllow = 'allow';
export const RuleDirectionInbound = 'ingress';
export const RuleDirectionOutbound = 'egress';
 
......
<script>
export default {};
import { GlForm, GlFormSelect, GlSprintf } from '@gitlab/ui';
import { s__ } from '~/locale';
import { RuleActionTypeAllow } from './constants';
export default {
components: {
GlForm,
GlFormSelect,
GlSprintf,
},
data() {
return { actionType: RuleActionTypeAllow };
},
actionTypes: [{ value: RuleActionTypeAllow, text: s__('NetworkPolicies|Allow') }],
};
</script>
 
<template>
<div class="gl-bg-gray-100 p-2"></div>
<div
class="gl-bg-gray-10 gl-border-solid gl-border-1 gl-border-gray-100 gl-rounded-base gl-px-5 gl-pt-5"
>
<gl-form inline>
<gl-sprintf
:message="
s__(
'NetworkPolicies|%{labelStart}Then%{labelEnd} %{action} %{spanStart}the network traffic.%{spanEnd}',
)
"
>
<template #label="{ content }">
<label for="actionType" class="text-uppercase gl-font-lg gl-mr-4 gl-mb-5!">{{
content
}}</label>
</template>
<template #action>
<gl-form-select
id="actionType"
class="gl-mr-4 gl-mb-5!"
:value="actionType"
:options="$options.actionTypes"
/>
</template>
<template #span="{ content }">
<span class="gl-mb-5">{{ content }}</span>
</template>
</gl-sprintf>
</gl-form>
</div>
</template>
<script>
import { mapActions } from 'vuex';
import { mapState, mapActions } from 'vuex';
import {
GlFormGroup,
GlFormSelect,
......@@ -11,6 +11,7 @@ import {
GlAlert,
} from '@gitlab/ui';
import { s__ } from '~/locale';
import { redirectTo } from '~/lib/utils/url_utility';
import EnvironmentPicker from '../environment_picker.vue';
import NetworkPolicyEditor from '../network_policy_editor.vue';
import PolicyRuleBuilder from './policy_rule_builder.vue';
......@@ -43,6 +44,12 @@ export default {
PolicyPreview,
PolicyActionPicker,
},
props: {
threatMonitoringPath: {
type: String,
required: true,
},
},
data() {
return {
editorMode: EditorModeRule,
......@@ -65,6 +72,8 @@ export default {
policyYaml() {
return toYaml(this.policy);
},
...mapState('threatMonitoring', ['currentEnvironmentId']),
...mapState('networkPolicies', ['errorUpdatingPolicy']),
shouldShowRuleEditor() {
return this.editorMode === EditorModeRule;
},
......@@ -80,6 +89,7 @@ export default {
},
methods: {
...mapActions('threatMonitoring', ['fetchEnvironments']),
...mapActions('networkPolicies', ['createPolicy']),
addRule() {
this.policy.rules.push(buildRule(RuleTypeEndpoint));
},
......@@ -110,6 +120,15 @@ export default {
 
this.editorMode = mode;
},
savePolicy() {
const policy = { manifest: toYaml(this.policy) };
return this.createPolicy({
environmentId: this.currentEnvironmentId,
policy,
}).then(() => {
if (!this.errorUpdatingPolicy) redirectTo(this.threatMonitoringPath);
});
},
},
policyTypes: [{ value: 'networkPolicy', text: s__('NetworkPolicies|Network Policy') }],
editorModes: [
......@@ -197,7 +216,7 @@ export default {
@endpoint-labels-change="updateEndpointLabels"
/>
 
<div class="gl-p-3 gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100">
<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"
......@@ -209,6 +228,7 @@ export default {
</div>
 
<h4>{{ s__('NetworkPolicies|Actions') }}</h4>
<p>{{ s__('NetworkPolicies|Traffic that does not match any rule will be blocked.') }}</p>
<policy-action-picker />
</div>
<div class="col-sm-12 col-md-6 col-lg-5 col-xl-4">
......@@ -238,10 +258,17 @@ export default {
<hr />
<div class="row">
<div class="col-md-auto">
<gl-button type="submit" category="primary" variant="success">{{
s__('NetworkPolicies|Create policy')
<gl-button
type="submit"
category="primary"
variant="success"
data-testid="create-policy"
@click="savePolicy"
>{{ s__('NetworkPolicies|Create policy') }}</gl-button
>
<gl-button category="secondary" variant="default" :href="threatMonitoringPath">{{
__('Cancel')
}}</gl-button>
<gl-button category="secondary" variant="default">{{ __('Cancel') }}</gl-button>
</div>
</div>
</section>
......
......@@ -133,7 +133,7 @@ export default {
<div
class="gl-bg-gray-10 gl-border-solid gl-border-1 gl-border-gray-100 gl-rounded-base px-3 pt-3"
>
<gl-form inline>
<gl-form inline @submit.prevent>
<gl-sprintf :message="sprintfTemplate">
<template #ifLabel="{ content }">
<label for="ruleType" class="text-uppercase gl-font-lg gl-mr-4 gl-mb-5!">{{
......
......@@ -4,7 +4,7 @@ import createStore from './store';
 
export default () => {
const el = document.querySelector('#js-policy-builder-app');
const { environmentsEndpoint, networkPoliciesEndpoint } = el.dataset;
const { environmentsEndpoint, networkPoliciesEndpoint, threatMonitoringPath } = el.dataset;
 
const store = createStore();
store.dispatch('threatMonitoring/setEndpoints', {
......@@ -18,7 +18,9 @@ export default () => {
el,
store,
render(createElement) {
return createElement(PolicyEditorApp, {});
return createElement(PolicyEditorApp, {
props: { threatMonitoringPath },
});
},
});
};
......@@ -12,6 +12,7 @@ export default {
state.environments = payload;
state.isLoadingEnvironments = false;
state.errorLoadingEnvironments = false;
if (payload.length > 0) state.currentEnvironmentId = payload[0].id;
},
[types.RECEIVE_ENVIRONMENTS_ERROR](state) {
state.isLoadingEnvironments = false;
......
......@@ -4,4 +4,5 @@
 
#js-policy-builder-app{ data: { network_policies_endpoint: project_security_network_policies_path(@project),
environments_endpoint: project_environments_path(@project),
threat_monitoring_path: project_threat_monitoring_path(@project),
} }
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PolicyActionPicker component renders policy action picker 1`] = `
<div
class="gl-bg-gray-10 gl-border-solid gl-border-1 gl-border-gray-100 gl-rounded-base gl-px-5 gl-pt-5"
>
<gl-form-stub
inline=""
>
<gl-sprintf-stub
message="%{labelStart}Then%{labelEnd} %{action} %{spanStart}the network traffic.%{spanEnd}"
/>
</gl-form-stub>
</div>
`;
......@@ -158,7 +158,7 @@ exports[`PolicyEditorApp component renders the policy editor layout 1`] = `
</h4>
<div
class="gl-p-3 gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100"
class="gl-p-3 gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100 gl-mb-5"
>
<gl-button-stub
category="primary"
......@@ -175,6 +175,10 @@ exports[`PolicyEditorApp component renders the policy editor layout 1`] = `
Actions
</h4>
<p>
Traffic that does not match any rule will be blocked.
</p>
<policy-action-picker-stub />
</div>
......@@ -213,6 +217,7 @@ spec:
>
<gl-button-stub
category="primary"
data-testid="create-policy"
icon=""
size="medium"
type="submit"
......@@ -223,6 +228,7 @@ spec:
<gl-button-stub
category="secondary"
href="/threat-monitoring"
icon=""
size="medium"
variant="default"
......
import { shallowMount } from '@vue/test-utils';
import PolicyActionPicker from 'ee/threat_monitoring/components/policy_editor/policy_action_picker.vue';
describe('PolicyActionPicker component', () => {
let wrapper;
const factory = ({ propsData } = {}) => {
wrapper = shallowMount(PolicyActionPicker, {
propsData: {
...propsData,
},
});
};
beforeEach(() => {
factory();
});
afterEach(() => {
wrapper.destroy();
});
it('renders policy action picker', () => {
expect(wrapper.element).toMatchSnapshot();
});
});
......@@ -12,6 +12,10 @@ import {
EndpointMatchModeLabel,
} from 'ee/threat_monitoring/components/policy_editor/constants';
import fromYaml from 'ee/threat_monitoring/components/policy_editor/lib/from_yaml';
import toYaml from 'ee/threat_monitoring/components/policy_editor/lib/to_yaml';
import { redirectTo } from '~/lib/utils/url_utility';
jest.mock('~/lib/utils/url_utility');
 
describe('PolicyEditorApp component', () => {
let store;
......@@ -22,9 +26,15 @@ describe('PolicyEditorApp component', () => {
Object.assign(store.state.threatMonitoring, {
...state,
});
Object.assign(store.state.networkPolicies, {
...state,
});
jest.spyOn(store, 'dispatch').mockImplementation(() => Promise.resolve());
 
wrapper = shallowMount(PolicyEditorApp, {
propsData: {
threatMonitoringPath: '/threat-monitoring',
...propsData,
},
store,
......@@ -45,7 +55,6 @@ describe('PolicyEditorApp component', () => {
 
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
 
it('renders the policy editor layout', () => {
......@@ -188,4 +197,32 @@ spec:
expect(findAddRuleButton().props('disabled')).toBe(true);
});
});
it('creates policy and redirects to a threat monitoring path', async () => {
wrapper.find("[data-testid='create-policy']").vm.$emit('click');
await wrapper.vm.$nextTick();
expect(store.dispatch).toHaveBeenCalledWith('networkPolicies/createPolicy', {
environmentId: -1,
policy: { manifest: toYaml(wrapper.vm.policy) },
});
expect(redirectTo).toHaveBeenCalledWith('/threat-monitoring');
});
describe('given there is a createPolicy error', () => {
beforeEach(() => {
factory({
state: {
errorUpdatingPolicy: true,
},
});
});
it('it does not redirect', async () => {
wrapper.find("[data-testid='create-policy']").vm.$emit('click');
await wrapper.vm.$nextTick();
expect(redirectTo).not.toHaveBeenCalledWith('/threat-monitoring');
});
});
});
......@@ -48,6 +48,10 @@ describe('Threat Monitoring mutations', () => {
it('sets errorLoadingEnvironments to false', () => {
expect(state.errorLoadingEnvironments).toBe(false);
});
it('sets currentEnvironmentId to 1', () => {
expect(state.currentEnvironmentId).toEqual(1);
});
});
 
describe(types.RECEIVE_ENVIRONMENTS_ERROR, () => {
......
......@@ -16170,6 +16170,9 @@ msgstr ""
msgid "NetworkPolicies|%{ifLabelStart}if%{ifLabelEnd} %{ruleType} %{isLabelStart}is%{isLabelEnd} %{ruleDirection} %{ruleSelector} %{directionLabelStart}and is outbound to a%{directionLabelEnd} %{rule} %{portsLabelStart}on%{portsLabelEnd} %{ports}"
msgstr ""
 
msgid "NetworkPolicies|%{labelStart}Then%{labelEnd} %{action} %{spanStart}the network traffic.%{spanEnd}"
msgstr ""
msgid "NetworkPolicies|%{number} selected"
msgstr ""
 
......@@ -16191,6 +16194,9 @@ msgstr ""
msgid "NetworkPolicies|All selected"
msgstr ""
 
msgid "NetworkPolicies|Allow"
msgstr ""
msgid "NetworkPolicies|Allow all inbound traffic to %{selector} from %{ruleSelector} on %{ports}"
msgstr ""
 
......@@ -16299,6 +16305,9 @@ msgstr ""
msgid "NetworkPolicies|Status"
msgstr ""
 
msgid "NetworkPolicies|Traffic that does not match any rule will be blocked."
msgstr ""
msgid "NetworkPolicies|YAML editor"
msgstr ""
 
......
Supports Markdown
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