Commit 6f471c29 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 8b778fae 36b3ca5c
---
name: debian_packages
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42670
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/5835
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/337288
milestone: '13.5'
type: development
group: group::package
......
......@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/333034
milestone: '14.0'
type: development
group: group::gitaly
default_enabled: false
default_enabled: true
......@@ -568,12 +568,12 @@ in the second step, do not supply the `EXTERNAL_URL` value.
# Sets `max_replication_slots` to double the number of database nodes.
# Patroni uses one extra slot per node when initiating the replication.
patroni['postgresql']['max_replication_slots'] = 8
patroni['postgresql']['max_replication_slots'] = 6
# Set `max_wal_senders` to one more than the number of replication slots in the cluster.
# This is used to prevent replication from using up all of the
# available database connections.
patroni['postgresql']['max_wal_senders'] = 9
patroni['postgresql']['max_wal_senders'] = 7
# Incoming recommended value for max connections is 500. See https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5691.
patroni['postgresql']['max_connections'] = 500
......
......@@ -570,12 +570,12 @@ in the second step, do not supply the `EXTERNAL_URL` value.
# Sets `max_replication_slots` to double the number of database nodes.
# Patroni uses one extra slot per node when initiating the replication.
patroni['postgresql']['max_replication_slots'] = 8
patroni['postgresql']['max_replication_slots'] = 6
# Set `max_wal_senders` to one more than the number of replication slots in the cluster.
# This is used to prevent replication from using up all of the
# available database connections.
patroni['postgresql']['max_wal_senders'] = 9
patroni['postgresql']['max_wal_senders'] = 7
# Incoming recommended value for max connections is 500. See https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5691.
patroni['postgresql']['max_connections'] = 500
......
......@@ -577,12 +577,12 @@ in the second step, do not supply the `EXTERNAL_URL` value.
# Sets `max_replication_slots` to double the number of database nodes.
# Patroni uses one extra slot per node when initiating the replication.
patroni['postgresql']['max_replication_slots'] = 8
patroni['postgresql']['max_replication_slots'] = 6
# Set `max_wal_senders` to one more than the number of replication slots in the cluster.
# This is used to prevent replication from using up all of the
# available database connections.
patroni['postgresql']['max_wal_senders'] = 9
patroni['postgresql']['max_wal_senders'] = 7
# Incoming recommended value for max connections is 500. See https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5691.
patroni['postgresql']['max_connections'] = 500
......
......@@ -841,12 +841,12 @@ in the second step, do not supply the `EXTERNAL_URL` value.
# Sets `max_replication_slots` to double the number of database nodes.
# Patroni uses one extra slot per node when initiating the replication.
patroni['postgresql']['max_replication_slots'] = 8
patroni['postgresql']['max_replication_slots'] = 6
# Set `max_wal_senders` to one more than the number of replication slots in the cluster.
# This is used to prevent replication from using up all of the
# available database connections.
patroni['postgresql']['max_wal_senders'] = 9
patroni['postgresql']['max_wal_senders'] = 7
# Incoming recommended value for max connections is 500. See https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5691.
patroni['postgresql']['max_connections'] = 500
......
......@@ -259,3 +259,27 @@ job1:
- echo 'this line should be hidden automatically after loading the job log'
- echo -e "\e[0Ksection_end:`date +%s`:my_first_section\r\e[0K"
```
## Deployment jobs
Deployment jobs are a specific kind of CI job in that they deploy code to
[environments](../environments/index.md). A deployment job is any job that
uses the `environment` keyword and the [`start` environment `action`](../yaml/index.md#environmentaction).
Deployment jobs do not need to be in the `deploy` stage. The following `deploy me`
job is an example of a deployment job. `action: start` is the default behavior and
is defined for the sake of the example, but you can omit it:
```yaml
deploy me:
script:
- deploy-to-cats.sh
environment:
name: production
url: https://cats.example.com
action: start
```
The behavior of deployment jobs can be controlled with
[deployment safety](../environments/deployment_safety.md) settings like
[skipping outdated deployment jobs](../environments/deployment_safety.md#prevent-deployments-during-deploy-freeze-windows)
and [ensuring only one deployment job runs at a time](../environments/deployment_safety.md#ensure-only-one-deployment-job-runs-at-a-time).
......@@ -1472,3 +1472,47 @@ If this happens, examine the following:
- Confirm there is sufficient disk space for the Gzip operation.
- If NFS is being used, check if the mount option `timeout` is set. The
default is `600`, and changing this to smaller values results in this error.
### `gitaly-backup` for repository backup and restore **(FREE SELF)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/333034) in GitLab 14.2.
> - [Deployed behind a feature flag](../user/feature_flags.md), enabled by default.
> - Recommended for production use.
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#disable-or-enable-gitaly-backup).
There can be
[risks when disabling released features](../user/feature_flags.md#risks-when-disabling-released-features).
Refer to this feature's version history for more details.
`gitaly-backup` is used by the backup Rake task to create and restore repository backups from Gitaly.
`gitaly-backup` replaces the previous backup method that directly calls RPCs on Gitaly from GitLab.
The backup Rake task must be able to find this executable. It can be configured in Omnibus GitLab packages:
1. Add the following to `/etc/gitlab/gitlab.rb`:
```ruby
gitlab_rails['backup_gitaly_backup_path'] = '/path/to/gitaly-backup'
```
1. [Reconfigure GitLab](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure)
for the changes to take effect
#### Disable or enable `gitaly-backup`
`gitaly-backup` is under development but ready for production use.
It is deployed behind a feature flag that is **enabled by default**.
[GitLab administrators with access to the GitLab Rails console](../administration/feature_flags.md)
can opt to disable it.
To disable it:
```ruby
Feature.disable(:gitaly_backup)
```
To enable it:
```ruby
Feature.enable(:gitaly_backup)
```
......@@ -102,7 +102,7 @@ release tag. When the `released_at` date and time has passed, the badge is autom
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/26016) in GitLab 12.6. Asset link editing was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9427) in GitLab 12.10.
Only users with at least the Developer can edit releases.
Only users with at least the Developer role can edit releases.
Read more about [Release permissions](#release-permissions).
To edit the details of a release:
......
......@@ -96,7 +96,16 @@ export default {
},
},
methods: {
triggerFormValidation() {
this.validateForm('timezone');
},
createSchedule() {
this.triggerFormValidation();
if (!this.isFormValid) {
return;
}
this.loading = true;
const { projectPath } = this;
......@@ -127,6 +136,7 @@ export default {
if (error) {
throw error;
}
this.$refs.addUpdateScheduleModal.hide();
this.$emit('scheduleCreated');
this.clearScheduleForm();
......@@ -140,11 +150,12 @@ export default {
});
},
editSchedule() {
this.loading = true;
const {
projectPath,
form: { timezone },
} = this;
this.loading = true;
this.$apollo
.mutate({
......
import { GlModal, GlAlert } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import AddEditScheduleForm from 'ee/oncall_schedules/components/add_edit_schedule_form.vue';
import AddEditScheduleModal, {
......@@ -58,9 +59,9 @@ describe('AddScheduleModal', () => {
};
async function awaitApolloDomMock() {
await wrapper.vm.$nextTick(); // kick off the DOM update
await nextTick(); // kick off the DOM update
await jest.runOnlyPendingTimers(); // kick off the mocked GQL stuff (promises)
await wrapper.vm.$nextTick(); // kick off the DOM update for flash
await nextTick(); // kick off the DOM update for flash
}
async function updateSchedule(localWrapper) {
......@@ -119,6 +120,8 @@ describe('AddScheduleModal', () => {
const findAlert = () => wrapper.findComponent(GlAlert);
const findModalForm = () => wrapper.findComponent(AddEditScheduleForm);
const submitForm = () => findModal().vm.$emit('primary', { preventDefault: jest.fn() });
describe('Schedule create', () => {
beforeEach(() => {
createComponent({ modalId: addScheduleModalId });
......@@ -134,9 +137,32 @@ describe('AddScheduleModal', () => {
});
});
it('makes a request with form data to create a schedule', () => {
mutate.mockResolvedValueOnce({});
findModal().vm.$emit('primary', { preventDefault: jest.fn() });
it('prevents form submit if schedule is invalid', () => {
createComponent({
modalId: addScheduleModalId,
data: { form: { name: 'schedule', timezone: null } },
});
submitForm();
expect(mutate).not.toHaveBeenCalled();
});
it("doesn't hide a modal and shows error alert on fail", async () => {
const error = 'some error';
mutate.mockImplementation(() => Promise.reject(error));
submitForm();
await waitForPromises();
const alert = findAlert();
expect(mockHideModal).not.toHaveBeenCalled();
expect(alert.exists()).toBe(true);
expect(alert.text()).toContain(error);
});
it('makes a request with form data to create a schedule and hides a modal', async () => {
mutate.mockImplementation(() =>
Promise.resolve({ data: { oncallScheduleCreate: { errors: [] } } }),
);
submitForm();
expect(mutate).toHaveBeenCalledWith({
mutation: expect.any(Object),
update: expect.any(Function),
......@@ -148,29 +174,15 @@ describe('AddScheduleModal', () => {
},
},
});
});
it('hides the modal on successful schedule creation', async () => {
mutate.mockResolvedValueOnce({ data: { oncallScheduleCreate: { errors: [] } } });
findModal().vm.$emit('primary', { preventDefault: jest.fn() });
await waitForPromises();
expect(mockHideModal).toHaveBeenCalled();
});
it("doesn't hide a modal and shows error alert on fail", async () => {
const error = 'some error';
mutate.mockResolvedValueOnce({ data: { oncallScheduleCreate: { errors: [error] } } });
findModal().vm.$emit('primary', { preventDefault: jest.fn() });
await waitForPromises();
const alert = findAlert();
expect(mockHideModal).not.toHaveBeenCalled();
expect(alert.exists()).toBe(true);
expect(alert.text()).toContain(error);
});
it('should clear the schedule form on a successful creation', () => {
mutate.mockResolvedValueOnce({});
findModal().vm.$emit('primary', { preventDefault: jest.fn() });
mutate.mockImplementation(() =>
Promise.resolve({ data: { oncallScheduleCreate: { errors: [] } } }),
);
submitForm();
expect(findModalForm().props('form')).toMatchObject({
name: '',
description: '',
......@@ -194,37 +206,31 @@ describe('AddScheduleModal', () => {
});
});
describe('Schedule update apollo API call', () => {
it('makes a request with `oncallScheduleUpdate` to update a schedule', () => {
mutate.mockResolvedValueOnce({});
findModal().vm.$emit('primary', { preventDefault: jest.fn() });
expect(mutate).toHaveBeenCalledWith({
mutation: expect.any(Object),
update: expect.anything(),
variables: {
iid: mockSchedule.iid,
projectPath,
name: mockSchedule.name,
description: mockSchedule.description,
timezone: mockSchedule.timezone.identifier,
},
});
});
it("doesn't hide the modal on fail", async () => {
const error = 'some error';
mutate.mockRejectedValueOnce(error);
submitForm();
await waitForPromises();
expect(mockHideModal).not.toHaveBeenCalled();
});
it('hides the modal on successful schedule creation', async () => {
mutate.mockResolvedValueOnce({ data: { oncallScheduleUpdate: { errors: [] } } });
findModal().vm.$emit('primary', { preventDefault: jest.fn() });
await waitForPromises();
expect(mockHideModal).toHaveBeenCalled();
});
it('makes a request with `oncallScheduleUpdate` to update a schedule and hides a modal on successful update', async () => {
mutate.mockResolvedValueOnce({ data: { oncallScheduleUpdate: { errors: [] } } });
submitForm();
it("doesn't hide the modal on fail", async () => {
const error = 'some error';
mutate.mockResolvedValueOnce({ data: { oncallScheduleUpdate: { errors: [error] } } });
findModal().vm.$emit('primary', { preventDefault: jest.fn() });
await waitForPromises();
expect(mockHideModal).not.toHaveBeenCalled();
expect(mutate).toHaveBeenCalledWith({
mutation: expect.any(Object),
update: expect.anything(),
variables: {
iid: mockSchedule.iid,
projectPath,
name: mockSchedule.name,
description: mockSchedule.description,
timezone: mockSchedule.timezone.identifier,
},
});
await waitForPromises();
expect(mockHideModal).toHaveBeenCalled();
});
describe('with mocked Apollo client', () => {
......@@ -255,7 +261,7 @@ describe('AddScheduleModal', () => {
it('it should not reload the page if the timezone has not changed', async () => {
mutate.mockResolvedValueOnce({});
findModal().vm.$emit('primary', { preventDefault: jest.fn() });
submitForm();
await waitForPromises();
expect(window.location.reload).not.toHaveBeenCalled();
});
......@@ -268,7 +274,7 @@ describe('AddScheduleModal', () => {
modalId: editScheduleModalId,
});
mutate.mockResolvedValueOnce({});
findModal().vm.$emit('primary', { preventDefault: jest.fn() });
submitForm();
expect(mutate).toHaveBeenCalledWith({
mutation: updateOncallScheduleMutation,
update: expect.anything(),
......
......@@ -297,7 +297,7 @@ namespace :gitlab do
end
def repository_backup_strategy
if Feature.enabled?(:gitaly_backup)
if Feature.enabled?(:gitaly_backup, default_enabled: :yaml)
max_concurrency = ENV['GITLAB_BACKUP_MAX_CONCURRENCY'].presence
max_storage_concurrency = ENV['GITLAB_BACKUP_MAX_STORAGE_CONCURRENCY'].presence
Backup::GitalyBackup.new(progress, parallel: max_concurrency, parallel_storage: max_storage_concurrency)
......
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