Commit e7662467 authored by ap4y's avatar ap4y

Implement toggle for Kubernetes::NetworkPolicy

This commit implements support for enabling and disabling
Kubernetes::NetworkPolicy resources via update action of the
corresponding API. Toggling happens via a custom label matcher for the
podSelector.
parent 7e369ce4
......@@ -50,6 +50,10 @@ module Projects
def update
policy = Gitlab::Kubernetes::NetworkPolicy.from_yaml(params[:manifest])
unless params[:enabled].nil?
params[:enabled] ? policy.enable : policy.disable
end
response = NetworkPolicies::DeployResourceService.new(
resource_name: params[:id],
policy: policy,
......
......@@ -9,7 +9,7 @@ describe Projects::Security::NetworkPoliciesController do
let_it_be(:project) { create(:project, :public, :repository, group: group) }
let_it_be(:environment) { create(:environment, :with_review_app, project: project) }
let_it_be(:action_params) { { project_id: project, namespace_id: project.namespace, environment_id: environment } }
let_it_be(:action_params) { { project_id: project, namespace_id: project.namespace, environment_id: environment.id } }
shared_examples 'CRUD service errors' do
context 'with a error service response' do
......@@ -239,8 +239,9 @@ describe Projects::Security::NetworkPoliciesController do
end
describe 'PUT #update' do
subject { put :update, params: action_params.merge(id: 'example-policy', manifest: manifest), format: :json }
subject { put :update, params: action_params.merge(id: 'example-policy', manifest: manifest, enabled: enabled), as: :json }
let(:enabled) { nil }
let(:service) { instance_double('NetworkPolicies::DeployResourceService', execute: ServiceResponse.success(payload: policy)) }
let(:policy) do
Gitlab::Kubernetes::NetworkPolicy.new(
......@@ -289,6 +290,34 @@ describe Projects::Security::NetworkPoliciesController do
end
include_examples 'CRUD service errors'
context 'with enabled param' do
let(:enabled) { true }
before do
allow(Gitlab::Kubernetes::NetworkPolicy).to receive(:new) { policy }
end
it 'enables policy and responds with success' do
expect(policy).to receive(:enable)
subject
expect(response).to have_gitlab_http_status(:success)
end
context 'with enabled=false' do
let(:enabled) { false }
it 'disables policy and responds with success' do
expect(policy).to receive(:disable)
subject
expect(response).to have_gitlab_http_status(:success)
end
end
end
end
context 'with unauthorized user' do
......
......@@ -3,6 +3,8 @@
module Gitlab
module Kubernetes
class NetworkPolicy
DISABLED_BY_LABEL = :'network-policy.gitlab.com/disabled_by'
def initialize(name:, namespace:, pod_selector:, ingress:, labels: nil, creation_timestamp: nil, policy_types: ["Ingress"], egress: nil)
@name = name
@namespace = namespace
......@@ -66,7 +68,8 @@ module Gitlab
namespace: namespace,
creation_timestamp: creation_timestamp,
manifest: manifest,
is_autodevops: autodevops?
is_autodevops: autodevops?,
is_enabled: enabled?
}
end
......@@ -76,6 +79,28 @@ module Gitlab
!labels[:chart].nil? && labels[:chart].start_with?('auto-deploy-app-')
end
# podSelector selects pods that should be targeted by this
# policy. We can narrow selection by requiring this policy to
# match our custom labels. Since DISABLED_BY label will not be
# on any pod a policy will be effectively disabled.
def enabled?
return true unless pod_selector&.key?(:matchLabels)
!pod_selector[:matchLabels]&.key?(DISABLED_BY_LABEL)
end
def enable
return if enabled?
pod_selector[:matchLabels].delete(DISABLED_BY_LABEL)
end
def disable
@pod_selector ||= {}
pod_selector[:matchLabels] ||= {}
pod_selector[:matchLabels].merge!(DISABLED_BY_LABEL => 'gitlab')
end
private
attr_reader :name, :namespace, :labels, :creation_timestamp, :pod_selector, :policy_types, :ingress, :egress
......
......@@ -219,7 +219,8 @@ describe Gitlab::Kubernetes::NetworkPolicy do
spec: { podSelector: pod_selector, policyTypes: %w(Ingress Egress), ingress: ingress, egress: egress }
}.deep_stringify_keys
),
is_autodevops: false
is_autodevops: false,
is_enabled: true
}
end
......@@ -256,4 +257,138 @@ describe Gitlab::Kubernetes::NetworkPolicy do
it { is_expected.to be true }
end
end
describe '#enabled?' do
subject { policy.enabled? }
let(:pod_selector) { nil }
let(:policy) do
described_class.new(
name: name,
namespace: namespace,
pod_selector: pod_selector,
ingress: ingress
)
end
it { is_expected.to be true }
context 'with empty pod_selector' do
let(:pod_selector) { {} }
it { is_expected.to be true }
end
context 'with nil matchLabels in pod_selector' do
let(:pod_selector) { { matchLabels: nil } }
it { is_expected.to be true }
end
context 'with empty matchLabels in pod_selector' do
let(:pod_selector) { { matchLabels: {} } }
it { is_expected.to be true }
end
context 'with disabled_by label in matchLabels in pod_selector' do
let(:pod_selector) do
{ matchLabels: { Gitlab::Kubernetes::NetworkPolicy::DISABLED_BY_LABEL => 'gitlab' } }
end
it { is_expected.to be false }
end
end
describe '#enable' do
subject { policy.enabled? }
let(:pod_selector) { nil }
let(:policy) do
described_class.new(
name: name,
namespace: namespace,
pod_selector: pod_selector,
ingress: ingress
)
end
before do
policy.enable
end
it { is_expected.to be true }
context 'with empty pod_selector' do
let(:pod_selector) { {} }
it { is_expected.to be true }
end
context 'with nil matchLabels in pod_selector' do
let(:pod_selector) { { matchLabels: nil } }
it { is_expected.to be true }
end
context 'with empty matchLabels in pod_selector' do
let(:pod_selector) { { matchLabels: {} } }
it { is_expected.to be true }
end
context 'with disabled_by label in matchLabels in pod_selector' do
let(:pod_selector) do
{ matchLabels: { Gitlab::Kubernetes::NetworkPolicy::DISABLED_BY_LABEL => 'gitlab' } }
end
it { is_expected.to be true }
end
end
describe '#disable' do
subject { policy.enabled? }
let(:pod_selector) { nil }
let(:policy) do
described_class.new(
name: name,
namespace: namespace,
pod_selector: pod_selector,
ingress: ingress
)
end
before do
policy.disable
end
it { is_expected.to be false }
context 'with empty pod_selector' do
let(:pod_selector) { {} }
it { is_expected.to be false }
end
context 'with nil matchLabels in pod_selector' do
let(:pod_selector) { { matchLabels: nil } }
it { is_expected.to be false }
end
context 'with empty matchLabels in pod_selector' do
let(:pod_selector) { { matchLabels: {} } }
it { is_expected.to be false }
end
context 'with disabled_by label in matchLabels in pod_selector' do
let(:pod_selector) do
{ matchLabels: { Gitlab::Kubernetes::NetworkPolicy::DISABLED_BY_LABEL => 'gitlab' } }
end
it { is_expected.to be false }
end
end
end
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