diff options
32 files changed, 336 insertions, 52 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index debfaddcc6d..a9899263941 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -a75309cec88ed34f594a4f6514bb0bb2aef7fcd5 +fdd1fe70085c1a20b10553680d88a967a4cfbfae diff --git a/app/assets/javascripts/clusters_list/components/ancestor_notice.vue b/app/assets/javascripts/clusters_list/components/ancestor_notice.vue new file mode 100644 index 00000000000..7954fc61785 --- /dev/null +++ b/app/assets/javascripts/clusters_list/components/ancestor_notice.vue @@ -0,0 +1,34 @@ +<script> +import { GlLink, GlSprintf } from '@gitlab/ui'; +import { mapState } from 'vuex'; + +export default { + components: { + GlLink, + GlSprintf, + }, + computed: { + ...mapState(['ancestorHelperPath', 'hasAncestorClusters']), + }, +}; +</script> + +<template> + <div v-if="hasAncestorClusters" class="bs-callout bs-callout-info"> + <p> + <gl-sprintf + :message=" + s__( + 'ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters. %{linkStart}More information%{linkEnd}', + ) + " + > + <template #link="{ content }"> + <gl-link :href="ancestorHelperPath"> + <strong>{{ content }}</strong> + </gl-link> + </template> + </gl-sprintf> + </p> + </div> +</template> diff --git a/app/assets/javascripts/clusters_list/components/clusters.vue b/app/assets/javascripts/clusters_list/components/clusters.vue index a51760599ea..7e9b720d269 100644 --- a/app/assets/javascripts/clusters_list/components/clusters.vue +++ b/app/assets/javascripts/clusters_list/components/clusters.vue @@ -9,6 +9,7 @@ import { GlSprintf, GlTable, } from '@gitlab/ui'; +import AncestorNotice from './ancestor_notice.vue'; import tooltip from '~/vue_shared/directives/tooltip'; import { CLUSTER_TYPES, STATUSES } from '../constants'; import { __, sprintf } from '~/locale'; @@ -17,6 +18,7 @@ export default { nodeMemoryText: __('%{totalMemory} (%{freeSpacePercentage}%{percentSymbol} free)'), nodeCpuText: __('%{totalCpu} (%{freeSpacePercentage}%{percentSymbol} free)'), components: { + AncestorNotice, GlBadge, GlLink, GlLoadingIcon, @@ -195,6 +197,8 @@ export default { <gl-loading-icon v-if="loadingClusters" size="md" class="gl-mt-3" /> <section v-else> + <ancestor-notice /> + <gl-table :items="clusters" :fields="fields" stacked="md" class="qa-clusters-table"> <template #cell(name)="{ item }"> <div :class="[contentAlignClasses, 'js-status']"> diff --git a/app/assets/javascripts/clusters_list/store/state.js b/app/assets/javascripts/clusters_list/store/state.js index 8f42cba6a6e..51fafd49479 100644 --- a/app/assets/javascripts/clusters_list/store/state.js +++ b/app/assets/javascripts/clusters_list/store/state.js @@ -1,4 +1,5 @@ export default (initialState = {}) => ({ + ancestorHelperPath: initialState.ancestorHelpPath, endpoint: initialState.endpoint, hasAncestorClusters: false, clusters: [], diff --git a/app/graphql/types/jira_user_type.rb b/app/graphql/types/jira_user_type.rb index 8aa21ce669b..999526a920e 100644 --- a/app/graphql/types/jira_user_type.rb +++ b/app/graphql/types/jira_user_type.rb @@ -13,7 +13,11 @@ module Types field :jira_email, GraphQL::STRING_TYPE, null: true, description: 'Email of the Jira user, returned only for users with public emails' field :gitlab_id, GraphQL::INT_TYPE, null: true, - description: 'Id of the matched GitLab user' + description: 'ID of the matched GitLab user' + field :gitlab_username, GraphQL::STRING_TYPE, null: true, + description: 'Username of the matched GitLab user' + field :gitlab_name, GraphQL::STRING_TYPE, null: true, + description: 'Name of the matched GitLab user' end # rubocop: enable Graphql/AuthorizeTypes end diff --git a/app/helpers/clusters_helper.rb b/app/helpers/clusters_helper.rb index 005070cca5c..b87d1c4e1ce 100644 --- a/app/helpers/clusters_helper.rb +++ b/app/helpers/clusters_helper.rb @@ -18,6 +18,7 @@ module ClustersHelper def js_clusters_list_data(path = nil) { + ancestor_help_path: help_page_path('user/group/clusters/index', anchor: 'cluster-precedence'), endpoint: path, img_tags: { aws: { path: image_path('illustrations/logos/amazon_eks.svg'), text: s_('ClusterIntegration|Amazon EKS') }, diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb index 17b9e56636b..3797ea1d96c 100644 --- a/app/services/ci/register_job_service.rb +++ b/app/services/ci/register_job_service.rb @@ -11,7 +11,7 @@ module Ci METRICS_SHARD_TAG_PREFIX = 'metrics_shard::'.freeze DEFAULT_METRICS_SHARD = 'default'.freeze - Result = Struct.new(:build, :valid?) + Result = Struct.new(:build, :build_json, :valid?) def initialize(runner) @runner = runner @@ -59,7 +59,7 @@ module Ci end register_failure - Result.new(nil, valid) + Result.new(nil, nil, valid) end # rubocop: enable CodeReuse/ActiveRecord @@ -71,7 +71,7 @@ module Ci # In case when 2 runners try to assign the same build, second runner will be declined # with StateMachines::InvalidTransition or StaleObjectError when doing run! or save method. if assign_runner!(build, params) - Result.new(build, true) + present_build!(build) end rescue StateMachines::InvalidTransition, ActiveRecord::StaleObjectError # We are looping to find another build that is not conflicting @@ -83,8 +83,10 @@ module Ci # In case we hit the concurrency-access lock, # we still have to return 409 in the end, # to make sure that this is properly handled by runner. - Result.new(nil, false) + Result.new(nil, nil, false) rescue => ex + # If an error (e.g. GRPC::DeadlineExceeded) occurred constructing + # the result, consider this as a failure to be retried. scheduler_failure!(build) track_exception_for_build(ex, build) @@ -92,6 +94,15 @@ module Ci nil end + # Force variables evaluation to occur now + def present_build!(build) + # We need to use the presenter here because Gitaly calls in the presenter + # may fail, and we need to ensure the response has been generated. + presented_build = ::Ci::BuildRunnerPresenter.new(build) # rubocop:disable CodeReuse/Presenter + build_json = ::API::Entities::JobRequest::Response.new(presented_build).to_json + Result.new(build, build_json, true) + end + def assign_runner!(build, params) build.runner_id = runner.id build.runner_session_attributes = params[:session] if params[:session].present? diff --git a/app/services/jira_import/users_mapper.rb b/app/services/jira_import/users_mapper.rb index 31a3f721556..c3cbeb157bd 100644 --- a/app/services/jira_import/users_mapper.rb +++ b/app/services/jira_import/users_mapper.rb @@ -14,9 +14,8 @@ module JiraImport { jira_account_id: jira_user['accountId'], jira_display_name: jira_user['displayName'], - jira_email: jira_user['emailAddress'], - gitlab_id: match_user(jira_user) - } + jira_email: jira_user['emailAddress'] + }.merge(match_user(jira_user)) end end @@ -25,7 +24,7 @@ module JiraImport # TODO: Matching user by email and displayName will be done as the part # of follow-up issue: https://gitlab.com/gitlab-org/gitlab/-/issues/219023 def match_user(jira_user) - nil + { gitlab_id: nil, gitlab_username: nil, gitlab_name: nil } end end end diff --git a/app/views/clusters/clusters/index.html.haml b/app/views/clusters/clusters/index.html.haml index a654a8741a4..557ad1bf280 100644 --- a/app/views/clusters/clusters/index.html.haml +++ b/app/views/clusters/clusters/index.html.haml @@ -12,15 +12,14 @@ = s_('ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project') = render 'clusters/clusters/buttons' - - if @has_ancestor_clusters - .bs-callout.bs-callout-info - = s_('ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters.') - %strong - = link_to _('More information'), help_page_path('user/group/clusters/index', anchor: 'cluster-precedence') - - if Feature.enabled?(:clusters_list_redesign) #js-clusters-list-app{ data: js_clusters_list_data(clusterable.index_path(format: :json)) } - else + - if @has_ancestor_clusters + .bs-callout.bs-callout-info + = s_('ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters.') + %strong + = link_to _('More information'), help_page_path('user/group/clusters/index', anchor: 'cluster-precedence') .clusters-table.js-clusters-list .gl-responsive-table-row.table-row-header{ role: "row" } .table-section.section-60{ role: "rowheader" } diff --git a/app/workers/concerns/reenqueuer.rb b/app/workers/concerns/reenqueuer.rb index 79a1d2d0070..bf6f6546c03 100644 --- a/app/workers/concerns/reenqueuer.rb +++ b/app/workers/concerns/reenqueuer.rb @@ -60,8 +60,6 @@ module Reenqueuer 5.seconds end - # We intend to get rid of sleep: - # https://gitlab.com/gitlab-org/gitlab/issues/121697 module ReenqueuerSleeper # The block will run, and then sleep until the minimum duration. Returns the # block's return value. diff --git a/changelogs/unreleased/225212-add-fields-to-jira-mutation.yml b/changelogs/unreleased/225212-add-fields-to-jira-mutation.yml new file mode 100644 index 00000000000..0873b43de80 --- /dev/null +++ b/changelogs/unreleased/225212-add-fields-to-jira-mutation.yml @@ -0,0 +1,5 @@ +--- +title: Add GitLab username and name to the import users from Jira mutation response +merge_request: 35542 +author: +type: changed diff --git a/changelogs/unreleased/rc-remove_metric_identifier_index.yml b/changelogs/unreleased/rc-remove_metric_identifier_index.yml new file mode 100644 index 00000000000..3196313e731 --- /dev/null +++ b/changelogs/unreleased/rc-remove_metric_identifier_index.yml @@ -0,0 +1,5 @@ +--- +title: Change PrometheusMetrics identifier index +merge_request: 35912 +author: +type: fixed diff --git a/changelogs/unreleased/sh-requeue-failed-job-register.yml b/changelogs/unreleased/sh-requeue-failed-job-register.yml new file mode 100644 index 00000000000..1a80095aeee --- /dev/null +++ b/changelogs/unreleased/sh-requeue-failed-job-register.yml @@ -0,0 +1,5 @@ +--- +title: Fail jobs that fail to render registration response +merge_request: 36274 +author: +type: fixed diff --git a/db/migrate/20200702201039_change_prometheus_metrics_identifier_index.rb b/db/migrate/20200702201039_change_prometheus_metrics_identifier_index.rb new file mode 100644 index 00000000000..248195c8c75 --- /dev/null +++ b/db/migrate/20200702201039_change_prometheus_metrics_identifier_index.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class ChangePrometheusMetricsIdentifierIndex < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + NEW_INDEX = :index_prometheus_metrics_on_identifier_and_null_project + OLD_INDEX = :index_prometheus_metrics_on_identifier + + disable_ddl_transaction! + + def up + add_concurrent_index :prometheus_metrics, :identifier, name: NEW_INDEX, unique: true, where: 'project_id IS NULL' + remove_concurrent_index_by_name :prometheus_metrics, OLD_INDEX + end + + def down + add_concurrent_index :prometheus_metrics, :identifier, name: OLD_INDEX, unique: true + remove_concurrent_index_by_name :prometheus_metrics, NEW_INDEX + end +end diff --git a/db/structure.sql b/db/structure.sql index 49c023d6ed4..752fc2b1502 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -20025,7 +20025,7 @@ CREATE INDEX index_prometheus_metrics_on_common ON public.prometheus_metrics USI CREATE INDEX index_prometheus_metrics_on_group ON public.prometheus_metrics USING btree ("group"); -CREATE UNIQUE INDEX index_prometheus_metrics_on_identifier ON public.prometheus_metrics USING btree (identifier); +CREATE UNIQUE INDEX index_prometheus_metrics_on_identifier_and_null_project ON public.prometheus_metrics USING btree (identifier) WHERE (project_id IS NULL); CREATE UNIQUE INDEX index_prometheus_metrics_on_identifier_and_project_id ON public.prometheus_metrics USING btree (identifier, project_id); @@ -23718,6 +23718,7 @@ COPY "schema_migrations" (version) FROM STDIN; 20200701093859 20200701205710 20200702123805 +20200702201039 20200703064117 20200703121557 20200703154822 diff --git a/doc/administration/gitaly/praefect.md b/doc/administration/gitaly/praefect.md index 88e59ba7ffc..a718de85b54 100644 --- a/doc/administration/gitaly/praefect.md +++ b/doc/administration/gitaly/praefect.md @@ -35,7 +35,8 @@ The availability objectives for Gitaly clusters are: Writes are replicated asynchronously. Any writes that have not been replicated to the newly promoted primary are lost. - [Strong consistency](#strong-consistency) can be used to improve this to "no loss". + [Strong consistency](#strong-consistency) can be used to avoid loss in some + circumstances. - **Recovery Time Objective (RTO):** Less than 10 seconds. @@ -886,8 +887,8 @@ after the write to the primary Gitaly node has happened. Praefect can instead provide strong consistency by creating a transaction and writing changes to all Gitaly nodes at once. Strong consistency is currently in [alpha](https://about.gitlab.com/handbook/product/#alpha-beta-ga) and not enabled by -default. For more information, see the -[strong consistency epic](https://gitlab.com/groups/gitlab-org/-/epics/1189). +default. If enabled, transactions are only available for a subset of RPCs. For more +information, see the [strong consistency epic](https://gitlab.com/groups/gitlab-org/-/epics/1189). To enable strong consistency: diff --git a/doc/administration/server_hooks.md b/doc/administration/server_hooks.md index 6df0f187a42..eebf9113643 100644 --- a/doc/administration/server_hooks.md +++ b/doc/administration/server_hooks.md @@ -119,6 +119,49 @@ For `<project>.git` you'll need to [translate your project name into the hashed storage format](repository_storage_types.md#translating-hashed-storage-paths) that GitLab uses. +## Environment Variables + +The following set of environment variables are available to server hooks. + +### GitLab Environment Variables + +| Envirnment Variable | purpose | +|---------------------|---------------------------------------------------------| +| GL_ID | GitLab identifier eg: user-2234 that initiated the push | +| GL_PROJECT_PATH (available starting 13.2) | GitLab project path | +| GL_PROTOCOL (available starting 13.2) | Protocol used with push | +| GL_REPOSITORY | project-<id> where id of the project | +| GL_USERNAME | GitLab username that initiated the push | + +Pre-receive and post-receive server hooks can also access the following Git environment variables. + +| Environment variable | Description | +|:-----------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `GIT_ALTERNATE_OBJECT_DIRECTORIES` | Alternate object directories in the quarantine environment. See [Git `receive-pack` documentation](https://git-scm.com/docs/git-receive-pack#_quarantine_environment). | +| `GIT_OBJECT_DIRECTORY` | GitLab project path in the quarantine environment. See [Git `receive-pack` documentation](https://git-scm.com/docs/git-receive-pack#_quarantine_environment). | +| `GIT_PUSH_OPTION_COUNT` | Number of push options. See [Git `pre-receive` documentation](https://git-scm.com/docs/githooks#pre-receive). | +| `GIT_PUSH_OPTION_<i>` | Value of push options where `i` is from `0` to `GIT_PUSH_OPTION_COUNT - 1`. See [Git `pre-receive` documentation](https://git-scm.com/docs/githooks#pre-receive). | + +NOTE: **Note:** +While other environment variables can be passed to server hooks, your application +should not rely on them as they can change. + +## Transition to Go + +> Introduced in GitLab 13.2 using feature flags. + +The following server hooks have been reimplemented in Go: + +- `pre-receive`, with the Go implementation used by default. To use the Ruby + implementation instead, [disable](../operations/feature_flags.md#enable-or-disable-feature-flag-strategies) + the `:gitaly_go_preceive_hook` feature flag. +- `update`, with the Go implementation used by default. To use the Ruby implementation + instead, [disable](../operations/feature_flags.md#enable-or-disable-feature-flag-strategies) + the `:gitaly_go_update_hook` feature flag. +- `post-receive`, however the Ruby implementation is used by default. To use the Go + implementation instead, [enabled](../operations/feature_flags.md#enable-or-disable-feature-flag-strategies) + the `:gitaly_go_postreceive_hook` feature flag. + ## Custom error messages > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5073) in GitLab 8.10. diff --git a/doc/api/README.md b/doc/api/README.md index 16413632d78..b07f14b5a7a 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -423,7 +423,7 @@ Status: 200 OK ``` CAUTION: **Deprecation:** -The `Links` Header will be removed in GitLab 14.0 to be aligned with the [W3C specification](https://www.w3.org/wiki/LinkHeader) +The `Links` Header will be removed in GitLab 14.0 to be aligned with the [W3C `Link` specification](https://www.w3.org/wiki/LinkHeader) The link to the next page contains an additional filter `id_after=42` which excludes records we have retrieved already. Note the type of filter depends on the `order_by` option used and we may have more than one additional filter. diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql index b25a3179ca6..f3659f9269f 100644 --- a/doc/api/graphql/reference/gitlab_schema.graphql +++ b/doc/api/graphql/reference/gitlab_schema.graphql @@ -6656,11 +6656,21 @@ type JiraService implements Service { type JiraUser { """ - Id of the matched GitLab user + ID of the matched GitLab user """ gitlabId: Int """ + Name of the matched GitLab user + """ + gitlabName: String + + """ + Username of the matched GitLab user + """ + gitlabUsername: String + + """ Account id of the Jira user """ jiraAccountId: String! diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json index 432342ad8b1..96097e3f5ac 100644 --- a/doc/api/graphql/reference/gitlab_schema.json +++ b/doc/api/graphql/reference/gitlab_schema.json @@ -18439,7 +18439,7 @@ "fields": [ { "name": "gitlabId", - "description": "Id of the matched GitLab user", + "description": "ID of the matched GitLab user", "args": [ ], @@ -18452,6 +18452,34 @@ "deprecationReason": null }, { + "name": "gitlabName", + "description": "Name of the matched GitLab user", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "gitlabUsername", + "description": "Username of the matched GitLab user", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "jiraAccountId", "description": "Account id of the Jira user", "args": [ diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 431e068a497..c80337175c9 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -1007,7 +1007,9 @@ Autogenerated return type of JiraImportUsers | Name | Type | Description | | --- | ---- | ---------- | -| `gitlabId` | Int | Id of the matched GitLab user | +| `gitlabId` | Int | ID of the matched GitLab user | +| `gitlabName` | String | Name of the matched GitLab user | +| `gitlabUsername` | String | Username of the matched GitLab user | | `jiraAccountId` | String! | Account id of the Jira user | | `jiraDisplayName` | String! | Display name of the Jira user | | `jiraEmail` | String | Email of the Jira user, returned only for users with public emails | diff --git a/doc/api/settings.md b/doc/api/settings.md index 78d992cff58..e5f3da7fbf6 100644 --- a/doc/api/settings.md +++ b/doc/api/settings.md @@ -48,7 +48,7 @@ Example response: "sign_in_text" : null, "container_expiration_policies_enable_historic_entries": true, "container_registry_token_expire_delay": 5, - "repository_storages": ["default"], + "repository_storages_weighted": {"default": 100}, "plantuml_enabled": false, "plantuml_url": null, "terminal_max_session_time": 0, @@ -314,7 +314,8 @@ are listed in the descriptions of the relevant settings. | `receive_max_input_size` | integer | no | Maximum push size (MB). | | `repository_checks_enabled` | boolean | no | GitLab will periodically run `git fsck` in all project and wiki repositories to look for silent disk corruption issues. | | `repository_size_limit` | integer | no | **(PREMIUM)** Size limit per repository (MB) | -| `repository_storages` | array of strings | no | A list of names of enabled storage paths, taken from `gitlab.yml`. New projects will be created in one of these stores, chosen at random. | +| `repository_storages` | array of strings | no | (GitLab 13.0 and earlier) List of names of enabled storage paths, taken from `gitlab.yml`. New projects are created in one of these stores, chosen at random. | +| `repository_storages_weighted` | hash of strings to integers | no | (GitLab 13.1 and later) Hash of names of taken from `gitlab.yml` to weights. New projects are created in one of these stores, chosen by a weighted random selection. | | `require_two_factor_authentication` | boolean | no | (**If enabled, requires:** `two_factor_grace_period`) Require all users to set up Two-factor authentication. | | `restricted_visibility_levels` | array of strings | no | Selected levels cannot be used by non-admin users for groups, projects or snippets. Can take `private`, `internal` and `public` as a parameter. Default is `null` which means there is no restriction. | | `rsa_key_restriction` | integer | no | The minimum allowed bit length of an uploaded RSA key. Default is `0` (no restriction). `-1` disables RSA keys. | diff --git a/doc/development/elasticsearch.md b/doc/development/elasticsearch.md index c63e0164e43..425b6b01b76 100644 --- a/doc/development/elasticsearch.md +++ b/doc/development/elasticsearch.md @@ -111,7 +111,8 @@ Patterns: - `'"((?:\\"|[^"]|\\")*)"'`: captures terms inside quotes, removing the quotes - `"'((?:\\'|[^']|\\')*)'"`: same as above, for single-quotes - `'\.([^.]+)(?=\.|\s|\Z)'`: separate terms with periods in-between -- `'([\p{L}_.-]+)'` : some common chars in file names to keep the whole filename intact (eg. `my_file-ñame.txt`) +- `'([\p{L}_.-]+)'`: some common chars in file names to keep the whole filename intact (eg. `my_file-ñame.txt`) +- `'([\p{L}\d_]+)'`: letters, numbers and underscores are the most common tokens in programming. Always capture them greedily regardless of context. ## Gotchas diff --git a/doc/development/fe_guide/vue.md b/doc/development/fe_guide/vue.md index 0d77e4d129b..2a0556c6cda 100644 --- a/doc/development/fe_guide/vue.md +++ b/doc/development/fe_guide/vue.md @@ -53,14 +53,14 @@ Be sure to read about [page-specific JavaScript](./performance.md#page-specific- #### Providing data from HAML to JavaScript -While mounting a Vue application may be a need to provide data from Rails to JavaScript. -To do that, provide the data through `data` attributes in the HTML element and query them while mounting the application. +While mounting a Vue application, you might need to provide data from Rails to JavaScript. +To do that, you can use the `data` attributes in the HTML element and query them while mounting the application. _Note:_ You should only do this while initializing the application, because the mounted element will be replaced with Vue-generated DOM. The advantage of providing data from the DOM to the Vue instance through `props` in the `render` function -instead of querying the DOM inside the main Vue component is that makes tests easier by avoiding the need to -create a fixture or an HTML element in the unit test. See the following example: +instead of querying the DOM inside the main Vue component is avoiding the need to create a fixture or an HTML element in the unit test, +which will make the tests easier. See the following example: ```javascript // haml @@ -134,7 +134,7 @@ This approach has a few benefits: intermediate components being aware of it (c.f. passing the flag down via props). - Good testability, since the flag can be provided to `mount`/`shallowMount` - from `vue-test-utils` as easily as a prop. + from `vue-test-utils` simply as a prop. ```javascript import { shallowMount } from '@vue/test-utils'; @@ -155,7 +155,7 @@ This folder holds all components that are specific of this new feature. If you need to use or create a component that will probably be used somewhere else, please refer to `vue_shared/components`. -A good thumb rule to know when you should create a component is to think if +A good rule of thumb to know when you should create a component is to think if it will be reusable elsewhere. For example, tables are used in a quite amount of places across GitLab, a table @@ -321,7 +321,7 @@ We should verify an event has been fired by asserting against the result of the ## Vue.js Expert Role -One should apply to be a Vue.js expert by opening an MR when the Merge Request's they create and review show: +You should only apply to be a Vue.js expert when your own merge requests and your reviews show: - Deep understanding of Vue and Vuex reactivity - Vue and Vuex code are structured according to both official and our guidelines diff --git a/lib/api/ci/runner.rb b/lib/api/ci/runner.rb index 4c96acd3ea7..31be1bb7e3e 100644 --- a/lib/api/ci/runner.rb +++ b/lib/api/ci/runner.rb @@ -108,6 +108,20 @@ module API end optional :job_age, type: Integer, desc: %q(Job should be older than passed age in seconds to be ran on runner) end + + # Since we serialize the build output ourselves to ensure Gitaly + # gRPC calls succeed, we need a custom Grape format to handle + # this: + # 1. Grape will ordinarily call `JSON.dump` when Content-Type is set + # to application/json. To avoid this, we need to define a custom type in + # `content_type` and a custom formatter to go with it. + # 2. Grape will parse the request input with the parser defined for + # `content_type`. If no such parser exists, it will be treated as text. We + # reuse the existing JSON parser to preserve the previous behavior. + content_type :build_json, 'application/json' + formatter :build_json, ->(object, _) { object } + parser :build_json, ::Grape::Parser::Json + post '/request' do authenticate_runner! @@ -128,9 +142,10 @@ module API result = ::Ci::RegisterJobService.new(current_runner).execute(runner_params) if result.valid? - if result.build + if result.build_json Gitlab::Metrics.add_event(:build_found) - present ::Ci::BuildRunnerPresenter.new(result.build), with: Entities::JobRequest::Response + env['api.format'] = :build_json + body result.build_json else Gitlab::Metrics.add_event(:build_not_found) header 'X-GitLab-Last-Update', new_update diff --git a/locale/gitlab.pot b/locale/gitlab.pot index f19ad07a192..cfb37fb12a4 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -5054,6 +5054,9 @@ msgstr "" msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters." msgstr "" +msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters. %{linkStart}More information%{linkEnd}" +msgstr "" + msgid "ClusterIntegration|Copy API URL" msgstr "" diff --git a/spec/frontend/clusters_list/components/ancestor_notice_spec.js b/spec/frontend/clusters_list/components/ancestor_notice_spec.js new file mode 100644 index 00000000000..c931912eaf9 --- /dev/null +++ b/spec/frontend/clusters_list/components/ancestor_notice_spec.js @@ -0,0 +1,51 @@ +import AncestorNotice from '~/clusters_list/components/ancestor_notice.vue'; +import ClusterStore from '~/clusters_list/store'; +import { shallowMount } from '@vue/test-utils'; +import { GlLink, GlSprintf } from '@gitlab/ui'; + +describe('ClustersAncestorNotice', () => { + let store; + let wrapper; + + const createWrapper = () => { + store = ClusterStore({ ancestorHelperPath: '/some/ancestor/path' }); + wrapper = shallowMount(AncestorNotice, { store, stubs: { GlSprintf } }); + return wrapper.vm.$nextTick(); + }; + + beforeEach(() => { + return createWrapper(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('when cluster does not have ancestors', () => { + beforeEach(() => { + store.state.hasAncestorClusters = false; + return wrapper.vm.$nextTick(); + }); + + it('displays no notice', () => { + expect(wrapper.isEmpty()).toBe(true); + }); + }); + + describe('when cluster has ancestors', () => { + beforeEach(() => { + store.state.hasAncestorClusters = true; + return wrapper.vm.$nextTick(); + }); + + it('displays notice text', () => { + expect(wrapper.text()).toContain( + 'Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters.', + ); + }); + + it('displays link', () => { + expect(wrapper.contains(GlLink)).toBe(true); + }); + }); +}); diff --git a/spec/graphql/types/jira_user_type_spec.rb b/spec/graphql/types/jira_user_type_spec.rb new file mode 100644 index 00000000000..6e55efb42f4 --- /dev/null +++ b/spec/graphql/types/jira_user_type_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['JiraUser'] do + specify { expect(described_class.graphql_name).to eq('JiraUser') } + + it 'has the expected fields' do + expect(described_class).to have_graphql_fields( + :jira_account_id, :jira_display_name, :jira_email, :gitlab_id, :gitlab_username, :gitlab_name + ) + end +end diff --git a/spec/helpers/clusters_helper_spec.rb b/spec/helpers/clusters_helper_spec.rb index cebf6235f44..8ad955b61f1 100644 --- a/spec/helpers/clusters_helper_spec.rb +++ b/spec/helpers/clusters_helper_spec.rb @@ -60,18 +60,24 @@ RSpec.describe ClustersHelper do end describe '#js_clusters_list_data' do - it 'displays endpoint path and images' do - js_data = helper.js_clusters_list_data('/path') + subject { helper.js_clusters_list_data('/path') } - expect(js_data[:endpoint]).to eq('/path') + it 'displays endpoint path' do + expect(subject[:endpoint]).to eq('/path') + end + + it 'generates svg image data', :aggregate_failures do + expect(subject.dig(:img_tags, :aws, :path)).to match(%r(/illustrations/logos/amazon_eks|svg)) + expect(subject.dig(:img_tags, :default, :path)).to match(%r(/illustrations/logos/kubernetes|svg)) + expect(subject.dig(:img_tags, :gcp, :path)).to match(%r(/illustrations/logos/google_gke|svg)) - expect(js_data.dig(:img_tags, :aws, :path)).to match(%r(/illustrations/logos/amazon_eks|svg)) - expect(js_data.dig(:img_tags, :default, :path)).to match(%r(/illustrations/logos/kubernetes|svg)) - expect(js_data.dig(:img_tags, :gcp, :path)).to match(%r(/illustrations/logos/google_gke|svg)) + expect(subject.dig(:img_tags, :aws, :text)).to eq('Amazon EKS') + expect(subject.dig(:img_tags, :default, :text)).to eq('Kubernetes Cluster') + expect(subject.dig(:img_tags, :gcp, :text)).to eq('Google GKE') + end - expect(js_data.dig(:img_tags, :aws, :text)).to eq('Amazon EKS') - expect(js_data.dig(:img_tags, :default, :text)).to eq('Kubernetes Cluster') - expect(js_data.dig(:img_tags, :gcp, :text)).to eq('Google GKE') + it 'displays and ancestor_help_path' do + expect(subject[:ancestor_help_path]).to eq('/help/user/group/clusters/index#cluster-precedence') end end diff --git a/spec/requests/api/ci/runner_spec.rb b/spec/requests/api/ci/runner_spec.rb index 20bd59c5c72..8106b7195c8 100644 --- a/spec/requests/api/ci/runner_spec.rb +++ b/spec/requests/api/ci/runner_spec.rb @@ -518,6 +518,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do request_job info: { platform: :darwin } expect(response).to have_gitlab_http_status(:created) + expect(response.headers['Content-Type']).to eq('application/json') expect(response.headers).not_to have_key('X-GitLab-Last-Update') expect(runner.reload.platform).to eq('darwin') expect(json_response['id']).to eq(job.id) @@ -569,6 +570,24 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do end end + context 'when a Gitaly exception is thrown during response' do + before do + allow_next_instance_of(Ci::BuildRunnerPresenter) do |instance| + allow(instance).to receive(:artifacts).and_raise(GRPC::DeadlineExceeded) + end + end + + it 'fails the job as a scheduler failure' do + request_job + + expect(response).to have_gitlab_http_status(:no_content) + expect(job.reload.failed?).to be_truthy + expect(job.failure_reason).to eq('scheduler_failure') + expect(job.runner_id).to eq(runner.id) + expect(job.runner_session).to be_nil + end + end + context 'when GIT_DEPTH is not specified and there is no default git depth for the project' do before do project.update!(ci_default_git_depth: nil) @@ -1090,7 +1109,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do def request_job(token = runner.token, **params) new_params = params.merge(token: token, last_update: last_update) - post api('/jobs/request'), params: new_params, headers: { 'User-Agent' => user_agent } + post api('/jobs/request'), params: new_params.to_json, headers: { 'User-Agent' => user_agent, 'Content-Type': 'application/json' } end end diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb index de4db4f144c..921f5ba4c7e 100644 --- a/spec/services/ci/register_job_service_spec.rb +++ b/spec/services/ci/register_job_service_spec.rb @@ -109,12 +109,14 @@ module Ci end context 'shared runner' do - let(:build) { execute(shared_runner) } + let(:response) { described_class.new(shared_runner).execute } + let(:build) { response.build } it { expect(build).to be_kind_of(Build) } it { expect(build).to be_valid } it { expect(build).to be_running } it { expect(build.runner).to eq(shared_runner) } + it { expect(Gitlab::Json.parse(response.build_json)['id']).to eq(build.id) } end context 'specific runner' do diff --git a/spec/services/jira_import/users_mapper_spec.rb b/spec/services/jira_import/users_mapper_spec.rb index 0fa98736b51..e5e8279a6fb 100644 --- a/spec/services/jira_import/users_mapper_spec.rb +++ b/spec/services/jira_import/users_mapper_spec.rb @@ -29,9 +29,9 @@ RSpec.describe JiraImport::UsersMapper do # mapping is tracked in https://gitlab.com/gitlab-org/gitlab/-/issues/219023 let(:mapped_users) do [ - { jira_account_id: 'abcd', jira_display_name: 'user1', jira_email: nil, gitlab_id: nil }, - { jira_account_id: 'efg', jira_display_name: nil, jira_email: nil, gitlab_id: nil }, - { jira_account_id: 'hij', jira_display_name: 'user3', jira_email: 'user3@example.com', gitlab_id: nil } + { jira_account_id: 'abcd', jira_display_name: 'user1', jira_email: nil, gitlab_id: nil, gitlab_username: nil, gitlab_name: nil }, + { jira_account_id: 'efg', jira_display_name: nil, jira_email: nil, gitlab_id: nil, gitlab_username: nil, gitlab_name: nil }, + { jira_account_id: 'hij', jira_display_name: 'user3', jira_email: 'user3@example.com', gitlab_id: nil, gitlab_username: nil, gitlab_name: nil } ] end |