Commit ebe0fbf3 authored by Rajendra Kadam's avatar Rajendra Kadam Committed by Michael Kozono

Add API endpoint for creating a Geo node

Refactor spec to use symbolic HTTP statuses
parent a6d89b00
---
title: Add API endpoint for creating a Geo node
merge_request: 22392
author: Rajendra Kadam
type: added
......@@ -3,6 +3,54 @@
In order to interact with Geo node endpoints, you need to authenticate yourself
as an admin.
## Create a new Geo node
Creates a new Geo node.
```
POST /geo_nodes
```
| Attribute | Type | Required | Description |
| ----------------------------| ------- | -------- | -----------------------------------------------------------------|
| `primary` | boolean | no | Specifying whether this node will be primary. Defaults to false. |
| `enabled` | boolean | no | Flag indicating if the Geo node is enabled. Defaults to true. |
| `name` | string | yes | The unique identifier for the Geo node. Must match `geo_node_name` if it is set in `gitlab.rb`, otherwise it must match `external_url` |
| `url` | string | yes | The user-facing URL for the Geo node. |
| `internal_url` | string | no | The URL defined on the primary node that secondary nodes should use to contact it. Returns `url` if not set. |
| `files_max_capacity` | integer | no | Control the maximum concurrency of LFS/attachment backfill for this secondary node. Defaults to 10. |
| `repos_max_capacity` | integer | no | Control the maximum concurrency of repository backfill for this secondary node. Defaults to 25. |
| `verification_max_capacity` | integer | no | Control the maximum concurrency of repository verification for this node. Defaults to 100. |
| `container_repositories_max_capacity` | integer | no | Control the maximum concurrency of container repository sync for this node. Defaults to 10. |
| `sync_object_storage` | boolean | no | Flag indicating if the secondary Geo node will replicate blobs in Object Storage. Defaults to false. |
Example response:
```json
{
"id": 3,
"name": "Test Node 1",
"url": "https://secondary.example.com/",
"internal_url": "https://secondary.example.com/",
"primary": false,
"enabled": true,
"current": false,
"files_max_capacity": 10,
"repos_max_capacity": 25,
"verification_max_capacity": 100,
"container_repositories_max_capacity": 10,
"sync_object_storage": false,
"clone_protocol": "http",
"web_edit_url": "https://primary.example.com/admin/geo/nodes/3/edit",
"web_geo_projects_url": "http://secondary.example.com/admin/geo/projects",
"_links": {
"self": "https://primary.example.com/api/v4/geo_nodes/3",
"status": "https://primary.example.com/api/v4/geo_nodes/3/status",
"repair": "https://primary.example.com/api/v4/geo_nodes/3/repair"
}
}
```
## Retrieve configuration about all Geo nodes
```
......
......@@ -9,6 +9,37 @@ module API
before { authenticated_as_admin! }
resource :geo_nodes do
# Add a new Geo node
#
# Example request:
# POST /geo_nodes
desc 'Create a new Geo node' do
success EE::API::Entities::GeoNode
end
params do
requires :primary, type: Boolean, desc: 'Specifying whether this node will be primary. Defaults to false.'
optional :enabled, type: Boolean, desc: 'Specifying whether this node will be enabled. Defaults to true.'
requires :name, type: String, desc: 'The unique identifier for the Geo node. Must match `geo_node_name` if it is set in `gitlab.rb`, otherwise it must match `external_url`'
requires :url, type: String, desc: 'The user-facing URL for the Geo node'
requires :internal_url, type: String, desc: 'The URL defined on the primary node that secondary nodes should use to contact it. Returns `url` if not set.'
optional :files_max_capacity, type: Integer, desc: 'Control the maximum concurrency of LFS/attachment backfill for this secondary node. Defaults to 10.'
optional :repos_max_capacity, type: Integer, desc: 'Control the maximum concurrency of repository backfill for this secondary node. Defaults to 25.'
optional :verification_max_capacity, type: Integer, desc: 'Control the maximum concurrency of repository verification for this node. Defaults to 100.'
optional :container_repositories_max_capacity, type: Integer, desc: 'Control the maximum concurrency of container repository sync for this node. Defaults to 10.'
optional :sync_object_storage, type: Boolean, desc: 'Flag indicating if the secondary Geo node will replicate blobs in Object Storage. Defaults to false.'
end
post do
create_params = declared_params(include_missing: false)
new_geo_node = ::Geo::NodeCreateService.new(create_params).execute
if new_geo_node.persisted?
present new_geo_node, with: EE::API::Entities::GeoNode
else
render_validation_error!(new_geo_node)
end
end
# Get all Geo node information
#
# Example request:
......@@ -142,7 +173,7 @@ module API
optional :enabled, type: Boolean, desc: 'Flag indicating if the Geo node is enabled'
optional :name, type: String, desc: 'The unique identifier for the Geo node. Must match `geo_node_name` if it is set in gitlab.rb, otherwise it must match `external_url`'
optional :url, type: String, desc: 'The user-facing URL of the Geo node'
optional :internal_url, type: String, desc: 'The URL defined on the primary node that secondary nodes should use to contact it. Defaults to url'
optional :internal_url, type: String, desc: 'The URL defined on the primary node that secondary nodes should use to contact it. Returns `url` if not set.'
optional :files_max_capacity, type: Integer, desc: 'Control the maximum concurrency of LFS/attachment backfill for this secondary node'
optional :repos_max_capacity, type: Integer, desc: 'Control the maximum concurrency of repository backfill for this secondary node'
optional :verification_max_capacity, type: Integer, desc: 'Control the maximum concurrency of repository verification for this node'
......
......@@ -17,18 +17,42 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
set(:admin) { create(:admin) }
set(:user) { create(:user) }
describe 'POST /geo_nodes' do
it 'denies access if not admin' do
post api('/geo_nodes', user), params: {}
expect(response).to have_gitlab_http_status(:forbidden)
end
it 'returns rendering error if params are missing' do
post api('/geo_nodes', admin), params: {}
expect(response).to have_gitlab_http_status(:bad_request)
end
it 'delegates the creation of the Geo node to Geo::NodeCreateService' do
geo_node_params = {
name: 'Test Node 1',
url: 'http://example.com',
internal_url: 'http://internal.example.com',
primary: false
}
expect_any_instance_of(Geo::NodeCreateService).to receive(:execute).once.and_call_original
post api('/geo_nodes', admin), params: geo_node_params
expect(response).to have_gitlab_http_status(:created)
end
end
describe 'GET /geo_nodes' do
it 'retrieves the Geo nodes if admin is logged in' do
get api("/geo_nodes", admin)
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/geo_nodes', dir: 'ee')
end
it 'denies access if not admin' do
get api('/geo_nodes', user)
expect(response).to have_gitlab_http_status(403)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
......@@ -36,7 +60,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
it 'retrieves the Geo nodes if admin is logged in' do
get api("/geo_nodes/#{primary.id}", admin)
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/geo_node', dir: 'ee')
expect(json_response['web_edit_url']).to end_with("/admin/geo/nodes/#{primary.id}/edit")
......@@ -53,7 +77,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
it 'denies access if not admin' do
get api('/geo_nodes', user)
expect(response).to have_gitlab_http_status(403)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
......@@ -63,7 +87,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
get api("/geo_nodes/status", admin)
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/geo_node_statuses', dir: 'ee')
expect(json_response.size).to eq(2)
end
......@@ -71,7 +95,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
it 'returns only one record if only one record exists' do
get api("/geo_nodes/status", admin)
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/geo_node_statuses', dir: 'ee')
expect(json_response.size).to eq(1)
end
......@@ -79,7 +103,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
it 'denies access if not admin' do
get api('/geo_nodes', user)
expect(response).to have_gitlab_http_status(403)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
......@@ -92,7 +116,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
get api("/geo_nodes/#{secondary.id}/status", admin)
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/geo_node_status', dir: 'ee')
expect(json_response['version']).to eq('secondary-version')
......@@ -112,7 +136,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
get api("/geo_nodes/#{secondary.id}/status", admin)
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/geo_node_status', dir: 'ee')
end
......@@ -124,7 +148,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
get api("/geo_nodes/#{secondary.id}/status", admin)
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/geo_node_status', dir: 'ee')
end
......@@ -137,7 +161,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
get api("/geo_nodes/#{secondary.id}/status", admin)
expect(response).to have_gitlab_http_status(404)
expect(response).to have_gitlab_http_status(:not_found)
end
it 'the primary shows 404 response if secondary node status does not exist in database yet' do
......@@ -148,7 +172,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
get api("/geo_nodes/#{secondary.id}/status", admin)
expect(response).to have_gitlab_http_status(404)
expect(response).to have_gitlab_http_status(:not_found)
end
it_behaves_like '404 response' do
......@@ -158,7 +182,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
it 'denies access if not admin' do
get api("/geo_nodes/#{secondary.id}/status", user)
expect(response).to have_gitlab_http_status(403)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
......@@ -170,7 +194,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
it 'denies access if not admin' do
post api("/geo_nodes/#{secondary.id}/repair", user)
expect(response).to have_gitlab_http_status(403)
expect(response).to have_gitlab_http_status(:forbidden)
end
it 'returns 200 for the primary node' do
......@@ -179,7 +203,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
post api("/geo_nodes/#{primary.id}/repair", admin)
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/geo_node_status', dir: 'ee')
end
......@@ -188,7 +212,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
post api("/geo_nodes/#{secondary.id}/repair", admin)
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/geo_node_status', dir: 'ee')
end
......@@ -197,7 +221,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
post api("/geo_nodes/#{secondary.id}/repair", admin)
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/geo_node_status', dir: 'ee')
end
end
......@@ -210,7 +234,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
it 'denies access if not admin' do
put api("/geo_nodes/#{secondary.id}", user), params: {}
expect(response).to have_gitlab_http_status(403)
expect(response).to have_gitlab_http_status(:forbidden)
end
it 'updates the parameters' do
......@@ -225,7 +249,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
put api("/geo_nodes/#{secondary.id}", admin), params: params
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/geo_node', dir: 'ee')
expect(json_response).to include(params)
end
......@@ -237,7 +261,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
put api("/geo_nodes/#{primary.id}", admin), params: params
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/geo_node', dir: 'ee')
expect(json_response).to include(params)
end
......@@ -249,7 +273,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
put api("/geo_nodes/#{primary.id}", admin), params: params
expect(response).to have_gitlab_http_status(400)
expect(response).to have_gitlab_http_status(:bad_request)
end
end
......@@ -261,21 +285,21 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
it 'denies access if not admin' do
delete api("/geo_nodes/#{secondary.id}", user)
expect(response).to have_gitlab_http_status(403)
expect(response).to have_gitlab_http_status(:forbidden)
end
it 'deletes the node' do
delete api("/geo_nodes/#{secondary.id}", admin)
expect(response).to have_gitlab_http_status(204)
expect(response).to have_gitlab_http_status(:no_content)
end
it 'returns 400 if Geo Node could not be deleted' do
it 'returns 500 if Geo Node could not be deleted' do
allow_any_instance_of(GeoNode).to receive(:destroy!).and_raise(StandardError, 'Something wrong')
delete api("/geo_nodes/#{secondary.id}", admin)
expect(response).to have_gitlab_http_status(500)
expect(response).to have_gitlab_http_status(:internal_server_error)
end
end
......@@ -288,7 +312,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
it 'forbids requests' do
get api("/geo_nodes/current/failures", admin)
expect(response).to have_gitlab_http_status(403)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
......@@ -303,7 +327,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
get api("/geo_nodes/current/failures", admin)
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/geo_project_registry', dir: 'ee')
end
......@@ -312,7 +336,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
get api("/geo_nodes/current/failures", admin)
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.count).to be_zero
end
......@@ -323,7 +347,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
get api("/geo_nodes/current/failures", admin), params: { type: :wiki }
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.count).to eq(1)
expect(json_response.first['wiki_retry_count']).to be > 0
end
......@@ -336,7 +360,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
get api("/geo_nodes/current/failures", admin), params: { type: :repository }
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.count).to eq(1)
expect(json_response.first['repository_retry_count']).to be > 0
end
......@@ -348,14 +372,14 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
get api("/geo_nodes/current/failures", admin), params: { type: :nonexistent }
expect(response).to have_gitlab_http_status(400)
expect(response).to have_gitlab_http_status(:bad_request)
end
end
it 'denies access if not admin' do
get api("/geo_nodes/current/failures", user)
expect(response).to have_gitlab_http_status(403)
expect(response).to have_gitlab_http_status(:forbidden)
end
context 'verification failures' do
......@@ -369,7 +393,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
get api("/geo_nodes/current/failures", admin), params: { failure_type: 'verification' }
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/geo_project_registry', dir: 'ee')
end
......@@ -378,7 +402,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
get api("/geo_nodes/current/failures", admin), params: { failure_type: 'verification' }
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.count).to be_zero
end
......@@ -389,7 +413,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
get api("/geo_nodes/current/failures", admin), params: { failure_type: 'verification', type: :wiki }
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.count).to eq(1)
expect(json_response.first['last_wiki_verification_failure']).to be_present
end
......@@ -402,7 +426,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
get api("/geo_nodes/current/failures", admin), params: { failure_type: 'verification', type: :repository }
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.count).to eq(1)
expect(json_response.first['last_repository_verification_failure']).to be_present
end
......@@ -420,7 +444,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
get api("/geo_nodes/current/failures", admin), params: { failure_type: 'checksum_mismatch' }
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/geo_project_registry', dir: 'ee')
end
......@@ -429,7 +453,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
get api("/geo_nodes/current/failures", admin), params: { failure_type: 'checksum_mismatch' }
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.count).to be_zero
end
......@@ -440,7 +464,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
get api("/geo_nodes/current/failures", admin), params: { failure_type: 'checksum_mismatch', type: :wiki }
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.count).to eq(1)
expect(json_response.first['wiki_checksum_mismatch']).to be_truthy
end
......@@ -453,7 +477,7 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
get api("/geo_nodes/current/failures", admin), params: { failure_type: 'checksum_mismatch', type: :repository }
expect(response).to have_gitlab_http_status(200)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.count).to eq(1)
expect(json_response.first['repository_checksum_mismatch']).to be_truthy
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