Commit 3cd26822 authored by Stan Hu's avatar Stan Hu

Merge branch 'add_support_cross_namespaces_network_policy' into 'master'

Add support for multiple environments

See merge request gitlab-org/gitlab!40529
parents 4f49cdb7 aa862ae8
......@@ -34,7 +34,7 @@ module Projects
end
def index
response = NetworkPolicies::ResourcesService.new(environment: environment).execute
response = NetworkPolicies::ResourcesService.new(project: project, environment_id: params[:environment_id]).execute
respond_with_service_response(response)
end
......@@ -89,7 +89,7 @@ module Projects
end
def respond_with_service_response(response)
payload = response.success? ? response.payload : { error: response.message }
payload = response.success? ? response.payload : { payload: response.payload, error: response.message }
respond_to do |format|
format.json do
render status: response.http_status, json: payload
......
......@@ -4,23 +4,59 @@ module NetworkPolicies
class ResourcesService
include NetworkPolicies::Responses
def initialize(environment:)
@platform = environment.deployment_platform
@kubernetes_namespace = environment.deployment_namespace
LIMIT = 100
def initialize(project:, environment_id: nil)
@kubeclient_info = extract_info_for_kubeclient(project, environment_id)
end
def execute
return no_platform_response unless @platform
return no_platform_response unless has_deployment_platform? @kubeclient_info
policies = []
errors = []
@kubeclient_info.each do |platform, namespace|
policies_per_environment, error_per_environment = execute_per_environment(platform, namespace)
policies += policies_per_environment
errors << error_per_environment if error_per_environment
end
errors.empty? ? ServiceResponse.success(payload: policies) : kubernetes_error_response(errors.join, policies)
end
private
policies = @platform.kubeclient
.get_network_policies(namespace: @kubernetes_namespace)
def execute_per_environment(platform, namespace)
policies = platform.kubeclient
.get_network_policies(namespace: namespace)
.map { |resource| Gitlab::Kubernetes::NetworkPolicy.from_resource(resource) }
policies += @platform.kubeclient
.get_cilium_network_policies(namespace: @kubernetes_namespace)
policies += platform.kubeclient
.get_cilium_network_policies(namespace: namespace)
.map { |resource| Gitlab::Kubernetes::CiliumNetworkPolicy.from_resource(resource) }
ServiceResponse.success(payload: policies)
[policies, nil]
rescue Kubeclient::HttpError => e
kubernetes_error_response(e)
[[], e]
end
def has_deployment_platform?(kubeclient_info)
kubeclient_info.any? { |platform, namespace| platform.present? }
end
# rubocop: disable CodeReuse/ActiveRecord
def extract_info_for_kubeclient(project, environment_id)
kubernetes_namespaces =
if environment_id
Clusters::KubernetesNamespace.where(environment: project.environments.id_in(environment_id))
else
Clusters::KubernetesNamespace.where(environment: project.environments.available.at_most(LIMIT))
end
kubernetes_namespaces
.order(updated_at: :desc)
.preload(:platform_kubernetes)
.group_by(&:namespace)
.map { |namespace, kubernetes_namespaces| [kubernetes_namespaces.first.platform_kubernetes, namespace] }
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
......@@ -2,10 +2,11 @@
module NetworkPolicies
module Responses
def kubernetes_error_response(error)
def kubernetes_error_response(error, payload = {})
ServiceResponse.error(
http_status: :bad_request,
message: s_('NetworkPolicies|Kubernetes error: %{error}') % { error: error }
message: s_('NetworkPolicies|Kubernetes error: %{error}') % { error: error },
payload: payload
)
end
......
---
title: Add support for multiple environments on the controller and service level
merge_request: 40529
author:
type: changed
......@@ -42,7 +42,7 @@ RSpec.describe Projects::Security::NetworkPoliciesController do
subject
expect(response).to have_gitlab_http_status(:bad_request)
expect(response.body).to eq('{"error":"error"}')
expect(response.body).to eq('{"payload":{},"error":"error"}')
end
end
end
......@@ -175,7 +175,7 @@ RSpec.describe Projects::Security::NetworkPoliciesController do
before do
group.add_developer(user)
allow(NetworkPolicies::ResourcesService).to receive(:new).with(environment: environment) { service }
allow(NetworkPolicies::ResourcesService).to receive(:new).with(environment_id: environment.id.to_s, project: project) { service }
end
it 'responds with policies' do
......
......@@ -3,9 +3,13 @@
require 'spec_helper'
RSpec.describe NetworkPolicies::ResourcesService do
let(:service) { NetworkPolicies::ResourcesService.new(environment: environment) }
let(:environment) { instance_double('Environment', deployment_platform: platform, deployment_namespace: 'namespace') }
let(:platform) { instance_double('Clusters::Platforms::Kubernetes', kubeclient: kubeclient) }
let(:service) { NetworkPolicies::ResourcesService.new(environment_id: environment_id, project: project) }
let(:environment) { create(:environment, project: project) }
let(:environment_id) { environment.id }
let(:project) { create(:project) }
let(:cluster) { create(:cluster, :instance) }
let!(:cluster_kubernetes_namespace) { create(:cluster_kubernetes_namespace, project: project, cluster: cluster, environment: environment, namespace: 'namespace') }
let(:platform) { double('Clusters::Platforms::Kubernetes', kubeclient: kubeclient) }
let(:kubeclient) { double('Kubeclient::Client') }
let(:policy) do
Gitlab::Kubernetes::NetworkPolicy.new(
......@@ -26,12 +30,16 @@ RSpec.describe NetworkPolicies::ResourcesService do
)
end
before do
allow_any_instance_of(Clusters::KubernetesNamespace).to receive(:platform_kubernetes).and_return(platform)
end
describe '#execute' do
subject { service.execute }
it 'returns success response with policies from the deployment namespace' do
expect(kubeclient).to receive(:get_network_policies).with(namespace: environment.deployment_namespace) { [policy.generate] }
expect(kubeclient).to receive(:get_cilium_network_policies).with(namespace: environment.deployment_namespace) { [cilium_policy.generate] }
expect(kubeclient).to receive(:get_network_policies).with(namespace: cluster_kubernetes_namespace.namespace) { [policy.generate] }
expect(kubeclient).to receive(:get_cilium_network_policies).with(namespace: cluster_kubernetes_namespace.namespace) { [cilium_policy.generate] }
expect(subject).to be_success
expect(subject.payload.count).to eq(2)
expect(subject.payload.first.as_json).to eq(policy.as_json)
......@@ -57,6 +65,52 @@ RSpec.describe NetworkPolicies::ResourcesService do
expect(subject).to be_error
expect(subject.http_status).to eq(:bad_request)
expect(subject.message).not_to be_nil
expect(subject.payload).to be_empty
end
end
context 'without environment_id' do
let(:environment_id) { nil }
let(:cluster_2) { create(:cluster, :project) }
let!(:cluster_kubernetes_namespace_2) { create(:cluster_kubernetes_namespace, project: project, cluster: cluster_2, environment: environment, namespace: 'namespace_2') }
let(:policy_2) do
Gitlab::Kubernetes::NetworkPolicy.new(
name: 'policy_2',
namespace: 'another_2',
selector: { matchLabels: { role: 'db' } },
ingress: [{ from: [{ namespaceSelector: { matchLabels: { project: 'myproject' } } }] }]
)
end
it 'returns success response with policies from two deployment namespaces', :aggregate_failures do
expect(kubeclient).to receive(:get_network_policies).with(namespace: cluster_kubernetes_namespace.namespace) { [policy.generate] }
expect(kubeclient).to receive(:get_cilium_network_policies).with(namespace: cluster_kubernetes_namespace.namespace) { [cilium_policy.generate] }
expect(kubeclient).to receive(:get_network_policies).with(namespace: cluster_kubernetes_namespace_2.namespace) { [policy_2.generate] }
expect(kubeclient).to receive(:get_cilium_network_policies).with(namespace: cluster_kubernetes_namespace_2.namespace) { [] }
expect(subject).to be_success
expect(subject.payload.count).to eq(3)
expect(subject.payload.map(&:as_json)).to include(policy.as_json, policy_2.as_json)
end
context 'with a partial successful response' do
let(:error_message) { 'system failure' }
before do
allow(kubeclient).to receive(:get_network_policies).with(namespace: cluster_kubernetes_namespace.namespace).and_return([policy.generate])
allow(kubeclient).to receive(:get_cilium_network_policies).with(namespace: cluster_kubernetes_namespace.namespace) { [] }
allow(kubeclient).to receive(:get_network_policies).with(namespace: cluster_kubernetes_namespace_2.namespace).and_raise(Kubeclient::HttpError.new(500, error_message, nil))
end
it 'returns error response for the platforms with failures' do
expect(subject).to be_error
expect(subject.message).to match(error_message)
end
it 'returns error response with the policies for all successful platforms' do
expect(subject).to be_error
expect(subject.payload.count).to eq(1)
expect(subject.payload.first.as_json).to eq(policy.as_json)
end
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