Commit 4cb5e501 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 0301a0ca
......@@ -256,6 +256,7 @@ export default class Clusters {
eventHub.$on('uninstallApplication', data => this.uninstallApplication(data));
eventHub.$on('setCrossplaneProviderStack', data => this.setCrossplaneProviderStack(data));
eventHub.$on('setIngressModSecurityEnabled', data => this.setIngressModSecurityEnabled(data));
eventHub.$on('resetIngressModSecurityEnabled', id => this.resetIngressModSecurityEnabled(id));
// Add event listener to all the banner close buttons
this.addBannerCloseHandler(this.unreachableContainer, 'unreachable');
this.addBannerCloseHandler(this.authenticationFailureContainer, 'authentication_failure');
......@@ -270,6 +271,7 @@ export default class Clusters {
eventHub.$off('setCrossplaneProviderStack');
eventHub.$off('uninstallApplication');
eventHub.$off('setIngressModSecurityEnabled');
eventHub.$off('resetIngressModSecurityEnabled');
}
initPolling(method, successCallback, errorCallback) {
......@@ -523,6 +525,10 @@ export default class Clusters {
this.store.updateAppProperty(id, 'modsecurity_enabled', modSecurityEnabled);
}
resetIngressModSecurityEnabled(id) {
this.store.updateAppProperty(id, 'isEditingModSecurityEnabled', false);
}
destroy() {
this.destroyed = true;
......
......@@ -119,9 +119,6 @@ export default {
ingressInstalled() {
return this.applications.ingress.status === APPLICATION_STATUS.INSTALLED;
},
ingressEnableModsecurity() {
return this.applications.ingress.modsecurity_enabled;
},
ingressExternalEndpoint() {
return this.applications.ingress.externalIp || this.applications.ingress.externalHostname;
},
......
<script>
import _ from 'lodash';
import { __ } from '../../locale';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import { APPLICATION_STATUS, INGRESS } from '~/clusters/constants';
import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
import { GlAlert, GlSprintf, GlLink, GlToggle, GlButton } from '@gitlab/ui';
import eventHub from '~/clusters/event_hub';
import modSecurityLogo from 'images/cluster_app_logos/modsecurity.png';
const { UPDATING, UNINSTALLING } = APPLICATION_STATUS;
const { UPDATING, UNINSTALLING, INSTALLING, INSTALLED, UPDATED } = APPLICATION_STATUS;
export default {
title: 'ModSecurity Web Application Firewall',
modsecurityUrl: 'https://modsecurity.org/about.html',
components: {
LoadingButton,
GlAlert,
GlSprintf,
GlLink,
GlToggle,
GlButton,
},
props: {
ingress: {
......@@ -26,6 +29,10 @@ export default {
default: '',
},
},
data: () => ({
modSecurityLogo,
hasValueChanged: false,
}),
computed: {
modSecurityEnabled: {
get() {
......@@ -36,6 +43,11 @@ export default {
id: INGRESS,
modSecurityEnabled: isEnabled,
});
if (this.hasValueChanged) {
this.resetStatus();
} else {
this.hasValueChanged = true;
}
},
},
ingressModSecurityDescription() {
......@@ -45,13 +57,21 @@ export default {
return [UPDATING].includes(this.ingress.status);
},
saveButtonDisabled() {
return [UNINSTALLING, UPDATING].includes(this.ingress.status);
return [UNINSTALLING, UPDATING, INSTALLING].includes(this.ingress.status);
},
saveButtonLabel() {
return this.saving ? __('Saving') : __('Save changes');
},
ingressInstalled() {
return this.ingress.installed;
/**
* Returns true either when:
* - The application is getting updated.
* - The user has changed some of the settings for an application which is
* neither getting installed nor updated.
*/
showButtons() {
return (
this.saving || (this.hasValueChanged && [INSTALLED, UPDATED].includes(this.ingress.status))
);
},
},
methods: {
......@@ -60,6 +80,11 @@ export default {
id: INGRESS,
params: { modsecurity_enabled: this.ingress.modsecurity_enabled },
});
this.resetStatus();
},
resetStatus() {
eventHub.$emit('resetIngressModSecurityEnabled', INGRESS);
this.hasValueChanged = false;
},
},
};
......@@ -75,25 +100,32 @@ export default {
@dismiss="alert = null"
>
{{
s__('ClusterIntegration|Something went wrong while updating the Web Application Firewall.')
s__(
'ClusterIntegration|Something went wrong while trying to save your settings. Please try again.',
)
}}
</gl-alert>
<div class="form-group">
<div class="form-check form-check-inline">
<input
v-model="modSecurityEnabled"
type="checkbox"
autocomplete="off"
class="form-check-input"
<div class="gl-responsive-table-row-layout" role="row">
<div class="table-section append-right-8 section-align-top" role="gridcell">
<img
:src="modSecurityLogo"
:alt="`${$options.title} logo`"
class="cluster-application-logo avatar s40"
/>
<label class="form-check-label label-bold" for="ingress-enable-modsecurity">
{{ s__('ClusterIntegration|Enable Web Application Firewall') }}
</label>
</div>
<div class="table-section section-wrap" role="gridcell">
<strong>
<gl-link :href="$options.modsecurityUrl" target="_blank">{{ $options.title }} </gl-link>
</strong>
<div class="form-group">
<p class="form-text text-muted">
<strong>
<gl-sprintf
:message="s__('ClusterIntegration|Learn more about %{linkStart}ModSecurity%{linkEnd}')"
:message="
s__(
'ClusterIntegration|Real-time web application monitoring, logging and access control. %{linkStart}More information%{linkEnd}',
)
"
>
<template #link="{ content }">
<gl-link :href="ingressModSecurityDescription" target="_blank"
......@@ -103,14 +135,30 @@ export default {
</gl-sprintf>
</strong>
</p>
<loading-button
v-if="ingressInstalled"
class="btn-success mt-1"
<div class="form-check form-check-inline mt-3">
<gl-toggle
v-model="modSecurityEnabled"
:label-on="__('Enabled')"
:label-off="__('Disabled')"
:disabled="saveButtonDisabled"
label-position="right"
/>
</div>
<div v-if="showButtons">
<gl-button
class="btn-success inline mr-1"
:loading="saving"
:disabled="saveButtonDisabled"
:label="saveButtonLabel"
@click="updateApplication"
/>
>
{{ saveButtonLabel }}
</gl-button>
<gl-button :disabled="saveButtonDisabled" @click="resetStatus">
{{ __('Cancel') }}
</gl-button>
</div>
</div>
</div>
</div>
</div>
</template>
......@@ -211,9 +211,7 @@ export default class ClusterStore {
this.state.applications.ingress.externalIp = serverAppEntry.external_ip;
this.state.applications.ingress.externalHostname = serverAppEntry.external_hostname;
if (!this.state.applications.ingress.isEditingModSecurityEnabled) {
this.state.applications.ingress.modsecurity_enabled =
serverAppEntry.modsecurity_enabled ||
this.state.applications.ingress.modsecurity_enabled;
this.state.applications.ingress.modsecurity_enabled = serverAppEntry.modsecurity_enabled;
}
} else if (appId === CERT_MANAGER) {
this.state.applications.cert_manager.email =
......
......@@ -77,6 +77,9 @@ export default {
v-gl-tooltip
title="Jump to next unresolved thread"
class="btn btn-default discussion-next-btn"
data-track-event="click_button"
data-track-label="mr_next_unresolved_thread"
data-track-property="click_next_unresolved_thread_top"
@click="jumpToNextDiscussion"
>
<icon name="comment-next" />
......
......@@ -28,6 +28,9 @@ export default {
v-gl-tooltip
class="btn btn-default discussion-next-btn"
:title="s__('MergeRequests|Jump to next unresolved thread')"
data-track-event="click_button"
data-track-label="mr_next_unresolved_thread"
data-track-property="click_next_unresolved_thread"
@click="jumpToNextRelativeDiscussion(fromDiscussionId)"
>
<icon name="comment-next" />
......
import IntegrationSettingsForm from '~/integrations/integration_settings_form';
import PrometheusMetrics from '~/prometheus_metrics/prometheus_metrics';
import initAlertsSettings from '~/alerts_service_settings';
document.addEventListener('DOMContentLoaded', () => {
const prometheusSettingsWrapper = document.querySelector('.js-prometheus-metrics-monitoring');
const integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form');
integrationSettingsForm.init();
if (prometheusSettingsWrapper) {
const prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring');
prometheusMetrics.loadActiveMetrics();
}
initAlertsSettings(document.querySelector('.js-alerts-service-settings'));
});
......@@ -14,5 +14,6 @@ document.addEventListener('DOMContentLoaded', () => {
snippetEmbed();
} else {
initSnippetsApp();
initNotes();
}
});
......@@ -14,5 +14,6 @@ document.addEventListener('DOMContentLoaded', () => {
snippetEmbed();
} else {
initSnippetsApp();
initNotes();
}
});
......@@ -64,7 +64,7 @@ export default {
:deployment="deployment"
:computed-deployment-status="computedDeploymentStatus"
:show-visual-review-app="showVisualReviewApp"
:visual-review-app-metadata="visualReviewAppMeta"
:visual-review-app-meta="visualReviewAppMeta"
/>
</div>
</div>
......
......@@ -173,7 +173,7 @@ export default {
:app-button-text="appButtonText"
:deployment="deployment"
:show-visual-review-app="showVisualReviewApp"
:visual-review-app-metadata="visualReviewAppMeta"
:visual-review-app-meta="visualReviewAppMeta"
/>
<deployment-action-button
v-if="stopUrl"
......
......@@ -93,8 +93,10 @@ export default {
/>
<visual-review-app-link
v-if="showVisualReviewApp"
:view-app-display="appButtonText"
:link="deploymentExternalUrl"
:app-metadata="visualReviewAppMeta"
:changes="deployment.changes"
/>
</span>
</template>
......@@ -118,6 +118,14 @@
}
}
.ssh-keys-list {
.last-used-at,
.expires,
.key-created-at {
line-height: 32px;
}
}
.key-created-at {
line-height: 42px;
}
......
# frozen_string_literal: true
class Admin::IntegrationsController < Admin::ApplicationController
include ServiceParams
before_action :not_found, unless: :instance_level_integrations_enabled?
before_action :service, only: [:edit, :update, :test]
def edit
end
def update
@service.attributes = service_params[:service]
if @service.save(context: :manual_change)
redirect_to edit_admin_application_settings_integration_path(@service), notice: success_message
else
render :edit
end
end
def test
if @service.can_test?
render json: service_test_response, status: :ok
else
render json: {}, status: :not_found
end
end
private
def instance_level_integrations_enabled?
Feature.enabled?(:instance_level_integrations)
end
def project
# TODO: Change to something more meaningful
Project.first
end
def service
@service ||= project.find_or_initialize_service(params[:id])
end
def success_message
message = @service.active? ? _('activated') : _('settings saved, but not activated')
_('%{service_title} %{message}.') % { service_title: @service.title, message: message }
end
def service_test_response
unless @service.update(service_params[:service])
return { error: true, message: _('Validations failed.'), service_response: @service.errors.full_messages.join(','), test_failed: false }
end
data = @service.test_data(project, current_user)
outcome = @service.test(data)
unless outcome[:success]
return { error: true, message: _('Test failed.'), service_response: outcome[:result].to_s, test_failed: true }
end
{}
rescue Gitlab::HTTP::BlockedUrlError => e
{ error: true, message: _('Test failed.'), service_response: e.message, test_failed: true }
end
end
......@@ -55,6 +55,6 @@ class Profiles::KeysController < Profiles::ApplicationController
private
def key_params
params.require(:key).permit(:title, :key)
params.require(:key).permit(:title, :key, :expires_at)
end
end
......@@ -52,28 +52,26 @@ class Projects::ServicesController < Projects::ApplicationController
private
def service_test_response
if @service.update(service_params[:service])
unless @service.update(service_params[:service])
return { error: true, message: _('Validations failed.'), service_response: @service.errors.full_messages.join(','), test_failed: false }
end
data = @service.test_data(project, current_user)
outcome = @service.test(data)
if outcome[:success]
{}
else
{ error: true, message: _('Test failed.'), service_response: outcome[:result].to_s, test_failed: true }
end
else
{ error: true, message: _('Validations failed.'), service_response: @service.errors.full_messages.join(','), test_failed: false }
unless outcome[:success]
return { error: true, message: _('Test failed.'), service_response: outcome[:result].to_s, test_failed: true }
end
{}
rescue Gitlab::HTTP::BlockedUrlError => e
{ error: true, message: _('Test failed.'), service_response: e.message, test_failed: true }
end
def success_message
if @service.active?
_("%{service_title} activated.") % { service_title: @service.title }
else
_("%{service_title} settings saved, but not activated.") % { service_title: @service.title }
end
message = @service.active? ? _('activated') : _('settings saved, but not activated')
_('%{service_title} %{message}.') % { service_title: @service.title, message: message }
end
def service
......
......@@ -16,7 +16,7 @@ module Clusters
include AfterCommitQueue
default_value_for :ingress_type, :nginx
default_value_for :modsecurity_enabled, false
default_value_for :modsecurity_enabled, true
default_value_for :version, VERSION
enum ingress_type: {
......
......@@ -6,6 +6,7 @@ class Key < ApplicationRecord
include AfterCommitQueue
include Sortable
include Sha256Attribute
include Expirable
sha256_attribute :fingerprint_sha256
......
%h3.page-title
= @service.title
%p= @service.description
= form_for @service, as: :service, url: admin_application_settings_integration_path, method: :put, html: { class: 'gl-show-field-errors fieldset-form integration-settings-form js-integration-settings-form', data: { 'can-test' => @service.can_test?, 'test-url' => test_admin_application_settings_integration_path(@service) } } do |form|
= render 'shared/service_settings', form: form, service: @service
- if @service.editable?
.footer-block.row-content-block
= service_save_button(@service)
= link_to _('Cancel'), admin_application_settings_integration_path, class: 'btn btn-cancel'
- add_to_breadcrumbs _('Integrations'), admin_application_settings_integration_path
- breadcrumb_title @service.title
- page_title @service.title, _('Integrations')
= render 'form'
......@@ -4,7 +4,7 @@
%p #{@service.description} template.
= form_for :service, url: admin_application_settings_service_path, method: :put, html: { class: 'fieldset-form' } do |form|
= render 'shared/service_settings', form: form, subject: @service
= render 'shared/service_settings', form: form, service: @service
.footer-block.row-content-block
= form.submit 'Save', class: 'btn btn-success'
......@@ -6,10 +6,15 @@
= f.label :key, s_('Profiles|Key'), class: 'label-bold'
%p= _("Paste your public SSH key, which is usually contained in the file '~/.ssh/id_ed25519.pub' or '~/.ssh/id_rsa.pub' and begins with 'ssh-ed25519' or 'ssh-rsa'. Don't use your private SSH key.")
= f.text_area :key, class: "form-control js-add-ssh-key-validation-input qa-key-public-key-field", rows: 8, required: true, placeholder: s_('Profiles|Typically starts with "ssh-ed25519 …" or "ssh-rsa …"')
.form-group
.form-row
.col.form-group
= f.label :title, _('Title'), class: 'label-bold'
= f.text_field :title, class: "form-control input-lg qa-key-title-field", required: true, placeholder: s_('Profiles|e.g. My MacBook key')
%p.form-text.text-muted= _('Name your individual key via a title')
%p.form-text.text-muted= s_('Profiles|Give your individual key a title')
.col.form-group
= f.label :expires_at, s_('Profiles|Expires at'), class: 'label-bold'
= f.date_field :expires_at, class: "form-control input-lg qa-key-expiry-field", min: Date.tomorrow
.js-add-ssh-key-validation-warning.hide
.bs-callout.bs-callout-warning{ role: 'alert', aria_live: 'assertive' }
......
%li.key-list-item
.float-left.append-right-10
%li.d-flex.align-items-center.key-list-item
.append-right-10
- if key.valid?
= icon 'key', class: 'settings-list-icon d-none d-sm-block'
- if key.expired?
%span.d-inline-block.has-tooltip{ title: s_('Profiles|Your key has expired') }
= sprite_icon('warning-solid', size: 16, css_class: 'settings-list-icon d-none d-sm-block')
- else
= icon 'exclamation-triangle', class: 'settings-list-icon d-none d-sm-block has-tooltip',
title: key.errors.full_messages.join(', ')
= sprite_icon('key', size: 16, css_class: 'settings-list-icon d-none d-sm-block ')
- else
%span.d-inline-block.has-tooltip{ title: key.errors.full_messages.join(', ') }
= sprite_icon('warning-solid', size: 16, css_class: 'settings-list-icon d-none d-sm-block')
.key-list-item-info
.key-list-item-info.w-100.float-none
= link_to path_to_key(key, is_admin), class: "title" do
= key.title
%span.text-truncate
= key.fingerprint
.last-used-at
last used:
= key.last_used_at ? time_ago_with_tooltip(key.last_used_at) : 'n/a'
.float-right
.key-list-item-dates.d-flex.align-items-start.justify-content-between
%span.last-used-at.append-right-10
= s_('Profiles|Last used:')
= key.last_used_at ? time_ago_with_tooltip(key.last_used_at) : _('Never')
%span.expires.append-right-10
= s_('Profiles|Expires:')
= key.expires_at ? key.expires_at.to_date : _('Never')
%span.key-created-at
= s_('Profiles|Created %{time_ago}'.html_safe) % { time_ago:time_ago_with_tooltip(key.created_at)}
- if key.can_delete?
= link_to path_to_key(key, is_admin), data: { confirm: _('Are you sure?')}, method: :delete, class: "btn btn-transparent prepend-left-10" do
= link_to path_to_key(key, is_admin), data: { confirm: _('Are you sure?')}, method: :delete, class: "btn btn-transparent prepend-left-10 align-baseline" do
%span.sr-only= _('Remove')
= icon('trash')
= sprite_icon('remove', size: 16)
......@@ -11,9 +11,12 @@
%li
%span.light= _('Created on:')
%strong= @key.created_at.to_s(:medium)
%li
%span.light= _('Expires:')
%strong= @key.expires_at.try(:to_s, :medium) || _('Never')
%li
%span.light= _('Last used on:')
%strong= @key.last_used_at.try(:to_s, :medium) || 'N/A'
%strong= @key.last_used_at.try(:to_s, :medium) || _('Never')
.col-md-8
= form_errors(@key, type: 'key') unless @key.valid?
......
- is_admin = local_assigns.fetch(:admin, false)
- if @keys.any?
%ul.content-list{ data: { qa_selector: 'ssh_keys_list' } }
%ul.content-list.ssh-keys-list{ data: { qa_selector: 'ssh_keys_list' } }
= render partial: 'profiles/keys/key', collection: @keys, locals: { is_admin: is_admin }
- else
%p.settings-message.text-center
......
......@@ -23,4 +23,5 @@
= _('There are no matching files')
%p.text-secondary
= _('Try using a different search term to find the file you are looking for.')
= spinner nil, true
.text-center.prepend-top-default.loading
.spinner.spinner-md
......@@ -11,7 +11,7 @@
%p= @service.detailed_description
.col-lg-9
= form_for(@service, as: :service, url: project_service_path(@project, @service.to_param), method: :put, html: { class: 'gl-show-field-errors integration-settings-form js-integration-settings-form', data: { 'can-test' => @service.can_test?, 'test-url' => test_project_service_path(@project, @service) } }) do |form|
= render 'shared/service_settings', form: form, subject: @service
= render 'shared/service_settings', form: form, service: @service
- if @service.editable?
.footer-block.row-content-block
= service_save_button(@service)
......
= form_errors(@service)
- if lookup_context.template_exists?('help', "projects/services/#{@service.to_param}", true)
= render "projects/services/#{@service.to_param}/help", subject: subject
= render "projects/services/#{@service.to_param}/help", subject: @service
- elsif @service.help.present?
.info-well
.well-segment
......
---
title: Optimize Deployment related counters
merge_request: 26757
author:
type: performance
---
title: Add CRUD for Instance-Level Integrations
merge_request: 26454
author:
type: added
---
title: Add changed pages dropdown to visual review modal
merge_request:
author:
type: added
---
title: Introduce optional expiry date for SSH Keys
merge_request: 26351
author:
type: added
---
title: Migrate .fa-spinner to .spinner for app/views/projects/find_file
merge_request: 25051
author: nuwe1
type: other
---
title: Added tracking to merge request jump to next thread buttons
merge_request: 26319
author: Martin Hobert
type: added
---
title: Replace checkbox by toggle for ModSecurity on Cluster App Page
merge_request: 26720
author:
type: changed
......@@ -121,6 +121,11 @@ namespace :admin do
get '/', to: redirect('admin/application_settings/general'), as: nil
resources :services, only: [:index, :edit, :update]
resources :integrations, only: [:edit, :update, :test] do
member do
put :test
end
end
get :usage_data
put :reset_registration_token
......
# frozen_string_literal: true
class AddExpiresAtToKeys < ActiveRecord::Migration[6.0]
DOWNTIME = false
def change
add_column :keys, :expires_at, :datetime_with_timezone
end
end
# frozen_string_literal: true
class AddIndexOnUserIdStatusCreatedAtToDeployments < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :deployments, [:user_id, :status, :created_at]
end
def down
remove_concurrent_index :deployments, [:user_id, :status, :created_at]
end
end
# frozen_string_literal: true
class AddIndexOnIdAndStatusToDeployments < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :deployments, [:id, :status]
end
def down
remove_concurrent_index :deployments, [:id, :status]
end
end
......@@ -1409,6 +1409,7 @@ ActiveRecord::Schema.define(version: 2020_03_11_165635) do
t.index ["environment_id", "id"], name: "index_deployments_on_environment_id_and_id"
t.index ["environment_id", "iid", "project_id"], name: "index_deployments_on_environment_id_and_iid_and_project_id"
t.index ["environment_id", "status"], name: "index_deployments_on_environment_id_and_status"
t.index ["id", "status"], name: "index_deployments_on_id_and_status"
t.index ["id"], name: "partial_index_deployments_for_legacy_successful_deployments", where: "((finished_at IS NULL) AND (status = 2))"
t.index ["project_id", "id"], name: "index_deployments_on_project_id_and_id", order: { id: :desc }
t.index ["project_id", "iid"], name: "index_deployments_on_project_id_and_iid", unique: true
......@@ -1417,6 +1418,7 @@ ActiveRecord::Schema.define(version: 2020_03_11_165635) do
t.index ["project_id", "status"], name: "index_deployments_on_project_id_and_status"
t.index ["project_id", "updated_at", "id"], name: "index_deployments_on_project_id_and_updated_at_and_id", order: { updated_at: :desc, id: :desc }
t.index ["project_id"], name: "partial_index_deployments_for_project_id_and_tag", where: "(tag IS TRUE)"
t.index ["user_id", "status", "created_at"], name: "index_deployments_on_user_id_and_status_and_created_at"
end
create_table "description_versions", force: :cascade do |t|
......@@ -2291,6 +2293,7 @@ ActiveRecord::Schema.define(version: 2020_03_11_165635) do
t.boolean "public", default: false, null: false
t.datetime "last_used_at"
t.binary "fingerprint_sha256"
t.datetime_with_timezone "expires_at"
t.index ["fingerprint"], name: "index_keys_on_fingerprint", unique: true
t.index ["fingerprint_sha256"], name: "index_keys_on_fingerprint_sha256"
t.index ["id", "type"], name: "index_on_deploy_keys_id_and_type_and_public", unique: true, where: "(public = true)"
......
---
# Checks the presence of more than one space between sentences or clauses.
# Check for the following in common content scenarios:
#
# - No spaces.
# - More than one space.
#
# For a list of all options, see https://errata-ai.github.io/vale/styles/
extends: existence
message: "'%s' should have one space between sentences or clauses."
message: '"%s" must contain one and only one space.'
link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#punctuation
level: warning
level: error
nonword: true
tokens:
- '[a-z][.?!,][A-Z]'
- '[.?!,] {2,}[\w]'
- '[\w.?!,\(\)\-":] {2,}[\w.?!,\(\)\-":]'
......@@ -34,7 +34,7 @@ We won't cover the installation and configuration of Windows Server or Active Di
- Install Windows Server 2012 - (`technet.microsoft.com`) - [Installing Windows Server 2012](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/jj134246(v=ws.11))
- Install Active Directory Domain Services (AD DS) (`technet.microsoft.com`)- [Install Active Directory Domain Services](https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/deploy/install-active-directory-domain-services--level-100-#BKMK_PS)
- Install Active Directory Domain Services (AD DS) (`technet.microsoft.com`) - [Install Active Directory Domain Services](https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/deploy/install-active-directory-domain-services--level-100-#BKMK_PS)
> **Shortcut:** You can quickly install AD DS via PowerShell using
`Install-WindowsFeature AD-Domain-Services -IncludeManagementTools`
......
......@@ -215,7 +215,7 @@ sudo gitlab-rake gitlab:geo:check
- Ensure that you entered the `external_url` or `gitlab_rails['geo_node_name']` when adding the secondary node in the admin are of the **primary** node.
- Prior to GitLab 12.4, edit the secondary node in the Admin Area of the **primary** node and ensure that there is a trailing `/` in the `Name` field.
1. Check returns Exception: PG::UndefinedTable: ERROR: relation "geo_nodes" does not exist
1. Check returns `Exception: PG::UndefinedTable: ERROR: relation "geo_nodes" does not exist`
```plaintext
Checking Geo ...
......@@ -252,7 +252,7 @@ sudo gitlab-rake gitlab:geo:check
The following sections outline troubleshooting steps for fixing replication
errors.
### Message: "ERROR: replication slots can only be used if max_replication_slots > 0"?
### Message: `ERROR: replication slots can only be used if max_replication_slots > 0`?
This means that the `max_replication_slots` PostgreSQL variable needs to
be set on the **primary** database. In GitLab 9.4, we have made this setting
......@@ -263,7 +263,7 @@ Be sure to restart PostgreSQL for this to take
effect. See the [PostgreSQL replication
setup][database-pg-replication] guide for more details.
### Message: "FATAL: could not start WAL streaming: ERROR: replication slot "geo_secondary_my_domain_com" does not exist"?
### Message: `FATAL: could not start WAL streaming: ERROR: replication slot "geo_secondary_my_domain_com" does not exist`?
This occurs when PostgreSQL does not have a replication slot for the
**secondary** node by that name.
......
......@@ -78,23 +78,40 @@ References:
## GitLab components and configuration instructions
The GitLab application depends on the following [components](../../development/architecture.md#component-diagram)
and services. They are included in the reference architectures along with our
recommendations for their use and configuration. They are presented in the order
in which you would typically configure them.
| Component | Description | Configuration Instructions |
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------|
| [Load Balancer(s)](load_balancer.md)[^6] | Handles load balancing for the GitLab nodes where required. | [Load balancer HA configuration](load_balancer.md) |
The GitLab application depends on the following [components](../../development/architecture.md#component-diagram).
It can also depend on several third party services depending on
your environment setup. Here we'll detail both in the order in which
you would typically configure them along with our recommendations for
their use and configuration.
### Third party services
Here's some details of several third party services a typical environment
will depend on. The services can be provided by numerous applications
or providers and further advice can be given on how best to select.
These should be configured first, before the [GitLab components](#gitlab-components).
| Component | Description | Configuration instructions |
|--------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------|
| [Load Balancer(s)](load_balancer.md)[^6] | Handles load balancing for the GitLab nodes where required | [Load balancer HA configuration](load_balancer.md) |
| [Cloud Object Storage service](object_storage.md)[^4] | Recommended store for shared data objects | [Cloud Object Storage configuration](object_storage.md) |
| [NFS](nfs.md)[^5] [^7] | Shared disk storage service. Can be used as an alternative for Gitaly or Object Storage. Required for GitLab Pages | [NFS configuration](nfs.md) |
### GitLab components
Next are all of the components provided directly by GitLab. As mentioned
earlier, they are presented in the typical order you would configure
them.
| Component | Description | Configuration instructions |
|---------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------|---------------------------------------------------------------|
| [Consul](../../development/architecture.md#consul)[^3] | Service discovery and health checks/failover | [Consul HA configuration](consul.md) **(PREMIUM ONLY)** |
| [PostgreSQL](../../development/architecture.md#postgresql) | Database | [Database HA configuration](database.md) |
| [PgBouncer](../../development/architecture.md#pgbouncer) | Database Pool Manager | [PgBouncer HA configuration](pgbouncer.md) **(PREMIUM ONLY)** |
| [Redis](../../development/architecture.md#redis)[^3] with Redis Sentinel | Key/Value store for shared data with HA watcher service | [Redis HA configuration](redis.md) |
| [Gitaly](../../development/architecture.md#gitaly)[^2] [^5] [^7] | Recommended high-level storage for Git repository data. | [Gitaly HA configuration](gitaly.md) |
| [Gitaly](../../development/architecture.md#gitaly)[^2] [^5] [^7] | Recommended high-level storage for Git repository data | [Gitaly HA configuration](gitaly.md) |
| [Sidekiq](../../development/architecture.md#sidekiq) | Asynchronous/Background jobs | |
| [Cloud Object Storage service](object_storage.md)[^4] | Recommended store for shared data objects such as LFS, Uploads, Artifacts, etc... | [Cloud Object Storage configuration](object_storage.md) |
| [GitLab application nodes](../../development/architecture.md#unicorn)[^1] | (Unicorn / Puma, Workhorse) - Web-requests (UI, API, Git over HTTP) | [GitLab app HA/scaling configuration](gitlab.md) |
| [NFS](nfs.md)[^5] [^7] | Shared disk storage service. Can be used as an alternative for Gitaly or Object Storage. Required for GitLab Pages. | [NFS configuration](nfs.md) |
| [Prometheus](../../development/architecture.md#prometheus) and [Grafana](../../development/architecture.md#grafana) | GitLab environment monitoring | [Monitoring node for scaling/HA](monitoring_node.md) |
In some cases, components can be combined on the same nodes to reduce complexity as well.
......
......@@ -24,6 +24,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" 'https://gitlab.example.com/a
"title": "Sample key 25",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt1256k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
"created_at": "2015-09-03T07:24:44.627Z",
"expires_at": "2020-05-05T00:00:00.000Z"
"user": {
"name": "John Smith",
"username": "john_smith",
......@@ -92,6 +93,7 @@ Example response:
"title": "Sample key 1",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt1016k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
"created_at": "2019-11-14T15:11:13.222Z",
"expires_at": "2020-05-05T00:00:00.000Z"
"user": {
"id": 1,
"name": "Administrator",
......
......@@ -239,7 +239,7 @@ are listed in the descriptions of the relevant settings.
| `external_auth_client_key_pass` | string | no | Passphrase to use for the private key when authenticating with the external service this is encrypted when stored |
| `external_auth_client_key` | string | required by: `external_auth_client_cert` | Private key for the certificate when authentication is required for the external authorization service, this is encrypted when stored |
| `external_authorization_service_default_label` | string | required by: `external_authorization_service_enabled` | The default classification label to use when requesting authorization and no classification label has been specified on the project |
| `external_authorization_service_enabled` | boolean | no | (**If enabled, requires:** `external_authorization_service_default_label`, `external_authorization_service_timeout` and `external_authorization_service_url` ) Enable using an external authorization service for accessing projects |
| `external_authorization_service_enabled` | boolean | no | (**If enabled, requires:** `external_authorization_service_default_label`, `external_authorization_service_timeout` and `external_authorization_service_url`) Enable using an external authorization service for accessing projects |
| `external_authorization_service_timeout` | float | required by: `external_authorization_service_enabled` | The timeout after which an authorization request is aborted, in seconds. When a request times out, access is denied to the user. (min: 0.001, max: 10, step: 0.001) |
| `external_authorization_service_url` | string | required by: `external_authorization_service_enabled` | URL to which authorization requests will be directed |
| `file_template_project_id` | integer | no | **(PREMIUM)** The ID of a project to load custom file templates from |
......
......@@ -53,7 +53,7 @@ There are some high level differences between the products worth mentioning:
- by [webhook](../triggers/README.md#triggering-a-pipeline-from-a-webhook)
- by [ChatOps](../chatops/README.md)
You can control which jobs run in which cases, depending on how they are triggered,
- You can control which jobs run in which cases, depending on how they are triggered,
with the [`rules` syntax](../yaml/README.md#rules).
- GitLab [pipeline scheduling concepts](../pipelines/schedules.md) are also different than with Jenkins.
- All jobs within a single stage always run in parallel, and all stages run in sequence. We are planning
......@@ -78,7 +78,9 @@ There are some high level differences between the products worth mentioning:
also leverage [`protected environments`](../yaml/README.md#protecting-manual-jobs-premium)
to control who is able to approve them.
- GitLab comes with a [container registry](../../user/packages/container_registry/index.md), and we recommend using
container images to set up your build environment.
container images to set up your build environment. For example, set up one pipeline that builds your build environment
itself and publish that to the container registry. Then, have your pipelines use this instead of each building their
own environment, which will be slower and may be less consistent. We have extensive docs on [how to use the Container Registry](../../user/packages/container_registry/index.md).
- Totally stuck and not sure where to turn for advice? The [GitLab community forum](https://forum.gitlab.com/) can be a great resource.
## Groovy vs. YAML
......
......@@ -86,7 +86,7 @@ To change the Sidekiq worker's frequency:
1. Edit the `gitlab_rails['pipeline_schedule_worker_cron']` value in your instance's `gitlab.rb` file.
1. [Reconfigure GitLab](../../administration/restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
For GitLab.com, refer to the [dedicated settings page](../../user/gitlab_com/index.md#cron-jobs).
For GitLab.com, refer to the [dedicated settings page](../../user/gitlab_com/index.md#gitlab-cicd).
## Working with scheduled pipelines
......
......@@ -200,7 +200,8 @@ Feature.enable(:anonymous_visual_review_feedback)
The feedback form is served through a script you add to pages in your Review App.
If you have [Developer permissions](../../user/permissions.md) to the project,
you can access it by clicking the **Review** button in the **Pipeline** section
of the merge request.
of the merge request. The form modal will also show a dropdown for changed pages
if [route maps](#route-maps) are configured in the project.
![review button](img/review_button.png)
......
......@@ -867,6 +867,10 @@ CAUTION: **Warning:**
There are some points to be aware of when
[using this feature with new branches or tags *without* pipelines for merge requests](#using-onlychanges-without-pipelines-for-merge-requests).
CAUTION: **Warning:**
There are some points to be aware of when
[using this feature with scheduled pipelines](#using-onlychanges-with-scheduled-pipelines).
##### Using `only:changes` with pipelines for merge requests
With [pipelines for merge requests](../merge_request_pipelines/index.md),
......@@ -931,6 +935,12 @@ This could result in some unexpected behavior, including:
- When pushing a new commit, the changed files are calculated using the previous commit
as the base SHA.
##### Using `only:changes` with scheduled pipelines
`only:changes` always evaluates as "true" in [Scheduled pipelines](../pipelines/schedules.md).
All files are considered to have "changed" when a scheduled pipeline
runs.
### `rules`
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/29011) in GitLab 12.3.
......
......@@ -353,7 +353,7 @@ TIP: **Tip:** If you do not want to maintain bastion hosts, you can set up [AWS
1. Review all your settings and, if you're happy, click **Launch**.
1. Acknowledge that you have access to an existing key pair or create a new one. Click **Launch Instance**.
Confirm that you can SHH into the instance:
Confirm that you can SSH into the instance:
1. On the EC2 Dashboard, click on **Instances** in the left menu.
1. Select **Bastion Host A** from your list of instances.
......@@ -367,6 +367,12 @@ Confirm that you can SHH into the instance:
1. Under the **Add Tags** section, we’ll set `Key: Name` and `Value: Bastion Host B` so that we can easily identify our two instances.
1. For the security group, select the existing `bastion-sec-group` we created above.
### Use SSH Agent Forwarding
EC2 instances running Linux use private key files for SSH authentication. You'll connect to your bastion host using an SSH client and the private key file stored on your client. Since the private key file is not present on the bastion host, you will not be able to connect to your instances in private subnets.
Storing private key files on your bastion host is a bad idea. To get around this, use SSH agent forwarding on your client. See [Securely Connect to Linux Instances Running in a Private Amazon VPC](https://aws.amazon.com/blogs/security/securely-connect-to-linux-instances-running-in-a-private-amazon-vpc/) for a step-by-step guide on how to use SSH agent forwarding.
## Deploying GitLab inside an auto scaling group
We'll use AWS's wizard to deploy GitLab and then SSH into the instance to
......
......@@ -186,8 +186,11 @@ Now, it's time to add the newly created public key to your GitLab account.
1. Navigating to **SSH Keys** and pasting your **public** key from the clipboard into the **Key** field. If you:
- Created the key with a comment, this will appear in the **Title** field.
- Created the key without a comment, give your key an identifiable title like _Work Laptop_ or _Home Workstation_.
1. Choose an (optional) expiry date for the key under "Expires at" section. (Introduced in [GitLab 12.9](https://gitlab.com/gitlab-org/gitlab/-/issues/36243))
1. Click the **Add key** button.
SSH keys that have "expired" using this procedure will still be valid in GitLab workflows. As the GitLab-configured expiration date is not included in the SSH key itself, you can still export public SSH keys as needed.
NOTE: **Note:**
If you manually copied your public SSH key make sure you copied the entire
key starting with `ssh-ed25519` (or `ssh-rsa`) and ending with your email.
......
......@@ -666,7 +666,7 @@ To use Auto Deploy on a Kubernetes 1.16+ cluster, you must:
This will opt-in to using a version of the PostgreSQL chart that supports Kubernetes
1.16 and higher.
CAUTION: **Caution:** Opting into `AUTO_DEVOPS_POSTGRES_CHANNEL` version `2` will delete
DANGER: **Danger:** Opting into `AUTO_DEVOPS_POSTGRES_CHANNEL` version `2` will delete
the version `1` PostgreSQL database. Please backup the contents of the PostgreSQL database
first before opting into version `2`, so that you can restore into the version `2` database.
......
......@@ -13,6 +13,35 @@ information provided, you can immediately begin risk analysis and remediation.
For an overview of application security with GitLab, see
[Security Deep Dive](https://www.youtube.com/watch?v=k4vEJnGYy84).
## Quick start
Get started quickly with Dependency Scanning, License Scanning, and Static Application Security
Testing (SAST) by adding the following to your `.gitlab-ci.yml`:
```yaml
include:
- template: Dependency-Scanning.gitlab-ci.yml
- template: License-Scanning.gitlab-ci.yml
- template: SAST.gitlab-ci.yml
```
To add Dynamic Application Security Testing (DAST) scanning, add the following to your
`.gitlab-ci.yml` and replace `https://staging.example.com` with a staging server's web address:
```yaml
include:
- template: DAST.gitlab-ci.yml
variables:
DAST_WEBSITE: https://staging.example.com
```
To ensure the DAST scanner runs *after* deploying the application to the staging server, review the [DAST full documentation](dast/index.md).
To add Container Scanning, follow the steps listed in the [Container Scanning documentation](container_scanning/index.md#requirements).
To further configure any of the other scanners, refer to each scanner's documentation.
## Security scanning tools
GitLab uses the following tools to scan and report known vulnerabilities found in your project.
......
......@@ -324,3 +324,19 @@ in your project's sidebar, and you'll see the licenses displayed, where:
- **Component:** The components which have this license.
![License List](img/license_list_v12_6.png)
## Policies
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22465) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.9.
The **Policies** tab allows you to see your project's software license policies
and the associated classifications for each.
Policies can be configured by maintainers of the project.
![Edit Policy](img/policies_maintainer_edit_v12_9.png)
![Add Policy](img/policies_maintainer_add_v12_9.png)
Developers of the project can view the policies configured in a project.
![View Policies](img/policies_v12_9.png)
......@@ -74,7 +74,7 @@ Below are the current settings regarding [GitLab CI/CD](../../ci/README.md).
| ----------- | ----------------- | ------------- |
| Artifacts maximum size (uncompressed) | 1G | 100M |
| Artifacts [expiry time](../../ci/yaml/README.md#artifactsexpire_in) | kept forever | deleted after 30 days unless otherwise specified |
| Scheduled Pipeline Cron | `*/5 * * * *` | `*/19 * * * *` |
| Scheduled Pipeline Cron | `*/5 * * * *` | `19 * * * *` |
| [Max jobs in active pipelines](../../administration/instance_limits.md#number-of-jobs-in-active-pipelines) | `500` for Free tier, unlimited otherwise | Unlimited
## Repository size limit
......@@ -372,15 +372,6 @@ NOTE: **Note:**
The `SIDEKIQ_MEMORY_KILLER_MAX_RSS` setting is `16000000` on Sidekiq import
nodes and Sidekiq export nodes.
## Cron jobs
Periodically executed jobs by Sidekiq, to self-heal GitLab, do external
synchronizations, run scheduled pipelines, etc.:
| Setting | GitLab.com | Default |
|-------- |------------- |------------- |
| `pipeline_schedule_worker` | `19 * * * *` | `19 * * * *` |
## PostgreSQL
GitLab.com being a fairly large installation of GitLab means we have changed
......
......@@ -46,10 +46,16 @@ If the requirements are not met, the **Designs** tab displays a message to the u
Designs support short references in Markdown, but this needs to be enabled by setting
the `:design_management_reference_filter_gfm_pipeline` feature flag.
## Supported files
Files uploaded must have a file extension of either `png`, `jpg`, `jpeg`,
`gif`, `bmp`, `tiff` or `ico`.
Support for [SVG files](https://gitlab.com/gitlab-org/gitlab/issues/12771)
and [PDFs](https://gitlab.com/gitlab-org/gitlab/issues/32811) is planned for a future release.
## Limitations
- Files uploaded must have a file extension of either `png`, `jpg`, `jpeg`, `gif`, `bmp`, `tiff` or `ico`.
The [`svg` extension is not yet supported](https://gitlab.com/gitlab-org/gitlab/issues/12771).
- Design uploads are limited to 10 files at a time.
- Design Management data
[isn't deleted when a project is destroyed](https://gitlab.com/gitlab-org/gitlab/issues/13429) yet.
......@@ -77,6 +83,11 @@ of the design, and will replace the previous version.
Designs cannot be added if the issue has been moved, or its
[discussion is locked](../../discussions/#lock-discussions).
[Introduced](https://gitlab.com/gitlab-org/gitlab/issues/34353) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.9,
you can drag and drop designs onto the dedicated dropzone to upload them.
![Drag and drop design uploads](img/design_drag_and_drop_uploads_v12_9.png)
### Skipped designs
Designs with the same filename as an existing uploaded design _and_ whose content has not changed will be skipped.
......
......@@ -27,6 +27,8 @@ in the merge request widget area:
![Code Quality Widget](img/code_quality.png)
For more information, see the Code Climate list of [Supported Languages for Maintainability](https://docs.codeclimate.com/docs/supported-languages-for-maintainability).
## Use cases
For instance, consider the following workflow:
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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