Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-07-06 12:08:36 +00:00
parent 5d3eac1cf8
commit c1f785fe21
37 changed files with 269 additions and 967 deletions

View File

@ -5,6 +5,7 @@ tasks:
- name: GDK
command: |
echo START_TIME_IN_SECONDS="$(date +%s)" | tee /workspace/gitpod_start_time.sh
gp sync-done gitpod-start
gp sync-await gdk-copied && cd /workspace/gitlab-development-kit && gdk help
- init: |
@ -35,9 +36,10 @@ tasks:
)
command: |
(
gp sync-await gitpod-start
set -e
gp sync-done gdk-copied
source /workspace/gitpod_start_time.sh
[[ -f /workspace/gitpod_start_time.sh ]] && source /workspace/gitpod_start_time.sh
SECONDS=0
cd /workspace/gitlab-development-kit
# update GDK
@ -72,7 +74,7 @@ tasks:
git checkout db/structure.sql
cd /workspace/gitlab-development-kit
# Waiting for GitLab ...
gp await-port 3000
gp ports await 3000
printf "Waiting for GitLab at $(gp url 3000) ..."
# Check /-/readiness which returns JSON, but we're only interested in the exit code
#
@ -84,7 +86,7 @@ tasks:
printf "$(date) GitLab is up (took ~%.1f minutes)\n" "$((10*$SECONDS/60))e-1" | tee -a /workspace/startup.log
gp preview $(gp url 3000) || true
PREBUILD_LOG=(/workspace/.gitpod/prebuild-log-*)
printf "Took %.1f minutes from https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitpod.yml being executed through to completion %s\n" "$((10*(($(date +%s)-${START_TIME_IN_SECONDS}))/60))e-1" "$([[ -f "$PREBUILD_LOG" ]] && echo "With Prebuilds")"
[[ -f /workspace/gitpod_start_time.sh ]] && printf "Took %.1f minutes from https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitpod.yml being executed through to completion %s\n" "$((10*(($(date +%s)-${START_TIME_IN_SECONDS}))/60))e-1" "$([[ -f "$PREBUILD_LOG" ]] && echo "With Prebuilds")"
)
ports:

View File

@ -1,95 +0,0 @@
<script>
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
export default {
components: {
GlButton,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
scrollUpButtonDisabled: {
type: Boolean,
required: false,
default: false,
},
scrollDownButtonDisabled: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
scrollUpAvailable: Boolean(this.$listeners.scrollUp),
scrollDownAvailable: Boolean(this.$listeners.scrollDown),
};
},
methods: {
handleRefreshClick() {
this.$emit('refresh');
},
handleScrollUp() {
this.$emit('scrollUp');
},
handleScrollDown() {
this.$emit('scrollDown');
},
},
};
</script>
<template>
<div>
<div
v-if="scrollUpAvailable"
v-gl-tooltip
class="controllers-buttons"
:title="__('Scroll to top')"
aria-labelledby="scroll-to-top"
>
<gl-button
id="scroll-to-top"
class="js-scroll-to-top gl-mr-2 btn-blank"
:aria-label="__('Scroll to top')"
:disabled="scrollUpButtonDisabled"
icon="scroll_up"
category="primary"
variant="default"
@click="handleScrollUp()"
/>
</div>
<div
v-if="scrollDownAvailable"
v-gl-tooltip
:disabled="scrollUpButtonDisabled"
class="controllers-buttons"
:title="__('Scroll to bottom')"
aria-labelledby="scroll-to-bottom"
>
<gl-button
id="scroll-to-bottom"
class="js-scroll-to-bottom gl-mr-2 btn-blank"
:aria-label="__('Scroll to bottom')"
:v-if="scrollDownAvailable"
:disabled="scrollDownButtonDisabled"
icon="scroll_down"
category="primary"
variant="default"
@click="handleScrollDown()"
/>
</div>
<gl-button
id="refresh-log"
v-gl-tooltip
class="js-refresh-log"
:title="__('Refresh')"
:aria-label="__('Refresh')"
icon="retry"
category="primary"
variant="default"
@click="handleRefreshClick"
/>
</div>
</template>

View File

@ -1,68 +0,0 @@
<script>
import { GlDropdown, GlDropdownSectionHeader, GlDropdownItem } from '@gitlab/ui';
import { mapActions, mapState } from 'vuex';
import { s__ } from '~/locale';
export default {
components: {
GlDropdown,
GlDropdownSectionHeader,
GlDropdownItem,
},
props: {
disabled: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
searchQuery: '',
};
},
computed: {
...mapState('environmentLogs', ['pods']),
podDropdownText() {
return this.pods.current || s__('Environments|No pod selected');
},
},
methods: {
...mapActions('environmentLogs', ['showPodLogs']),
isCurrentPod(podName) {
return podName === this.pods.current;
},
},
};
</script>
<template>
<div>
<gl-dropdown
ref="podsDropdown"
:text="podDropdownText"
:disabled="disabled"
class="gl-mr-3 gl-mb-3 gl-display-flex gl-md-display-block qa-pods-dropdown"
>
<gl-dropdown-section-header>
{{ s__('Environments|Select pod') }}
</gl-dropdown-section-header>
<gl-dropdown-item v-if="!pods.options.length" disabled>
<span ref="noPodsMsg" class="text-muted">
{{ s__('Environments|No pods to display') }}
</span>
</gl-dropdown-item>
<gl-dropdown-item
v-for="podName in pods.options"
:key="podName"
:is-check-item="true"
:is-checked="isCurrentPod(podName)"
class="text-nowrap"
@click="showPodLogs(podName)"
>
{{ podName }}
</gl-dropdown-item>
</gl-dropdown>
</div>
</template>

View File

@ -1,30 +0,0 @@
<script>
import { GlFilteredSearchToken, GlLoadingIcon } from '@gitlab/ui';
export default {
components: {
GlFilteredSearchToken,
GlLoadingIcon,
},
inheritAttrs: false,
props: {
config: {
type: Object,
required: true,
},
},
};
</script>
<template>
<gl-filtered-search-token :config="config" v-bind="{ ...$attrs }" v-on="$listeners">
<template #suggestions>
<div class="m-1">
<gl-loading-icon v-if="config.loading" size="sm" />
<div v-else class="py-1 px-2 text-muted">
{{ config.noOptionsText }}
</div>
</div>
</template>
</gl-filtered-search-token>
</template>

View File

