Commit a48db27a authored by Alan (Maciej) Paruszewski's avatar Alan (Maciej) Paruszewski Committed by Douglas Barbosa Alexandre

Add networkPolicies to GraphQL API for project

parent 234694b9
......@@ -5730,6 +5730,29 @@ The edge type for [`Namespace`](#namespace).
| <a id="namespaceedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="namespaceedgenode"></a>`node` | [`Namespace`](#namespace) | The item at the end of the edge. |
#### `NetworkPolicyConnection`
The connection type for [`NetworkPolicy`](#networkpolicy).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="networkpolicyconnectionedges"></a>`edges` | [`[NetworkPolicyEdge]`](#networkpolicyedge) | A list of edges. |
| <a id="networkpolicyconnectionnodes"></a>`nodes` | [`[NetworkPolicy]`](#networkpolicy) | A list of nodes. |
| <a id="networkpolicyconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
#### `NetworkPolicyEdge`
The edge type for [`NetworkPolicy`](#networkpolicy).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="networkpolicyedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="networkpolicyedgenode"></a>`node` | [`NetworkPolicy`](#networkpolicy) | The item at the end of the edge. |
#### `NoteConnection`
The connection type for [`Note`](#note).
......@@ -10617,6 +10640,21 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="namespaceprojectssearch"></a>`search` | [`String`](#string) | Search project with most similar names or paths. |
| <a id="namespaceprojectssort"></a>`sort` | [`NamespaceProjectSort`](#namespaceprojectsort) | Sort projects by this criteria. |
### `NetworkPolicy`
Represents the network policy.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="networkpolicyenabled"></a>`enabled` | [`Boolean!`](#boolean) | Indicates whether this policy is enabled. |
| <a id="networkpolicyfromautodevops"></a>`fromAutoDevops` | [`Boolean!`](#boolean) | Indicates whether this policy is created from AutoDevops. |
| <a id="networkpolicyname"></a>`name` | [`String!`](#string) | Name of the policy. |
| <a id="networkpolicynamespace"></a>`namespace` | [`String!`](#string) | Namespace of the policy. |
| <a id="networkpolicyupdatedat"></a>`updatedAt` | [`Time!`](#time) | Timestamp of when the policy YAML was last updated. |
| <a id="networkpolicyyaml"></a>`yaml` | [`String!`](#string) | YAML definition of the policy. |
### `Note`
#### Fields
......@@ -11593,6 +11631,22 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="projectmilestonestimeframe"></a>`timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. |
| <a id="projectmilestonestitle"></a>`title` | [`String`](#string) | The title of the milestone. |
##### `Project.networkPolicies`
Network Policies of the project.
Returns [`NetworkPolicyConnection`](#networkpolicyconnection).
This field returns a [connection](#connections). It accepts the
four standard [pagination arguments](#connection-pagination-arguments):
`before: String`, `after: String`, `first: Int`, `last: Int`.
###### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="projectnetworkpoliciesenvironmentid"></a>`environmentId` | [`EnvironmentID`](#environmentid) | The global ID of the environment to filter policies. |
##### `Project.packages`
Packages of the project.
......
......@@ -169,6 +169,12 @@ module EE
null: true,
description: 'Scan Execution Policies of the project',
resolver: ::Resolvers::ScanExecutionPolicyResolver
field :network_policies,
::Types::NetworkPolicyType.connection_type,
null: true,
description: 'Network Policies of the project',
resolver: ::Resolvers::NetworkPolicyResolver
end
def api_fuzzing_ci_configuration
......
# frozen_string_literal: true
module Resolvers
class NetworkPolicyResolver < BaseResolver
include Gitlab::Graphql::Authorize::AuthorizeResource
type Types::NetworkPolicyType, null: true
authorize :read_threat_monitoring
argument :environment_id,
::Types::GlobalIDType[::Environment],
required: false,
description: 'The global ID of the environment to filter policies.'
alias_method :project, :object
def resolve(**args)
authorize!(project)
result = NetworkPolicies::ResourcesService.new(project: project, environment_id: resolve_gid(args[:environment_id])).execute
raise Gitlab::Graphql::Errors::BaseError, result.message unless result.success?
result.payload.map do |policy|
policy_json = policy.as_json
{
name: policy_json[:name],
namespace: policy_json[:namespace],
updated_at: policy_json[:creation_timestamp],
yaml: policy_json[:manifest],
from_auto_devops: policy_json[:is_autodevops],
enabled: policy_json[:is_enabled]
}
end
end
def resolve_gid(environment_id)
return if environment_id.blank?
# TODO: remove this line when the compatibility layer is removed
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
::Types::GlobalIDType[::Environment]
.coerce_isolated_input(environment_id)
.model_id
end
end
end
# frozen_string_literal: true
module Types
# rubocop: disable Graphql/AuthorizeTypes
class NetworkPolicyType < BaseObject
graphql_name 'NetworkPolicy'
description 'Represents the network policy'
field :name,
GraphQL::STRING_TYPE,
null: false,
description: 'Name of the policy.'
field :namespace,
GraphQL::STRING_TYPE,
null: false,
description: 'Namespace of the policy.'
field :enabled,
GraphQL::BOOLEAN_TYPE,
null: false,
description: 'Indicates whether this policy is enabled.'
field :from_auto_devops,
GraphQL::BOOLEAN_TYPE,
null: false,
description: 'Indicates whether this policy is created from AutoDevops.'
field :yaml,
GraphQL::STRING_TYPE,
null: false,
description: 'YAML definition of the policy.'
field :updated_at,
Types::TimeType,
null: false,
description: 'Timestamp of when the policy YAML was last updated.'
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Resolvers::NetworkPolicyResolver do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
let(:user) { project.owner }
let(:time_now) { Time.current }
let(:policy) do
Gitlab::Kubernetes::NetworkPolicy.new(
name: 'policy',
namespace: 'another',
creation_timestamp: time_now,
selector: { matchLabels: { role: 'db' } },
ingress: [{ from: [{ namespaceSelector: { matchLabels: { project: 'myproject' } } }] }]
)
end
let(:cilium_policy) do
Gitlab::Kubernetes::CiliumNetworkPolicy.new(
name: 'cilium_policy',
namespace: 'another',
creation_timestamp: time_now,
resource_version: '102',
selector: { matchLabels: { role: 'db' } },
ingress: [{ endpointFrom: [{ matchLabels: { project: 'myproject' } }] }]
)
end
specify do
expect(described_class).to have_nullable_graphql_type(Types::NetworkPolicyType)
end
describe '#resolve' do
subject(:resolve_network_policies) { resolve(described_class, obj: project, args: { environment_id: environment_id }, ctx: { current_user: user }) }
let(:service_result) { instance_double(ServiceResponse, success?: true, payload: [policy, cilium_policy]) }
let(:environment_id) { nil }
before do
allow_next_instance_of(NetworkPolicies::ResourcesService) do |resources_service|
allow(resources_service).to receive(:execute).and_return(service_result)
end
end
context 'when feature is not licensed' do
before do
stub_licensed_features(threat_monitoring: false)
end
it 'raises ResourceNotAvailable error' do
expect { resolve_network_policies }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when feature is licensed' do
before do
stub_licensed_features(threat_monitoring: true)
end
context 'when NetworkPolicies::ResourcesService is not executed successfully' do
let(:service_result) { instance_double(ServiceResponse, success?: false, message: 'Error fetching the result') }
it 'raises Gitlab::Graphql::Errors::BaseError' do
expect { resolve_network_policies }.to raise_error(Gitlab::Graphql::Errors::BaseError, 'Error fetching the result')
end
end
context 'when NetworkPolicies::ResourcesService is executed successfully' do
context 'when environment_id is not provided' do
it 'uses NetworkPolicies::ResourceService without environment_id to fetch policies' do
expect(NetworkPolicies::ResourcesService).to receive(:new).with(project: project, environment_id: nil)
resolve_network_policies
end
end
context 'when environment_id is provided' do
let(:environment_id) { 'gid://gitlab/Environment/31' }
it 'uses NetworkPolicies::ResourceService with resolved environment_id to fetch policies' do
expect(NetworkPolicies::ResourcesService).to receive(:new).with(project: project, environment_id: '31')
resolve_network_policies
end
end
it 'returns scan execution policies' do
expected_resolved = [
{
name: 'policy',
namespace: 'another',
enabled: true,
yaml: policy.as_json[:manifest],
updated_at: time_now,
from_auto_devops: false
},
{
name: 'cilium_policy',
namespace: 'another',
enabled: true,
yaml: cilium_policy.as_json[:manifest],
updated_at: time_now,
from_auto_devops: false
}
]
expect(resolve_network_policies).to eq(expected_resolved)
end
end
context 'when user is unauthorized' do
let(:user) { create(:user) }
it 'raises ResourceNotAvailable error' do
expect { resolve_network_policies }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['NetworkPolicy'] do
it { expect(described_class.graphql_name).to eq('NetworkPolicy') }
it 'has the expected fields' do
expect(described_class).to have_graphql_fields(
:name,
:namespace,
:enabled,
:from_auto_devops,
:yaml,
:updated_at
)
end
end
......@@ -21,7 +21,7 @@ RSpec.describe GitlabSchema.types['Project'] do
vulnerability_severities_count packages compliance_frameworks vulnerabilities_count_by_day
security_dashboard_path iterations iteration_cadences cluster_agents repository_size_excess actual_repository_size_limit
code_coverage_summary api_fuzzing_ci_configuration path_locks incident_management_escalation_policies
incident_management_escalation_policy scan_execution_policies
incident_management_escalation_policy scan_execution_policies network_policies
]
expect(described_class).to include_graphql_fields(*expected_fields)
......
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