Commit 330eac18 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 3359a5a5
...@@ -7,23 +7,34 @@ class Projects::Environments::PrometheusApiController < Projects::ApplicationCon ...@@ -7,23 +7,34 @@ class Projects::Environments::PrometheusApiController < Projects::ApplicationCon
before_action :environment before_action :environment
def proxy def proxy
result = Prometheus::ProxyService.new( variable_substitution_result =
variable_substitution_service.new(environment, permit_params).execute
if variable_substitution_result[:status] == :error
return error_response(variable_substitution_result)
end
prometheus_result = Prometheus::ProxyService.new(
environment, environment,
proxy_method, proxy_method,
proxy_path, proxy_path,
proxy_params variable_substitution_result[:params]
).execute ).execute
return continue_polling_response if result.nil? return continue_polling_response if prometheus_result.nil?
return error_response(result) if result[:status] == :error return error_response(prometheus_result) if prometheus_result[:status] == :error
success_response(result) success_response(prometheus_result)
end end
private private
def query_context def variable_substitution_service
Gitlab::Prometheus::QueryVariables.call(environment) Prometheus::ProxyVariableSubstitutionService
end
def permit_params
params.permit!
end end
def environment def environment
...@@ -37,15 +48,4 @@ class Projects::Environments::PrometheusApiController < Projects::ApplicationCon ...@@ -37,15 +48,4 @@ class Projects::Environments::PrometheusApiController < Projects::ApplicationCon
def proxy_path def proxy_path
params[:proxy_path] params[:proxy_path]
end end
def proxy_params
substitute_query_variables(params).permit!
end
def substitute_query_variables(params)
query = params[:query]
return params unless query
params.merge(query: query % query_context)
end
end end
# frozen_string_literal: true
module Prometheus
class ProxyVariableSubstitutionService < BaseService
include Stepable
steps :add_params_to_result, :substitute_ruby_variables
def initialize(environment, params = {})
@environment, @params = environment, params.deep_dup
end
def execute
execute_steps
end
private
def add_params_to_result(result)
result[:params] = params
success(result)
end
def substitute_ruby_variables(result)
return success(result) unless query
# The % operator doesn't replace variables if the hash contains string
# keys.
result[:params][:query] = query % predefined_context.symbolize_keys
success(result)
rescue TypeError, ArgumentError => exception
log_error(exception.message)
Gitlab::Sentry.track_acceptable_exception(exception, extra: {
template_string: query,
variables: predefined_context
})
error(_('Malformed string'))
end
def predefined_context
@predefined_context ||= Gitlab::Prometheus::QueryVariables.call(@environment)
end
def query
params[:query]
end
end
end
...@@ -313,6 +313,17 @@ The following table shows the possible return codes for API requests. ...@@ -313,6 +313,17 @@ The following table shows the possible return codes for API requests.
## Pagination ## Pagination
We support two kinds of pagination methods:
- Offset-based pagination. This is the default method and available on all endpoints.
- Keyset-based pagination. Added to selected endpoints but being
[progressively rolled out](https://gitlab.com/groups/gitlab-org/-/epics/2039).
For large collections, we recommend keyset pagination (when available) over offset
pagination for performance reasons.
### Offset-based pagination
Sometimes the returned result will span across many pages. When listing Sometimes the returned result will span across many pages. When listing
resources you can pass the following parameters: resources you can pass the following parameters:
...@@ -324,10 +335,10 @@ resources you can pass the following parameters: ...@@ -324,10 +335,10 @@ resources you can pass the following parameters:
In the example below, we list 50 [namespaces](namespaces.md) per page. In the example below, we list 50 [namespaces](namespaces.md) per page.
```bash ```bash
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/namespaces?per_page=50 curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/namespaces?per_page=50"
``` ```
### Pagination Link header #### Pagination Link header
[Link headers](https://www.w3.org/wiki/LinkHeader) are sent back with each [Link headers](https://www.w3.org/wiki/LinkHeader) are sent back with each
response. They have `rel` set to prev/next/first/last and contain the relevant response. They have `rel` set to prev/next/first/last and contain the relevant
...@@ -362,7 +373,7 @@ X-Total: 8 ...@@ -362,7 +373,7 @@ X-Total: 8
X-Total-Pages: 3 X-Total-Pages: 3
``` ```
### Other pagination headers #### Other pagination headers
Additional pagination headers are also sent back. Additional pagination headers are also sent back.
...@@ -383,6 +394,48 @@ and **behind the `api_kaminari_count_with_limit` ...@@ -383,6 +394,48 @@ and **behind the `api_kaminari_count_with_limit`
more than 10,000, the `X-Total` and `X-Total-Pages` headers as well as the more than 10,000, the `X-Total` and `X-Total-Pages` headers as well as the
`rel="last"` `Link` are not present in the response headers. `rel="last"` `Link` are not present in the response headers.
### Keyset-based pagination
Keyset-pagination allows for more efficient retrieval of pages and - in contrast to offset-based pagination - runtime
is independent of the size of the collection.
This method is controlled by the following parameters:
| Parameter | Description |
| ------------ | -------------------------------------- |
| `pagination` | `keyset` (to enable keyset pagination) |
| `per_page` | Number of items to list per page (default: `20`, max: `100`) |
In the example below, we list 50 [projects](projects.md) per page, ordered by `id` ascending.
```bash
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects?pagination=keyset&per_page=50&order_by=id&sort=asc"
```
The response header includes a link to the next page. For example:
```
HTTP/1.1 200 OK
...
Link: <https://gitlab.example.com/api/v4/projects?pagination=keyset&per_page=50&order_by=id&sort=asc&id_after=42>; rel="next"
Status: 200 OK
...
```
The link to the next page contains an additional filter `id_after=42` which excludes records we have retrieved already.
Note the type of filter depends on the `order_by` option used and we may have more than one additional filter.
The `Link` header is absent when the end of the collection has been reached and there are no additional records to retrieve.
We recommend using only the given link to retrieve the next page instead of building your own URL. Apart from the headers shown,
we don't expose additional pagination headers.
Keyset-based pagination is only supported for selected resources and ordering options:
| Resource | Order |
| ------------------------- | -------------------------- |
| [Projects](projects.md) | `order_by=id` only |
## Namespaced path encoding ## Namespaced path encoding
If using namespaced API calls, make sure that the `NAMESPACE/PROJECT_PATH` is If using namespaced API calls, make sure that the `NAMESPACE/PROJECT_PATH` is
......
...@@ -61,6 +61,9 @@ GET /projects ...@@ -61,6 +61,9 @@ GET /projects
| `id_after` | integer | no | Limit results to projects with IDs greater than the specified ID | | `id_after` | integer | no | Limit results to projects with IDs greater than the specified ID |
| `id_before` | integer | no | Limit results to projects with IDs less than the specified ID | | `id_before` | integer | no | Limit results to projects with IDs less than the specified ID |
NOTE: **Note:**
This endpoint supports [keyset pagination](README.md#keyset-based-pagination) for selected `order_by` options.
When `simple=true` or the user is unauthenticated this returns something like: When `simple=true` or the user is unauthenticated this returns something like:
```json ```json
...@@ -309,6 +312,9 @@ GET /users/:user_id/projects ...@@ -309,6 +312,9 @@ GET /users/:user_id/projects
| `id_after` | integer | no | Limit results to projects with IDs greater than the specified ID | | `id_after` | integer | no | Limit results to projects with IDs greater than the specified ID |
| `id_before` | integer | no | Limit results to projects with IDs less than the specified ID | | `id_before` | integer | no | Limit results to projects with IDs less than the specified ID |
NOTE: **Note:**
This endpoint supports [keyset pagination](README.md#keyset-based-pagination) for selected `order_by` options.
```json ```json
[ [
{ {
......
...@@ -34,13 +34,17 @@ pipelines that are run from an [external integration](../user/project/integratio ...@@ -34,13 +34,17 @@ pipelines that are run from an [external integration](../user/project/integratio
## Per-project user setting ## Per-project user setting
The setting to enable or disable GitLab CI/CD Pipelines can be found in your project in To enable or disable GitLab CI/CD Pipelines in your project:
**Settings > General > Visibility, project features, permissions**. If the project
visibility is set to:
- **Private**, only project members can access pipelines. 1. Navigate to **Settings > General > Visibility, project features, permissions**.
- **Internal** or **Public**, pipelines can be made accessible to either 1. Expand the **Repository** section
project members only or everyone with access. 1. Enable or disable the **Pipelines** checkbox as required.
**Project visibility** will also affect pipeline visibility. If set to:
- **Private**: Only project members can access pipelines.
- **Internal** or **Public**: Pipelines can be set to either **Only Project Members**
or **Everyone With Access** via the drop-down box.
Press **Save changes** for the settings to take effect. Press **Save changes** for the settings to take effect.
......
...@@ -267,13 +267,19 @@ This feature: ...@@ -267,13 +267,19 @@ This feature:
kubectl -n gitlab-managed-apps exec -it $(kubectl get pods -n gitlab-managed-apps | grep 'ingress-controller' | awk '{print $1}') -- tail -f /var/log/modsec/audit.log kubectl -n gitlab-managed-apps exec -it $(kubectl get pods -n gitlab-managed-apps | grep 'ingress-controller' | awk '{print $1}') -- tail -f /var/log/modsec/audit.log
``` ```
There is a small performance overhead by enabling `modsecurity`. However, if this is There is a small performance overhead by enabling `modsecurity`. If this is
considered significant for your application, you can toggle the feature flag back to considered significant for your application, you can either:
false by running the following command within the Rails console:
- Disable ModSecurity's rule engine for your deployed application by setting
```ruby [the deployment variable](../../topics/autodevops/index.md)
Feature.disable(:ingress_modsecurity) `AUTO_DEVOPS_MODSECURITY_SEC_RULE_ENGINE` to `Off`. This will prevent ModSecurity from
``` processing any requests for the given application or environment.
- Toggle the feature flag to false by running the following command within your
instance's Rails console:
```ruby
Feature.disable(:ingress_modsecurity)
```
Once disabled, you must [uninstall](#uninstalling-applications) and reinstall your Ingress Once disabled, you must [uninstall](#uninstalling-applications) and reinstall your Ingress
application for the changes to take effect. application for the changes to take effect.
......
...@@ -149,6 +149,9 @@ Pipeline visibility is determined by: ...@@ -149,6 +149,9 @@ Pipeline visibility is determined by:
- Your current [user access level](../../permissions.md). - Your current [user access level](../../permissions.md).
- The **Public pipelines** project setting under your project's **Settings > CI/CD > General pipelines**. - The **Public pipelines** project setting under your project's **Settings > CI/CD > General pipelines**.
NOTE: **Note:**
If the project visibility is set to **Private**, the [**Public pipelines** setting will have no effect](../../../ci/enable_or_disable_ci.md#per-project-user-setting).
This also determines the visibility of these related features: This also determines the visibility of these related features:
- Job output logs - Job output logs
......
...@@ -156,6 +156,7 @@ module API ...@@ -156,6 +156,7 @@ module API
mount ::API::ProtectedTags mount ::API::ProtectedTags
mount ::API::Releases mount ::API::Releases
mount ::API::Release::Links mount ::API::Release::Links
mount ::API::RemoteMirrors
mount ::API::Repositories mount ::API::Repositories
mount ::API::Runner mount ::API::Runner
mount ::API::Runners mount ::API::Runners
......
...@@ -166,6 +166,18 @@ module API ...@@ -166,6 +166,18 @@ module API
end end
end end
class RemoteMirror < Grape::Entity
expose :id
expose :enabled
expose :safe_url, as: :url
expose :update_status
expose :last_update_at
expose :last_update_started_at
expose :last_successful_update_at
expose :last_error
expose :only_protected_branches
end
class ProjectImportStatus < ProjectIdentity class ProjectImportStatus < ProjectIdentity
expose :import_status expose :import_status
......
# frozen_string_literal: true
module API
class RemoteMirrors < Grape::API
include PaginationParams
before do
# TODO: Remove flag: https://gitlab.com/gitlab-org/gitlab/issues/38121
not_found! unless Feature.enabled?(:remote_mirrors_api, user_project)
end
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc "List the project's remote mirrors" do
success Entities::RemoteMirror
end
params do
use :pagination
end
get ':id/remote_mirrors' do
unauthorized! unless can?(current_user, :admin_remote_mirror, user_project)
present paginate(user_project.remote_mirrors),
with: Entities::RemoteMirror
end
end
end
end
...@@ -10614,6 +10614,9 @@ msgstr "" ...@@ -10614,6 +10614,9 @@ msgstr ""
msgid "Makes this issue confidential." msgid "Makes this issue confidential."
msgstr "" msgstr ""
msgid "Malformed string"
msgstr ""
msgid "Manage" msgid "Manage"
msgstr "" msgstr ""
......
...@@ -26,9 +26,11 @@ module QA ...@@ -26,9 +26,11 @@ module QA
merge_request.visit! merge_request.visit!
expect(page).to have_text('to be squashed')
Page::MergeRequest::Show.perform do |merge_request_page| Page::MergeRequest::Show.perform do |merge_request_page|
merge_request_page.retry_on_exception(reload: true) do
expect(merge_request_page).to have_text('to be squashed')
end
merge_request_page.mark_to_squash merge_request_page.mark_to_squash
merge_request_page.merge! merge_request_page.merge!
......
...@@ -63,9 +63,7 @@ describe Projects::Environments::PrometheusApiController do ...@@ -63,9 +63,7 @@ describe Projects::Environments::PrometheusApiController do
context 'with nil query' do context 'with nil query' do
let(:params_without_query) do let(:params_without_query) do
params = environment_params environment_params.except(:query)
params.delete(:query)
params
end end
before do before do
......
{
"type": "object",
"required": [
"id",
"enabled",
"url",
"update_status",
"last_update_at",
"last_update_started_at",
"last_successful_update_at",
"last_error",
"only_protected_branches"
],
"properties": {
"id": { "type": "integer" },
"enabled": { "type": "boolean" },
"url": { "type": "string" },
"update_status": { "type": "string" },
"last_update_at": { "type": ["string", "null"] },
"last_update_started_at": { "type": ["string", "null"] },
"last_successful_update_at": { "type": ["string", "null"] },
"last_error": { "type": ["string", "null"] },
"only_protected_branches": { "type": "boolean" }
},
"additionalProperties": false
}
{
"type": "array",
"items": { "$ref": "remote_mirror.json" }
}
...@@ -14,7 +14,7 @@ describe Gitlab::Prometheus::QueryVariables do ...@@ -14,7 +14,7 @@ describe Gitlab::Prometheus::QueryVariables do
it do it do
is_expected.to include(environment_filter: is_expected.to include(environment_filter:
%{container_name!="POD",environment="#{slug}"}) %Q[container_name!="POD",environment="#{slug}"])
end end
context 'without deployment platform' do context 'without deployment platform' do
......
# frozen_string_literal: true
require 'spec_helper'
describe API::RemoteMirrors do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository, :remote_mirror) }
describe 'GET /projects/:id/remote_mirrors' do
let(:route) { "/projects/#{project.id}/remote_mirrors" }
it 'requires `admin_remote_mirror` permission' do
project.add_developer(user)
get api(route, user)
expect(response).to have_gitlab_http_status(:unauthorized)
end
it 'returns a list of remote mirrors' do
project.add_maintainer(user)
get api(route, user)
expect(response).to have_gitlab_http_status(:success)
expect(response).to match_response_schema('remote_mirrors')
end
context 'with the `remote_mirrors_api` feature disabled' do
before do
stub_feature_flags(remote_mirrors_api: false)
end
it 'responds with `not_found`' do
get api(route, user)
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Prometheus::ProxyVariableSubstitutionService do
describe '#execute' do
let_it_be(:environment) { create(:environment) }
let(:params_keys) { { query: 'up{environment="%{ci_environment_slug}"}' } }
let(:params) { ActionController::Parameters.new(params_keys).permit! }
let(:result) { subject.execute }
subject { described_class.new(environment, params) }
shared_examples 'success' do
it 'replaces variables with values' do
expect(result[:status]).to eq(:success)
expect(result[:params][:query]).to eq(expected_query)
end
end
shared_examples 'error' do |message|
it 'returns error' do
expect(result[:status]).to eq(:error)
expect(result[:message]).to eq(message)
end
end
context 'does not alter params passed to the service' do
it do
subject.execute
expect(params).to eq(
ActionController::Parameters.new(
query: 'up{environment="%{ci_environment_slug}"}'
).permit!
)
end
end
context 'with predefined variables' do
it_behaves_like 'success' do
let(:expected_query) { %Q[up{environment="#{environment.slug}"}] }
end
context 'with nil query' do
let(:params_keys) { {} }
it_behaves_like 'success' do
let(:expected_query) { nil }
end
end
end
context 'ruby template rendering' do
let(:params_keys) do
{ query: 'up{env=%{ci_environment_slug},%{environment_filter}}' }
end
it_behaves_like 'success' do
let(:expected_query) do
"up{env=#{environment.slug},container_name!=\"POD\"," \
"environment=\"#{environment.slug}\"}"
end
end
context 'with multiple occurrences of variable in string' do
let(:params_keys) do
{ query: 'up{env1=%{ci_environment_slug},env2=%{ci_environment_slug}}' }
end
it_behaves_like 'success' do
let(:expected_query) { "up{env1=#{environment.slug},env2=#{environment.slug}}" }
end
end
context 'with multiple variables in string' do
let(:params_keys) do
{ query: 'up{env=%{ci_environment_slug},%{environment_filter}}' }
end
it_behaves_like 'success' do
let(:expected_query) do
"up{env=#{environment.slug}," \
"container_name!=\"POD\",environment=\"#{environment.slug}\"}"
end
end
end
context 'with unknown variables in string' do
let(:params_keys) { { query: 'up{env=%{env_slug}}' } }
it_behaves_like 'success' do
let(:expected_query) { 'up{env=%{env_slug}}' }
end
end
# This spec is needed if there are multiple keys in the context provided
# by `Gitlab::Prometheus::QueryVariables.call(environment)` which is
# passed to the Ruby `%` operator.
# If the number of keys in the context is one, there is no need for
# this spec.
context 'with extra variables in context' do
let(:params_keys) { { query: 'up{env=%{ci_environment_slug}}' } }
it_behaves_like 'success' do
let(:expected_query) { "up{env=#{environment.slug}}" }
end
it 'has more than one variable in context' do
expect(Gitlab::Prometheus::QueryVariables.call(environment).size).to be > 1
end
end
# The ruby % operator will not replace known variables if there are unknown
# variables also in the string. It doesn't raise an error
# (though the `sprintf` and `format` methods do).
context 'with unknown and known variables in string' do
let(:params_keys) do
{ query: 'up{env=%{ci_environment_slug},other_env=%{env_slug}}' }
end
it_behaves_like 'success' do
let(:expected_query) { 'up{env=%{ci_environment_slug},other_env=%{env_slug}}' }
end
end
context 'when rendering raises error' do
context 'when TypeError is raised' do
let(:params_keys) { { query: '{% a %}' } }
it_behaves_like 'error', 'Malformed string'
end
context 'when ArgumentError is raised' do
let(:params_keys) { { query: '%<' } }
it_behaves_like 'error', 'Malformed string'
end
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