@ -1,26 +0,0 @@
export const SET_PROJECT_ENVIRONMENT = 'SET_PROJECT_ENVIRONMENT';
export const SET_SEARCH = 'SET_SEARCH';
export const SET_MANAGED_APP = 'SET_MANAGED_APP';
export const SET_TIME_RANGE = 'SET_TIME_RANGE';
export const SHOW_TIME_RANGE_INVALID_WARNING = 'SHOW_TIME_RANGE_INVALID_WARNING';
export const HIDE_TIME_RANGE_INVALID_WARNING = 'HIDE_TIME_RANGE_INVALID_WARNING';
export const SET_CURRENT_POD_NAME = 'SET_CURRENT_POD_NAME';
export const REQUEST_ENVIRONMENTS_DATA = 'REQUEST_ENVIRONMENTS_DATA';
export const RECEIVE_ENVIRONMENTS_DATA_SUCCESS = 'RECEIVE_ENVIRONMENTS_DATA_SUCCESS';
export const RECEIVE_ENVIRONMENTS_DATA_ERROR = 'RECEIVE_ENVIRONMENTS_DATA_ERROR';
export const HIDE_REQUEST_ENVIRONMENTS_ERROR = 'HIDE_REQUEST_ENVIRONMENTS_ERROR';
export const REQUEST_LOGS_DATA = 'REQUEST_LOGS_DATA';
export const RECEIVE_LOGS_DATA_SUCCESS = 'RECEIVE_LOGS_DATA_SUCCESS';
export const RECEIVE_LOGS_DATA_ERROR = 'RECEIVE_LOGS_DATA_ERROR';
export const REQUEST_LOGS_DATA_PREPEND = 'REQUEST_LOGS_DATA_PREPEND';
export const RECEIVE_LOGS_DATA_PREPEND_SUCCESS = 'RECEIVE_LOGS_DATA_PREPEND_SUCCESS';
export const RECEIVE_LOGS_DATA_PREPEND_ERROR = 'RECEIVE_LOGS_DATA_PREPEND_ERROR';
export const HIDE_REQUEST_LOGS_ERROR = 'HIDE_REQUEST_LOGS_ERROR';
export const REFRESH_POD_LOGS = 'REFRESH_POD_LOGS';
export const RECEIVE_PODS_DATA_SUCCESS = 'RECEIVE_PODS_DATA_SUCCESS';
export const RECEIVE_PODS_DATA_ERROR = 'RECEIVE_PODS_DATA_ERROR';

View File

@ -1,110 +0,0 @@
import { convertToFixedRange } from '~/lib/utils/datetime_range';
import * as types from './mutation_types';
const mapLine = ({ timestamp, pod, message }) => ({
timestamp,
pod,
message,
});
export default {
// Search Data
[types.SET_SEARCH](state, searchQuery) {
state.search = searchQuery;
},
// Time Range Data
[types.SET_TIME_RANGE](state, timeRange) {
state.timeRange.selected = timeRange;
state.timeRange.current = convertToFixedRange(timeRange);
},
[types.SHOW_TIME_RANGE_INVALID_WARNING](state) {
state.timeRange.invalidWarning = true;
},
[types.HIDE_TIME_RANGE_INVALID_WARNING](state) {
state.timeRange.invalidWarning = false;
},
// Environments Data
[types.SET_PROJECT_ENVIRONMENT](state, environmentName) {
state.environments.current = environmentName;
// Clear current pod options
state.pods.current = null;
state.pods.options = [];
},
[types.REQUEST_ENVIRONMENTS_DATA](state) {
state.environments.options = [];
state.environments.isLoading = true;
},
[types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS](state, environmentOptions) {
state.environments.options = environmentOptions;
state.environments.isLoading = false;
},
[types.RECEIVE_ENVIRONMENTS_DATA_ERROR](state) {
state.environments.options = [];
state.environments.isLoading = false;
state.environments.fetchError = true;
},
[types.HIDE_REQUEST_ENVIRONMENTS_ERROR](state) {
state.environments.fetchError = false;
},
// Logs data
[types.REQUEST_LOGS_DATA](state) {
state.timeRange.current = convertToFixedRange(state.timeRange.selected);
state.logs.lines = [];
state.logs.isLoading = true;
// start pagination from the beginning
state.logs.cursor = null;
state.logs.isComplete = false;
},
[types.RECEIVE_LOGS_DATA_SUCCESS](state, { logs = [], cursor }) {
state.logs.lines = logs.map(mapLine);
state.logs.isLoading = false;
state.logs.cursor = cursor;
if (!cursor) {
state.logs.isComplete = true;
}
},
[types.RECEIVE_LOGS_DATA_ERROR](state) {
state.logs.lines = [];
state.logs.isLoading = false;
state.logs.fetchError = true;
},
[types.REQUEST_LOGS_DATA_PREPEND](state) {
state.logs.isLoading = true;
},
[types.RECEIVE_LOGS_DATA_PREPEND_SUCCESS](state, { logs = [], cursor }) {
const lines = logs.map(mapLine);
state.logs.lines = lines.concat(state.logs.lines);
state.logs.isLoading = false;
state.logs.cursor = cursor;
if (!cursor) {
state.logs.isComplete = true;
}
},
[types.RECEIVE_LOGS_DATA_PREPEND_ERROR](state) {
state.logs.isLoading = false;
state.logs.fetchError = true;
},
[types.HIDE_REQUEST_LOGS_ERROR](state) {
state.logs.fetchError = false;
},
// Pods data
[types.SET_CURRENT_POD_NAME](state, podName) {
state.pods.current = podName;
},
[types.RECEIVE_PODS_DATA_SUCCESS](state, podOptions) {
state.pods.options = podOptions;
},
[types.RECEIVE_PODS_DATA_ERROR](state) {
state.pods.options = [];
},
};

View File

@ -1,56 +0,0 @@
import { convertToFixedRange } from '~/lib/utils/datetime_range';
import { timeRanges, defaultTimeRange } from '~/vue_shared/constants';
export default () => ({
/**
* Full text search
*/
search: '',
/**
* Time range (Show last)
*/
timeRange: {
options: timeRanges,
// Selected time range, can be fixed or relative
selected: defaultTimeRange,
// Current time range, must be fixed
current: convertToFixedRange(defaultTimeRange),
invalidWarning: false,
},
/**
* Environments list information
*/
environments: {
options: [],
isLoading: false,
current: null,
fetchError: false,
},
/**
* Jobs with logs
*/
logs: {
lines: [],
isLoading: false,
/**
* Logs `cursor` represents the current pagination position,
* Should be sent in next batch (page) of logs to be fetched
*/
cursor: null,
isComplete: false,
fetchError: false,
},
/**
* Pods list information
*/
pods: {
options: [],
current: null,
},
});

View File

