Commit 8787a3c4 authored by Sean McGivern's avatar Sean McGivern

Merge branch 'network-policy-editor-feature-flag' into 'master'

Network policy editor feature flag

See merge request gitlab-org/gitlab!41949
parents 6409be6a ec560eae
...@@ -88,8 +88,9 @@ investigate it for potential threats by ...@@ -88,8 +88,9 @@ investigate it for potential threats by
The **Threat Monitoring** page's **Policy** tab displays deployed The **Threat Monitoring** page's **Policy** tab displays deployed
network policies for all available environments. You can check a network policies for all available environments. You can check a
network policy's `yaml` manifest and toggle the policy's enforcement network policy's `yaml` manifest, toggle the policy's enforcement
status. This section has the following prerequisites: status, and create and edit deployed policies. This section has the
following prerequisites:
- Your project contains at least one [environment](../../../ci/environments/index.md) - Your project contains at least one [environment](../../../ci/environments/index.md)
- You've [installed Cilium](../../clusters/applications.md#install-cilium-using-gitlab-cicd) - You've [installed Cilium](../../clusters/applications.md#install-cilium-using-gitlab-cicd)
...@@ -124,3 +125,47 @@ Disabled network policies have the ...@@ -124,3 +125,47 @@ Disabled network policies have the
`podSelector` block. This narrows the scope of such a policy and as a `podSelector` block. This narrows the scope of such a policy and as a
result it doesn't affect any pods. The policy itself is still deployed result it doesn't affect any pods. The policy itself is still deployed
to the corresponding deployment namespace. to the corresponding deployment namespace.
### Container Network Policy editor
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3403) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.4.
The policy editor allows you to create, edit, and delete policies. To
create a new policy click the **New policy** button located in the
**Policy** tab's header. To edit an existing policy, click**Edit
policy** in the selected policy drawer.
NOTE: **Note:**
The policy editor only supports the
[CiliumNetworkPolicy](https://docs.cilium.io/en/v1.8/policy/)specification. Regular
Kubernetes
[NetworkPolicy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#networkpolicy-v1-networking-k8s-io)
resources aren't supported.
The policy editor has two modes:
- The visual _Rule_ mode allows you to construct and preview policy
rules using rule blocks and related controls.
- YAML mode allows you to enter a policy definition in `.yaml` format
and is aimed at expert users and cases that the Rule mode doesn't
support.
You can use both modes interchangeably and switch between them at any
time. If a YAML resource is incorrect, Rule mode is automatically
disabled. You must use YAML mode to fix your policy before Rule mode
is available again.
Rule mode supports the following rule types:
- [Labels](https://docs.cilium.io/en/v1.8/policy/language/#labels-based).
- [Entities](https://docs.cilium.io/en/v1.8/policy/language/#entities-based).
- [IP/CIDR](https://docs.cilium.io/en/v1.8/policy/language/#ip-cidr-based). Only
the `toCIDR` block without `except` is supported.
- [DNS](https://docs.cilium.io/en/v1.8/policy/language/#dns-based).
- [Level 4](https://docs.cilium.io/en/v1.8/policy/language/#layer-4-examples)
can be added to all other rules.
Once your policy is complete, save it by pressing the **Save policy**
button at the bottom of the editor. Existing policies can also be
removed from the editor interface by clicking the **Delete policy**
button at the bottom of the editor.
...@@ -18,8 +18,6 @@ import NetworkPolicyEditor from './network_policy_editor.vue'; ...@@ -18,8 +18,6 @@ import NetworkPolicyEditor from './network_policy_editor.vue';
import PolicyDrawer from './policy_editor/policy_drawer.vue'; import PolicyDrawer from './policy_editor/policy_drawer.vue';
import { CiliumNetworkPolicyKind } from './policy_editor/constants'; import { CiliumNetworkPolicyKind } from './policy_editor/constants';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default { export default {
components: { components: {
GlTable, GlTable,
...@@ -34,7 +32,6 @@ export default { ...@@ -34,7 +32,6 @@ export default {
NetworkPolicyEditor, NetworkPolicyEditor,
PolicyDrawer, PolicyDrawer,
}, },
mixins: [glFeatureFlagsMixin()],
props: { props: {
documentationPath: { documentationPath: {
type: String, type: String,
...@@ -80,14 +77,10 @@ export default { ...@@ -80,14 +77,10 @@ export default {
: false; : false;
}, },
shouldShowCiliumDrawer() { shouldShowCiliumDrawer() {
return this.glFeatures.networkPolicyEditor && this.hasCiliumSelectedPolicy; return this.hasCiliumSelectedPolicy;
}, },
shouldShowEditButton() { shouldShowEditButton() {
return ( return this.hasCiliumSelectedPolicy && Boolean(this.selectedPolicy.creationTimestamp);
this.glFeatures.networkPolicyEditor &&
this.hasCiliumSelectedPolicy &&
Boolean(this.selectedPolicy.creationTimestamp)
);
}, },
editPolicyPath() { editPolicyPath() {
return this.hasSelectedPolicy return this.hasSelectedPolicy
...@@ -184,7 +177,7 @@ export default { ...@@ -184,7 +177,7 @@ export default {
<div class="pt-3 px-3 bg-gray-light"> <div class="pt-3 px-3 bg-gray-light">
<div class="row justify-content-between align-items-center"> <div class="row justify-content-between align-items-center">
<environment-picker ref="environmentsPicker" /> <environment-picker ref="environmentsPicker" />
<div v-if="glFeatures.networkPolicyEditor" class="col-sm-auto"> <div class="col-sm-auto">
<gl-button <gl-button
category="secondary" category="secondary"
variant="info" variant="info"
......
...@@ -3,10 +3,6 @@ ...@@ -3,10 +3,6 @@
module Projects module Projects
class ThreatMonitoringController < Projects::ApplicationController class ThreatMonitoringController < Projects::ApplicationController
before_action :authorize_read_threat_monitoring! before_action :authorize_read_threat_monitoring!
before_action :verify_network_policy_editor_flag!, only: [:new, :edit]
before_action do
push_frontend_feature_flag(:network_policy_editor, project)
end
def edit def edit
@environment = project.environments.find(params[:environment_id]) @environment = project.environments.find(params[:environment_id])
...@@ -23,11 +19,5 @@ module Projects ...@@ -23,11 +19,5 @@ module Projects
render_404 render_404
end end
end end
private
def verify_network_policy_editor_flag!
render_404 unless Feature.enabled?(:network_policy_editor, project, default_enabled: false)
end
end end
end end
---
title: Add policy editor to the Threat Monitoring page
merge_request: 41949
author:
type: added
...@@ -83,29 +83,11 @@ RSpec.describe Projects::ThreatMonitoringController do ...@@ -83,29 +83,11 @@ RSpec.describe Projects::ThreatMonitoringController do
stub_licensed_features(threat_monitoring: true) stub_licensed_features(threat_monitoring: true)
end end
context 'and feature flag is disabled' do it 'renders the new template' do
before do subject
stub_feature_flags(network_policy_editor: false)
end
it 'returns 404' do
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'and feature flag is enabled' do
before do
stub_feature_flags(network_policy_editor: true)
end
it 'renders the new template' do
subject
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:new) expect(response).to render_template(:new)
end
end end
end end
...@@ -165,14 +147,35 @@ RSpec.describe Projects::ThreatMonitoringController do ...@@ -165,14 +147,35 @@ RSpec.describe Projects::ThreatMonitoringController do
end end
context 'when feature is available' do context 'when feature is available' do
let(:service) { instance_double('NetworkPolicies::FindResourceService', execute: ServiceResponse.success(payload: policy)) }
let(:policy) do
Gitlab::Kubernetes::CiliumNetworkPolicy.new(
name: 'policy',
namespace: 'another',
selector: { matchLabels: { role: 'db' } },
ingress: [{ from: [{ namespaceSelector: { matchLabels: { project: 'myproject' } } }] }]
)
end
before do before do
stub_licensed_features(threat_monitoring: true) stub_licensed_features(threat_monitoring: true)
allow(NetworkPolicies::FindResourceService).to(
receive(:new)
.with(resource_name: 'policy', environment: environment, kind: Gitlab::Kubernetes::CiliumNetworkPolicy::KIND)
.and_return(service)
)
end end
context 'and feature flag is disabled' do it 'renders the new template' do
before do subject
stub_feature_flags(network_policy_editor: false)
end expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:edit)
end
context 'when environment is missing' do
let(:environment_id) { 'missing' }
it 'returns 404' do it 'returns 404' do
subject subject
...@@ -181,51 +184,13 @@ RSpec.describe Projects::ThreatMonitoringController do ...@@ -181,51 +184,13 @@ RSpec.describe Projects::ThreatMonitoringController do
end end
end end
context 'and feature flag is enabled' do context 'when service failed' do
let(:service) { instance_double('NetworkPolicies::FindResourceService', execute: ServiceResponse.success(payload: policy)) } let(:service) { instance_double('NetworkPolicies::FindResourceService', execute: ServiceResponse.error(message: 'error')) }
let(:policy) do
Gitlab::Kubernetes::CiliumNetworkPolicy.new(
name: 'policy',
namespace: 'another',
selector: { matchLabels: { role: 'db' } },
ingress: [{ from: [{ namespaceSelector: { matchLabels: { project: 'myproject' } } }] }]
)
end
before do
stub_feature_flags(network_policy_editor: true)
allow(NetworkPolicies::FindResourceService).to(
receive(:new)
.with(resource_name: 'policy', environment: environment, kind: Gitlab::Kubernetes::CiliumNetworkPolicy::KIND)
.and_return(service)
)
end
it 'renders the new template' do it 'returns 404' do
subject subject
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:not_found)
expect(response).to render_template(:edit)
end
context 'when environment is missing' do
let(:environment_id) { 'missing' }
it 'returns 404' do
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when service failed' do
let(:service) { instance_double('NetworkPolicies::FindResourceService', execute: ServiceResponse.error(message: 'error')) }
it 'returns 404' do
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end end
end end
end end
......
...@@ -6,10 +6,10 @@ exports[`NetworkPolicyList component renders policies table 1`] = ` ...@@ -6,10 +6,10 @@ exports[`NetworkPolicyList component renders policies table 1`] = `
<table <table
aria-busy="false" aria-busy="false"
aria-colcount="3" aria-colcount="3"
aria-describedby="__BVID__529__caption_" aria-describedby="__BVID__341__caption_"
aria-multiselectable="false" aria-multiselectable="false"
class="table b-table gl-table table-hover b-table-stacked-md b-table-selectable b-table-select-single" class="table b-table gl-table table-hover b-table-stacked-md b-table-selectable b-table-select-single"
id="__BVID__529" id="__BVID__341"
role="table" role="table"
> >
<!----> <!---->
......
...@@ -61,85 +61,50 @@ describe('NetworkPolicyList component', () => { ...@@ -61,85 +61,50 @@ describe('NetworkPolicyList component', () => {
expect(findEnvironmentsPicker().exists()).toBe(true); expect(findEnvironmentsPicker().exists()).toBe(true);
}); });
it('does not render the new policy button', () => { it('renders the new policy button', () => {
const button = wrapper.find('[data-testid="new-policy"]'); const button = wrapper.find('[data-testid="new-policy"]');
expect(button.exists()).toBe(false); expect(button.exists()).toBe(true);
}); });
describe('given the networkPolicyEditor feature flag is enabled', () => { it('does not render the new policy drawer', () => {
beforeEach(() => { expect(wrapper.find(PolicyDrawer).exists()).toBe(false);
factory({ });
provide: {
glFeatures: {
networkPolicyEditor: true,
},
},
});
});
it('renders the new policy button', () => {
const button = wrapper.find('[data-testid="new-policy"]');
expect(button.exists()).toBe(true);
});
it('does not render the new policy drawer', () => {
expect(wrapper.find(PolicyDrawer).exists()).toBe(false);
});
it('does not render edit button', () => {
expect(wrapper.find('[data-testid="edit-button"]').exists()).toBe(false);
});
describe('given there is a selected policy', () => { it('does not render edit button', () => {
beforeEach(() => { expect(wrapper.find('[data-testid="edit-button"]').exists()).toBe(false);
factory({ });
provide: {
glFeatures: {
networkPolicyEditor: true,
},
},
data: () => ({ selectedPolicyName: 'policy' }),
});
});
});
describe('given selected policy is a cilium policy', () => { describe('given selected policy is a cilium policy', () => {
const manifest = `apiVersion: cilium.io/v2 const manifest = `apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy kind: CiliumNetworkPolicy
metadata: metadata:
name: policy name: policy
spec: spec:
endpointSelector: {}`; endpointSelector: {}`;
beforeEach(() => { beforeEach(() => {
factory({ factory({
provide: { data: () => ({ selectedPolicyName: 'policy' }),
glFeatures: { state: {
networkPolicyEditor: true, policies: [
{
name: 'policy',
creationTimestamp: new Date(),
manifest,
}, },
}, ],
data: () => ({ selectedPolicyName: 'policy' }), },
state: {
policies: [
{
name: 'policy',
creationTimestamp: new Date(),
manifest,
},
],
},
});
}); });
});
it('renders the new policy drawer', () => { it('renders the new policy drawer', () => {
expect(wrapper.find(PolicyDrawer).exists()).toBe(true); expect(wrapper.find(PolicyDrawer).exists()).toBe(true);
}); });
it('renders edit button', () => { it('renders edit button', () => {
const button = wrapper.find('[data-testid="edit-button"]'); const button = wrapper.find('[data-testid="edit-button"]');
expect(button.exists()).toBe(true); expect(button.exists()).toBe(true);
expect(button.attributes().href).toBe('/policies/policy/edit?environment_id=-1'); expect(button.attributes().href).toBe('/policies/policy/edit?environment_id=-1');
});
}); });
}); });
......
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