@ -1,7 +1,6 @@
import { GlToast } from '@gitlab/ui';
import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import Translate from '~/vue_shared/translate';
import SettingsApp from './components/group_settings_app.vue';
import { apolloProvider } from './graphql';
@ -20,7 +19,6 @@ export default () => {
provide: {
groupPath: el.dataset.groupPath,
groupDependencyProxyPath: el.dataset.groupDependencyProxyPath,
defaultExpanded: parseBoolean(el.dataset.defaultExpanded),
},
render(createElement) {
return createElement(SettingsApp);

View File

@ -1,7 +1,7 @@
<script>
import { GlToggle, GlSprintf, GlLink } from '@gitlab/ui';
import { s__ } from '~/locale';
import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue';
import SettingsBlock from '~/packages_and_registries/shared/components/settings_block.vue';
import updateDependencyProxySettings from '~/packages_and_registries/settings/group/graphql/mutations/update_dependency_proxy_settings.mutation.graphql';
import updateDependencyProxyImageTtlGroupPolicy from '~/packages_and_registries/settings/group/graphql/mutations/update_dependency_proxy_image_ttl_group_policy.mutation.graphql';
import { updateGroupPackageSettings } from '~/packages_and_registries/settings/group/graphql/utils/cache_update';
@ -39,7 +39,7 @@ export default {
links: {
DEPENDENCY_PROXY_DOCS_PATH,
},
inject: ['defaultExpanded', 'groupPath', 'groupDependencyProxyPath'],
inject: ['groupPath', 'groupDependencyProxyPath'],
props: {
dependencyProxySettings: {
type: Object,
@ -129,10 +129,7 @@ export default {
</script>
<template>
<settings-block
:default-expanded="defaultExpanded"
data-qa-selector="dependency_proxy_settings_content"
>
<settings-block data-qa-selector="dependency_proxy_settings_content">
<template #title> {{ $options.i18n.DEPENDENCY_PROXY_HEADER }} </template>
<template #description> {{ $options.i18n.DEPENDENCY_PROXY_DESCRIPTION }} </template>
<template #default>

View File

@ -9,7 +9,7 @@ import {
import updateNamespacePackageSettings from '~/packages_and_registries/settings/group/graphql/mutations/update_group_packages_settings.mutation.graphql';
import { updateGroupPackageSettings } from '~/packages_and_registries/settings/group/graphql/utils/cache_update';
import { updateGroupPackagesSettingsOptimisticResponse } from '~/packages_and_registries/settings/group/graphql/utils/optimistic_responses';
import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue';
import SettingsBlock from '~/packages_and_registries/shared/components/settings_block.vue';
export default {
name: 'PackageSettings',
@ -23,7 +23,7 @@ export default {
GenericSettings,
DuplicatesSettings,
},
inject: ['defaultExpanded', 'groupPath'],
inject: ['groupPath'],
props: {
packageSettings: {
type: Object,
@ -84,10 +84,7 @@ export default {
</script>
<template>
<settings-block
:default-expanded="defaultExpanded"
data-qa-selector="package_registry_settings_content"
>
<settings-block data-qa-selector="package_registry_settings_content">
<template #title> {{ $options.i18n.PACKAGE_SETTINGS_HEADER }}</template>
<template #description>
<span data-testid="description">

View File

@ -0,0 +1,17 @@
<template>
<section class="settings gl-py-7">
<div class="gl-lg-display-flex">
<div class="gl-lg-w-half gl-pr-10">
<h4>
<slot name="title"></slot>
</h4>
<p>
<slot name="description"></slot>
</p>
</div>
<div class="gl-lg-w-half gl-pt-3">
<slot></slot>
</div>
</div>
</section>
</template>

View File

@ -28,6 +28,7 @@
&:first-of-type {
margin-top: 10px;
padding-top: 0;
border: 0;
}

View File

@ -30,6 +30,8 @@ module Ci
raise NotSupportedAdapterError, 'This file format requires a dedicated adapter'
end
::Gitlab::ApplicationContext.push(artifact: file.model)
file.open do |stream|
file_format_adapter_class.new(stream).each_blob(&blk)
end

View File

@ -2,10 +2,11 @@
- help_path = local_assigns.fetch(:help_path)
- label = local_assigns.fetch(:label)
- last = local_assigns.fetch(:last, false)
- classes = ["btn btn-confirm gl-button btn-confirm-secondary gl-flex-direction-column gl-w-half"]
- classes = ["btn btn-confirm gl-button btn-confirm-secondary gl-flex-direction-column gl-flex-basis-0 gl-flex-grow-1 gl-min-w-0"]
- conditional_classes = [("gl-mr-5" unless last)]
= link_to help_path, class: classes + conditional_classes do
.svg-content.gl-p-3= image_tag logo_path, alt: label, class: "gl-w-64 gl-h-64"
%span
%span.gl-display-flex.gl-align-items-center.gl-m-3.gl-h-64
= image_tag logo_path, alt: label, class: "gl-w-15 gl-max-h-full gl-max-w-full"
%span.gl-white-space-normal
= label

View File

@ -1,14 +1,18 @@
- gke_label = s_('ClusterIntegration|Google GKE')
- eks_label = s_('ClusterIntegration|Amazon EKS')
- civo_label = s_('ClusterIntegration|Civo Kubernetes')
- create_cluster_label = s_('ClusterIntegration|Where do you want to create a cluster?')
- eks_help_path = help_page_path('user/infrastructure/clusters/connect/new_eks_cluster')
- gke_help_path = help_page_path('user/infrastructure/clusters/connect/new_gke_cluster')
- eks_help_path = help_page_path('user/infrastructure/clusters/connect/new_eks_cluster.md')
- gke_help_path = help_page_path('user/infrastructure/clusters/connect/new_gke_cluster.md')
- civo_help_path = help_page_path('user/infrastructure/clusters/connect/new_civo_cluster.md')
.gl-p-5
.gl-py-5.gl-md-pl-5.gl-md-pr-5
%h4.gl-mb-5
= create_cluster_label
.gl-display-flex
= render partial: 'clusters/clusters/cloud_providers/cloud_provider_button',
locals: { label: eks_label, logo_path: 'illustrations/logos/amazon_eks.svg', help_path: eks_help_path }
= render partial: 'clusters/clusters/cloud_providers/cloud_provider_button',
locals: { label: civo_label, logo_path: 'illustrations/third-party-logos/civo.svg', help_path: civo_help_path }
= render partial: 'clusters/clusters/cloud_providers/cloud_provider_button',
locals: { label: gke_label, logo_path: 'illustrations/logos/google_gke.svg', help_path: gke_help_path, last: true }

View File

@ -2,6 +2,5 @@
- page_title _('Packages & Registries')
- @content_class = 'limit-container-width' unless fluid_layout
%section#js-packages-and-registries-settings{ data: { default_expanded: expanded_by_default?.to_s,
group_path: @group.full_path,
%section#js-packages-and-registries-settings{ data: { group_path: @group.full_path,
group_dependency_proxy_path: group_dependency_proxy_path(@group) } }

View File

@ -9,11 +9,15 @@ end
if log_deprecations?
# Log deprecation warnings emitted through Kernel#warn, such as from gems or
# the Ruby VM.
Warning.process(/.+is deprecated$/) do |warning|
Gitlab::DeprecationJsonLogger.info(message: warning.strip, source: 'ruby')
# Returning :default means we continue emitting this to stderr as well.
:default
end
actions = {
/.+is deprecated$/ => lambda do |warning|
Gitlab::DeprecationJsonLogger.info(message: warning.strip, source: 'ruby')
# Returning :default means we continue emitting this to stderr as well.
:default
end
}
Warning.process('', actions)
# Log deprecation warnings emitted from Rails (see ActiveSupport::Deprecation).
ActiveSupport::Notifications.subscribe('deprecation.rails') do |name, start, finish, id, payload|

View File

@ -6,19 +6,91 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Configure an external Sidekiq instance **(FREE SELF)**
You can configure an external Sidekiq instance by using the Sidekiq that's
bundled in the GitLab package. Sidekiq requires connection to the Redis,
You can configure an external Sidekiq instance by using the Sidekiq that's bundled in the GitLab package. Sidekiq requires connection to the Redis,
PostgreSQL, and Gitaly instances.
## Required configuration
## Configure TCP access for PostgreSQL, Gitaly, and Redis
To configure Sidekiq:
By default, GitLab uses UNIX sockets and is not set up to communicate via TCP. To change this:
1. Edit the `/etc/gitlab/gitlab.rb` file on your GitLab instance and add the following:
```ruby
## PostgreSQL
# Replace POSTGRESQL_PASSWORD_HASH with a generated md5 value
postgresql['sql_user_password'] = 'POSTGRESQL_PASSWORD_HASH'
postgresql['listen_address'] = '0.0.0.0'
postgresql['port'] = 5432
# Add the Sidekiq nodes to PostgreSQL's trusted addresses.
# In the following example, 10.10.1.30/32 is the private IP
# of the Sidekiq server.
postgresql['md5_auth_cidr_addresses'] = %w(127.0.0.1/32 10.10.1.30/32)
postgresql['trust_auth_cidr_addresses'] = %w(127.0.0.1/32 10.10.1.30/32)
## Gitaly
# Make Gitaly accept connections on all network interfaces
gitaly['listen_addr'] = "0.0.0.0:8075"
## Set up the Gitaly token as a form of authentication since you are accessing Gitaly over the network
## https://docs.gitlab.com/ee/administration/gitaly/configure_gitaly.html#about-the-gitaly-token
gitaly['auth_token'] = 'abc123secret'
praefect['auth_token'] = 'abc123secret'
gitlab_rails['gitaly_token'] = 'abc123secret'
## Redis configuration
redis['bind'] = '0.0.0.0'
redis['port'] = 6379
# Password to Authenticate Redis
redis['password'] = 'redis-password-goes-here'
gitlab_rails['auto_migrate'] = false
```
1. Run `reconfigure`:
```shell
sudo gitlab-ctl reconfigure
```
1. Restart the `PostgreSQL` server:
```shell
sudo gitlab-ctl restart postgresql
```
1. After the restart, set `auto_migrate` to `true` or comment to use the default settings:
```ruby
gitlab_rails['auto_migrate'] = true
```
1. Run `reconfigure` again:
```shell
sudo gitlab-ctl reconfigure
```
## Set up Sidekiq instance
1. SSH into the Sidekiq server.
1. Confirm that you can access the PostgreSQL, Gitaly, and Redis ports:
```shell
telnet <GitLab host> 5432 # PostgreSQL
telnet <GitLab host> 8075 # Gitaly
telnet <GitLab host> 6379 # Redis
```
1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab package
using steps 1 and 2. **Do not complete any other steps.**
1. Edit `/etc/gitlab/gitlab.rb` with the following information and make sure
to replace with your values:
1. Copy the `/etc/gitlab/gitlab.rb` file from the GitLab instance and add the following settings. Make sure
to replace them with your values:
<!--
Updates to example must be made at:
@ -59,15 +131,24 @@ Updates to example must be made at:
##
external_url 'https://gitlab.example.com'
# Configure the gitlab-shell API callback URL. Without this, `git push` will
# fail. This can be your 'front door' GitLab URL or an internal load
# balancer.
gitlab_rails['internal_api_url'] = 'GITLAB_URL'
gitlab_shell['secret_token'] = 'SHELL_TOKEN'
########################################
#### Redis ###
########################################
## Must be the same in every sentinel node
redis['master_name'] = 'gitlab-redis'
## Must be the same in every sentinel node.
redis['master_name'] = 'gitlab-redis' # Required if you have setup redis cluster
## The same password for Redis authentication you set up for the master node.
redis['master_password'] = '<redis_master_password>'
### If redis is running on the main Gitlab instance and you have opened the TCP port as above add the following
gitlab_rails['redis_host'] = '<gitlab host>'
gitlab_rails['redis_port'] = 6379
#######################################
### Gitaly ###
@ -78,7 +159,7 @@ Updates to example must be made at:
git_data_dirs({
"default" => {
"gitaly_address" => "tcp://gitaly:8075",
"gitaly_token" => "<gitaly_token>"
"gitaly_token" => "<gitaly_token>"
}
})
@ -90,12 +171,6 @@ Updates to example must be made at:
gitlab_rails['db_host'] = '<database_host>'
gitlab_rails['db_port'] = '5432'
gitlab_rails['db_password'] = '<database_password>'
# Add the Sidekiq nodes to PostgreSQL's trusted addresses.
# In the following example, 10.10.1.30/32 is the private IP
# of the Sidekiq server.
postgresql['trust_auth_cidr_addresses'] = %w(127.0.0.1/32 10.10.1.30/32)
## Prevent database migrations from running on upgrade automatically
gitlab_rails['auto_migrate'] = false
@ -112,13 +187,15 @@ Updates to example must be made at:
sidekiq['max_concurrency'] = 10
```
1. Copy the `/etc/gitlab/gitlab-secrets.json` file from the GitLab instance and replace the file in the Sidekiq instance.
1. Reconfigure GitLab:
```shell
sudo gitlab-ctl reconfigure
```
1. Restart the Sidekiq nodes after completing the process and finishing the database migrations.
1. Restart the Sidekiq instance after completing the process and finishing the database migrations.
## Configure multiple Sidekiq nodes with shared storage
@ -218,7 +295,7 @@ To make health checks available from `localhost:8092`:
1. Edit `/etc/gitlab/gitlab.rb`:
```ruby
```ruby
sidekiq['health_checks_enabled'] = true
sidekiq['health_checks_listen_address'] = "localhost"
sidekiq['health_checks_listen_port'] = "8092"

View File

@ -199,7 +199,7 @@ module Gitlab
field_name = field.try(:attribute_name) || field
field_value = node[field_name]
ordering[field_name] = if field_value.is_a?(Time)
field_value.strftime('%Y-%m-%d %H:%M:%S.%N %Z')
field_value.to_s(:inspect)
else
field_value.to_s
end

View File

@ -96,7 +96,9 @@ module Gitlab
column_definitions.each_with_object({}.with_indifferent_access) do |column_definition, hash|
field_value = node[column_definition.attribute_name]
hash[column_definition.attribute_name] = if field_value.is_a?(Time)
field_value.strftime('%Y-%m-%d %H:%M:%S.%N %Z')
# use :inspect formatter to provide specific timezone info
# eg 2022-07-05 21:57:56.041499000 +0800
field_value.to_s(:inspect)
elsif field_value.nil?
nil
elsif lower_named_function?(column_definition)

View File

@ -8812,6 +8812,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
msgid "ClusterIntegration|Civo Kubernetes"
msgstr ""
msgid "ClusterIntegration|Clear cluster cache"
msgstr ""
@ -14665,12 +14668,6 @@ msgstr ""
msgid "Environments|No deployments yet"
msgstr ""
msgid "Environments|No pod selected"
msgstr ""
msgid "Environments|No pods to display"
msgstr ""
msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action” being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
msgstr ""
@ -14695,9 +14692,6 @@ msgstr ""
msgid "Environments|Rollback environment %{name}?"
msgstr ""
msgid "Environments|Select pod"
msgstr ""
msgid "Environments|Show all"
msgstr ""
@ -31748,9 +31742,6 @@ msgstr ""
msgid "Refine your search criteria (select a %{strong_open}group%{strong_close} and %{strong_open}project%{strong_close} when possible)"
msgstr ""
msgid "Refresh"
msgstr ""
msgid "Refresh the page and try again."
msgstr ""
@ -34359,7 +34350,7 @@ msgstr ""
msgid "SecurityOrchestration|After dismissing the alert, the information will never be shown again."
msgstr ""
msgid "SecurityOrchestration|After enabling a group-level policy, this policy automatically applies to all projects in this group."
msgid "SecurityOrchestration|After enabling a group-level policy, this policy automatically applies to all projects and sub-groups in this group."
msgstr ""
msgid "SecurityOrchestration|All policies"

View File

@ -72,6 +72,10 @@ class PipelineTestReportBuilder
# Please see for more info: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69053#note_709939709
def test_report_for_build(pipeline, build_id)
fetch("#{pipeline['web_url']}/tests/suite.json?build_ids[]=#{build_id}")
rescue Net::HTTPServerException => e
raise e unless e.response.code == 404
puts "Artifacts not found. They may have expired. Skipping this build."
end
def build_test_report_json_for_pipeline(pipeline)
@ -92,7 +96,8 @@ class PipelineTestReportBuilder
test_report['suites'] ||= []
failed_builds_for_test_stage.each do |failed_build|
test_report['suites'] << test_report_for_build(pipeline, failed_build['id'])
suite = test_report_for_build(pipeline, failed_build['id'])
test_report['suites'] << suite if suite
end
end

View File

@ -60,7 +60,6 @@ RSpec.describe 'Group Packages & Registries settings' do
visit_settings_page
expect(page).to have_content('Duplicate packages')
expect(page).to have_button('Collapse')
end
it 'automatically saves changes to the server', :js do

View File

@ -1,88 +0,0 @@
import { GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import LogControlButtons from '~/logs/components/log_control_buttons.vue';
describe('LogControlButtons', () => {
let wrapper;
const findScrollToTop = () => wrapper.find('.js-scroll-to-top');
const findScrollToBottom = () => wrapper.find('.js-scroll-to-bottom');
const findRefreshBtn = () => wrapper.find('.js-refresh-log');
const initWrapper = (opts) => {
wrapper = shallowMount(LogControlButtons, {
listeners: {
scrollUp: () => {},
scrollDown: () => {},
},
...opts,
});
};
afterEach(() => {
if (wrapper) {
wrapper.destroy();
}
});
it('displays UI elements', () => {
initWrapper();
expect(findScrollToTop().is(GlButton)).toBe(true);
expect(findScrollToBottom().is(GlButton)).toBe(true);
expect(findRefreshBtn().is(GlButton)).toBe(true);
});
it('emits a `refresh` event on click on `refresh` button', async () => {
initWrapper();
// An `undefined` value means no event was emitted
expect(wrapper.emitted('refresh')).toBe(undefined);
findRefreshBtn().vm.$emit('click');
await nextTick();
expect(wrapper.emitted('refresh')).toHaveLength(1);
});
describe('when scrolling actions are enabled', () => {
beforeEach(async () => {
// mock scrolled to the middle of a long page
initWrapper();
await nextTick();
});
it('click on "scroll to top" scrolls up', () => {
expect(findScrollToTop().attributes('disabled')).toBeUndefined();
findScrollToTop().vm.$emit('click');
expect(wrapper.emitted('scrollUp')).toHaveLength(1);
});
it('click on "scroll to bottom" scrolls down', () => {
expect(findScrollToBottom().attributes('disabled')).toBeUndefined();
findScrollToBottom().vm.$emit('click');
expect(wrapper.emitted('scrollDown')).toHaveLength(1);
});
});
describe('when scrolling actions are disabled', () => {
beforeEach(async () => {
initWrapper({ listeners: {} });
await nextTick();
});
it('buttons are disabled', async () => {
await nextTick();
expect(findScrollToTop().exists()).toBe(false);
expect(findScrollToBottom().exists()).toBe(false);
// This should be enabled when gitlab-ui contains:
// https://gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/1149
// expect(findScrollToBottom().is('[disabled]')).toBe(true);
});
});
});

View File

@ -1,71 +0,0 @@
import { GlFilteredSearchToken, GlLoadingIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import TokenWithLoadingState from '~/logs/components/tokens/token_with_loading_state.vue';
describe('TokenWithLoadingState', () => {
let wrapper;
const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const initWrapper = (props = {}, options) => {
wrapper = shallowMount(TokenWithLoadingState, {
propsData: {
cursorPosition: 'start',
...props,
},
...options,
});
};
beforeEach(() => {});
it('passes entire config correctly', () => {
const config = {
icon: 'pod',
type: 'pod',
title: 'Pod name',
unique: true,
};
initWrapper({ config });
expect(findFilteredSearchToken().props('config')).toEqual(config);
});
describe('suggestions are replaced', () => {
let mockNoOptsText;
let config;
let stubs;
beforeEach(() => {
mockNoOptsText = 'No suggestions available';
config = {
loading: false,
noOptionsText: mockNoOptsText,
};
stubs = {
GlFilteredSearchToken: {
template: `<div><slot name="suggestions"></slot></div>`,
},
};
});
it('renders a loading icon', () => {
config.loading = true;
initWrapper({ config }, { stubs });
expect(findLoadingIcon().exists()).toBe(true);
expect(wrapper.text()).toBe('');
});
it('renders an empty results message', () => {
initWrapper({ config }, { stubs });
expect(findLoadingIcon().exists()).toBe(false);
expect(wrapper.text()).toBe(mockNoOptsText);
});
});
});

View File

@ -1,71 +0,0 @@
const mockProjectPath = 'root/autodevops-deploy';
export const mockEnvName = 'production';
export const mockEnvironmentsEndpoint = `${mockProjectPath}/environments.json`;
export const mockEnvId = '99';
export const mockDocumentationPath = '/documentation.md';
export const mockLogsEndpoint = '/dummy_logs_path.json';
export const mockCursor = 'MOCK_CURSOR';
export const mockNextCursor = 'MOCK_NEXT_CURSOR';
const makeMockEnvironment = (id, name, advancedQuerying) => ({
id,
project_path: mockProjectPath,
name,
logs_api_path: mockLogsEndpoint,
enable_advanced_logs_querying: advancedQuerying,
});
export const mockEnvironment = makeMockEnvironment(mockEnvId, mockEnvName, true);
export const mockEnvironments = [
mockEnvironment,
makeMockEnvironment(101, 'staging', false),
makeMockEnvironment(102, 'review/a-feature', false),
];
export const mockPodName = 'production-764c58d697-aaaaa';
export const mockPods = [
mockPodName,
'production-764c58d697-bbbbb',
'production-764c58d697-ccccc',
'production-764c58d697-ddddd',
];
export const mockLogsResult = [
{
timestamp: '2019-12-13T13:43:18.2760123Z',
message: 'log line 1',
pod: 'foo',
},
{
timestamp: '2019-12-13T13:43:18.2760123Z',
message: 'log line A',
pod: 'bar',
},
{
timestamp: '2019-12-13T13:43:26.8420123Z',
message: 'log line 2',
pod: 'foo',
},
{
timestamp: '2019-12-13T13:43:26.8420123Z',
message: 'log line B',
pod: 'bar',
},
];
export const mockTrace = [
'Dec 13 13:43:18.276 | foo | log line 1',
'Dec 13 13:43:18.276 | bar | log line A',
'Dec 13 13:43:26.842 | foo | log line 2',
'Dec 13 13:43:26.842 | bar | log line B',
];
export const mockResponse = {
pod_name: mockPodName,
pods: mockPods,
logs: mockLogsResult,
cursor: mockNextCursor,
};
export const mockSearch = 'foo +bar';

View File

@ -1,257 +0,0 @@
import * as types from '~/logs/stores/mutation_types';
import mutations from '~/logs/stores/mutations';
import logsPageState from '~/logs/stores/state';
import {
mockEnvName,
mockEnvironments,
mockPods,
mockPodName,
mockLogsResult,
mockSearch,
mockCursor,
mockNextCursor,
} from '../mock_data';
describe('Logs Store Mutations', () => {
let state;
beforeEach(() => {
state = logsPageState();
});
it('ensures mutation types are correctly named', () => {
Object.keys(types).forEach((k) => {
expect(k).toEqual(types[k]);
});
});
describe('SET_PROJECT_ENVIRONMENT', () => {
it('sets the environment', () => {
mutations[types.SET_PROJECT_ENVIRONMENT](state, mockEnvName);
expect(state.environments.current).toEqual(mockEnvName);
});
});
describe('SET_SEARCH', () => {
it('sets the search', () => {
mutations[types.SET_SEARCH](state, mockSearch);
expect(state.search).toEqual(mockSearch);
});
});
describe('REQUEST_ENVIRONMENTS_DATA', () => {
it('inits data', () => {
mutations[types.REQUEST_ENVIRONMENTS_DATA](state);
expect(state.environments.options).toEqual([]);
expect(state.environments.isLoading).toEqual(true);
});
});
describe('RECEIVE_ENVIRONMENTS_DATA_SUCCESS', () => {
it('receives environments data and stores it as options', () => {
expect(state.environments.options).toEqual([]);
mutations[types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS](state, mockEnvironments);
expect(state.environments.options).toEqual(mockEnvironments);
expect(state.environments.isLoading).toEqual(false);
});
});
describe('RECEIVE_ENVIRONMENTS_DATA_ERROR', () => {
it('captures an error loading environments', () => {
mutations[types.RECEIVE_ENVIRONMENTS_DATA_ERROR](state);
expect(state.environments).toEqual({
options: [],
isLoading: false,
current: null,
fetchError: true,
});
});
});
describe('REQUEST_LOGS_DATA', () => {
it('starts loading for logs', () => {
mutations[types.REQUEST_LOGS_DATA](state);
expect(state.timeRange.current).toEqual({
start: expect.any(String),
end: expect.any(String),
});
expect(state.logs).toEqual({
lines: [],
cursor: null,
fetchError: false,
isLoading: true,
isComplete: false,
});
});
});
describe('RECEIVE_LOGS_DATA_SUCCESS', () => {
it('receives logs lines and cursor', () => {
mutations[types.RECEIVE_LOGS_DATA_SUCCESS](state, {
logs: mockLogsResult,
cursor: mockCursor,
});
expect(state.logs).toEqual({
lines: mockLogsResult,
isLoading: false,
cursor: mockCursor,
isComplete: false,
fetchError: false,
});
});
it('receives logs lines and a null cursor to indicate the end', () => {
mutations[types.RECEIVE_LOGS_DATA_SUCCESS](state, {
logs: mockLogsResult,
cursor: null,
});
expect(state.logs).toEqual({
lines: mockLogsResult,
isLoading: false,
cursor: null,
isComplete: true,
fetchError: false,
});
});
});
describe('RECEIVE_LOGS_DATA_ERROR', () => {
it('receives log data error and stops loading', () => {
mutations[types.RECEIVE_LOGS_DATA_ERROR](state);
expect(state.logs).toEqual({
lines: [],
isLoading: false,
cursor: null,
isComplete: false,
fetchError: true,
});
});
});
describe('REQUEST_LOGS_DATA_PREPEND', () => {
it('receives logs lines and cursor', () => {
mutations[types.REQUEST_LOGS_DATA_PREPEND](state);
expect(state.logs.isLoading).toBe(true);
});
});
describe('RECEIVE_LOGS_DATA_PREPEND_SUCCESS', () => {
it('receives logs lines and cursor', () => {
mutations[types.RECEIVE_LOGS_DATA_PREPEND_SUCCESS](state, {
logs: mockLogsResult,
cursor: mockCursor,
});
expect(state.logs).toEqual({
lines: mockLogsResult,
isLoading: false,
cursor: mockCursor,
isComplete: false,
fetchError: false,
});
});
it('receives additional logs lines and a new cursor', () => {
mutations[types.RECEIVE_LOGS_DATA_PREPEND_SUCCESS](state, {
logs: mockLogsResult,
cursor: mockCursor,
});
mutations[types.RECEIVE_LOGS_DATA_PREPEND_SUCCESS](state, {
logs: mockLogsResult,
cursor: mockNextCursor,
});
expect(state.logs).toEqual({
lines: [...mockLogsResult, ...mockLogsResult],
isLoading: false,
cursor: mockNextCursor,
isComplete: false,
fetchError: false,
});
});
it('receives logs lines and a null cursor to indicate is complete', () => {
mutations[types.RECEIVE_LOGS_DATA_PREPEND_SUCCESS](state, {
logs: mockLogsResult,
cursor: null,
});
expect(state.logs).toEqual({
lines: mockLogsResult,
isLoading: false,
cursor: null,
isComplete: true,
fetchError: false,
});
});
});
describe('RECEIVE_LOGS_DATA_PREPEND_ERROR', () => {
it('receives logs lines and cursor', () => {
mutations[types.RECEIVE_LOGS_DATA_PREPEND_ERROR](state);
expect(state.logs.isLoading).toBe(false);
expect(state.logs.fetchError).toBe(true);
});
});
describe('SET_CURRENT_POD_NAME', () => {
it('set current pod name', () => {
mutations[types.SET_CURRENT_POD_NAME](state, mockPodName);
expect(state.pods.current).toEqual(mockPodName);
});
});
describe('SET_TIME_RANGE', () => {
it('sets a default range', () => {
expect(state.timeRange.selected).toEqual(expect.any(Object));
expect(state.timeRange.current).toEqual(expect.any(Object));
});
it('sets a time range', () => {
const mockRange = {
start: '2020-01-10T18:00:00.000Z',
end: '2020-01-10T10:00:00.000Z',
};
mutations[types.SET_TIME_RANGE](state, mockRange);
expect(state.timeRange.selected).toEqual(mockRange);
expect(state.timeRange.current).toEqual(mockRange);
});
});
describe('RECEIVE_PODS_DATA_SUCCESS', () => {
it('receives pods data success', () => {
mutations[types.RECEIVE_PODS_DATA_SUCCESS](state, mockPods);
expect(state.pods).toEqual(
expect.objectContaining({
options: mockPods,
}),
);
});
});
describe('RECEIVE_PODS_DATA_ERROR', () => {
it('receives pods data error', () => {
mutations[types.RECEIVE_PODS_DATA_ERROR](state);
expect(state.pods).toEqual(
expect.objectContaining({
options: [],
}),
);
});
});
});

View File

@ -38,7 +38,6 @@ describe('DependencyProxySettings', () => {
let updateTtlPoliciesMutationResolver;
const defaultProvide = {
defaultExpanded: false,
groupPath: 'foo_group_path',
groupDependencyProxyPath: 'group_dependency_proxy_path',
};
@ -109,12 +108,6 @@ describe('DependencyProxySettings', () => {
expect(findSettingsBlock().exists()).toBe(true);
});
it('passes the correct props to settings block', () => {
mountComponent();
expect(findSettingsBlock().props('defaultExpanded')).toBe(false);
});
it('has the correct header text and description', () => {
mountComponent();

View File

@ -26,7 +26,6 @@ describe('Group Settings App', () => {
let show;
const defaultProvide = {
defaultExpanded: false,
groupPath: 'foo_group_path',
};

View File

@ -31,7 +31,6 @@ describe('Packages Settings', () => {
let apolloProvider;
const defaultProvide = {
defaultExpanded: false,
groupPath: 'foo_group_path',
};
@ -93,12 +92,6 @@ describe('Packages Settings', () => {
expect(findSettingsBlock().exists()).toBe(true);
});
it('passes the correct props to settings block', () => {
mountComponent();
expect(findSettingsBlock().props('defaultExpanded')).toBe(false);
});
it('has the correct header text', () => {
mountComponent();

View File

@ -0,0 +1,43 @@
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import SettingsBlock from '~/packages_and_registries/shared/components/settings_block.vue';
describe('SettingsBlock', () => {
let wrapper;
const mountComponent = (propsData) => {
wrapper = shallowMountExtended(SettingsBlock, {
propsData,
slots: {
title: '<div data-testid="title-slot"></div>',
description: '<div data-testid="description-slot"></div>',
default: '<div data-testid="default-slot"></div>',
},
});
};
afterEach(() => {
wrapper.destroy();
});
const findDefaultSlot = () => wrapper.findByTestId('default-slot');
const findTitleSlot = () => wrapper.findByTestId('title-slot');
const findDescriptionSlot = () => wrapper.findByTestId('description-slot');
it('has a default slot', () => {
mountComponent();
expect(findDefaultSlot().exists()).toBe(true);
});
it('has a title slot', () => {
mountComponent();
expect(findTitleSlot().exists()).toBe(true);
});
it('has a description slot', () => {
mountComponent();
expect(findDescriptionSlot().exists()).toBe(true);
});
});

View File

@ -196,10 +196,10 @@ Object {
],
"paginationInfo": Object {
"__typename": "PageInfo",
"endCursor": "eyJyZWxlYXNlZF9hdCI6IjIwMTgtMTItMTAgMDA6MDA6MDAuMDAwMDAwMDAwIFVUQyIsImlkIjoiMSJ9",
"endCursor": "eyJyZWxlYXNlZF9hdCI6IjIwMTgtMTItMTAgMDA6MDA6MDAuMDAwMDAwMDAwICswMDAwIiwiaWQiOiIxIn0",
"hasNextPage": false,
"hasPreviousPage": false,
"startCursor": "eyJyZWxlYXNlZF9hdCI6IjIwMTktMDEtMTAgMDA6MDA6MDAuMDAwMDAwMDAwIFVUQyIsImlkIjoiMiJ9",
"startCursor": "eyJyZWxlYXNlZF9hdCI6IjIwMTktMDEtMTAgMDA6MDA6MDAuMDAwMDAwMDAwICswMDAwIiwiaWQiOiIyIn0",
},
}
`;

View File

@ -3,6 +3,10 @@
require 'spec_helper'
RSpec.describe '0_log_deprecations' do
def setup_other_deprecations
Warning.process(__FILE__) { :raise }
end
def load_initializer
load Rails.root.join('config/initializers/0_log_deprecations.rb')
end
@ -11,6 +15,7 @@ RSpec.describe '0_log_deprecations' do
before do
stub_env('GITLAB_LOG_DEPRECATIONS', env_var)
setup_other_deprecations
load_initializer
end
@ -20,7 +25,7 @@ RSpec.describe '0_log_deprecations' do
ActiveSupport::Notifications.unsubscribe('deprecation.rails')
end
context 'for Ruby deprecations' do
describe 'Ruby deprecations' do
context 'when catching deprecations through Kernel#warn' do
it 'also logs them to deprecation logger' do
expect(Gitlab::DeprecationJsonLogger).to receive(:info).with(
@ -32,7 +37,7 @@ RSpec.describe '0_log_deprecations' do
end
end
context 'for other messages from Kernel#warn' do
describe 'other messages from Kernel#warn' do
it 'does not log them to deprecation logger' do
expect(Gitlab::DeprecationJsonLogger).not_to receive(:info)
@ -51,7 +56,7 @@ RSpec.describe '0_log_deprecations' do
end
end
context 'for Rails deprecations' do
describe 'Rails deprecations' do
it 'logs them to deprecation logger' do
expect(Gitlab::DeprecationJsonLogger).to receive(:info).with(
message: match(/^DEPRECATION WARNING: ABC will be removed/),

View File

@ -79,7 +79,7 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do
let(:nodes) { Project.all.order(Gitlab::Pagination::Keyset::Order.build([column_order_updated_at, column_order_created_at, column_order_id])) }
it 'returns the encoded value of the order' do
expect(decoded_cursor(cursor)).to include('updated_at' => project.updated_at.strftime('%Y-%m-%d %H:%M:%S.%N %Z'))
expect(decoded_cursor(cursor)).to include('updated_at' => project.updated_at.to_s(:inspect))
end
end
end

View File

@ -92,7 +92,7 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do
let(:nodes) { Project.order(:updated_at) }
it 'returns the encoded value of the order' do
expect(decoded_cursor(cursor)).to include('updated_at' => project.updated_at.strftime('%Y-%m-%d %H:%M:%S.%N %Z'))
expect(decoded_cursor(cursor)).to include('updated_at' => project.updated_at.to_s(:inspect))
end
it 'includes the :id even when not specified in the order' do
@ -104,7 +104,7 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do
let(:nodes) { Project.order(:updated_at).order(:created_at) }
it 'returns the encoded value of the order' do
expect(decoded_cursor(cursor)).to include('updated_at' => project.updated_at.strftime('%Y-%m-%d %H:%M:%S.%N %Z'))
expect(decoded_cursor(cursor)).to include('updated_at' => project.updated_at.to_s(:inspect))
end
end
@ -112,7 +112,7 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do
let(:nodes) { Project.order(Arel.sql('projects.updated_at IS NULL')).order(:updated_at).order(:id) }
it 'returns the encoded value of the order' do
expect(decoded_cursor(cursor)).to include('updated_at' => project.updated_at.strftime('%Y-%m-%d %H:%M:%S.%N %Z'))
expect(decoded_cursor(cursor)).to include('updated_at' => project.updated_at.to_s(:inspect))
end
end
end

View File

@ -53,6 +53,15 @@ RSpec.describe Ci::Artifactable do
expect { |b| artifact.each_blob(&b) }.to raise_error(described_class::NotSupportedAdapterError)
end
end
context 'pushes artifact_size to application context' do
let(:artifact) { create(:ci_job_artifact, :junit) }
it 'logs artifact size', :aggregate_failures do
expect { |b| artifact.each_blob(&b) }.to yield_control.once
expect(Gitlab::ApplicationContext.current).to include("meta.artifact_size" => artifact.size)
end
end
end
context 'ActiveRecord scopes' do

View File

@ -103,16 +103,18 @@ RSpec.describe PipelineTestReportBuilder do
end
describe '#test_report_for_latest_pipeline' do
let(:failed_build_uri) { "#{failed_pipeline_url}/tests/suite.json?build_ids[]=#{failed_build_id}" }
before do
allow(subject).to receive(:fetch).with(failed_build_uri).and_return(failed_builds_for_pipeline)
end
it 'fetches builds from pipeline related to MR' do
expect(subject).to receive(:fetch).with("#{failed_pipeline_url}/tests/suite.json?build_ids[]=#{failed_build_id}").and_return(failed_builds_for_pipeline)
subject.test_report_for_latest_pipeline
expected = { "suites" => [failed_builds_for_pipeline] }.to_json
expect(subject.test_report_for_latest_pipeline).to eq(expected)
end
context 'canonical pipeline' do
before do
allow(subject).to receive(:test_report_for_build).and_return(test_report_for_build)
end
context 'no previous pipeline' do
let(:mr_pipelines) { [] }
@ -171,6 +173,10 @@ RSpec.describe PipelineTestReportBuilder do
end
context 'failed pipeline and failed test builds' do
before do
allow(subject).to receive(:fetch).with(failed_build_uri).and_return(test_report_for_build)
end
it 'returns populated test list for suites' do
actual = subject.test_report_for_latest_pipeline
expected = {
@ -180,6 +186,36 @@ RSpec.describe PipelineTestReportBuilder do
expect(actual).to eq(expected)
end
end
context 'when receiving a server error' do
let(:response) { instance_double('Net::HTTPResponse') }
let(:error) { Net::HTTPServerException.new('server error', response) }
let(:test_report_for_latest_pipeline) { subject.test_report_for_latest_pipeline }
before do
allow(response).to receive(:code).and_return(response_code)
allow(subject).to receive(:fetch).with(failed_build_uri).and_raise(error)
end
context 'when response code is 404' do
let(:response_code) { 404 }
it 'continues without the missing reports' do
expected = { 'suites' => [] }.to_json
expect { test_report_for_latest_pipeline }.not_to raise_error
expect(test_report_for_latest_pipeline).to eq(expected)
end
end
context 'when response code is unexpected' do
let(:response_code) { 500 }
it 'raises HTTPServerException' do
expect { test_report_for_latest_pipeline }.to raise_error(error)
end
end
end
end
end
end