diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-02-21 18:18:42 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-02-21 18:18:42 +0000 |
commit | 2ee7cff13b1a3929bebf2af7d223e2f445d72fca (patch) | |
tree | b155d3f76bd575b8401544a0d05fdbf595529056 | |
parent | 871e82b7c73283c2c71355e3258a6c9d3b8c0eda (diff) | |
download | gitlab-ce-2ee7cff13b1a3929bebf2af7d223e2f445d72fca.tar.gz |
Add latest changes from gitlab-org/gitlab@master
33 files changed, 297 insertions, 314 deletions
diff --git a/.gitlab/ci/setup.gitlab-ci.yml b/.gitlab/ci/setup.gitlab-ci.yml index 1aeccfcb212..ad500fe0ddc 100644 --- a/.gitlab/ci/setup.gitlab-ci.yml +++ b/.gitlab/ci/setup.gitlab-ci.yml @@ -159,7 +159,7 @@ add-jh-folder: script: - JH_BRANCH=$(./scripts/setup/find-jh-branch.rb) - 'echo "JH_BRANCH: ${JH_BRANCH}"' - - curl --location -o "jh-folder.tar.gz" "https://gitlab.com/gitlab-org/gitlab-jh/gitlab/-/archive/${JH_BRANCH}/gitlab-${JH_BRANCH}.tar.gz?path=jh" + - curl --location -o "jh-folder.tar.gz" "https://gitlab.com/gitlab-org/gitlab-jh-mirrors/gitlab/-/archive/${JH_BRANCH}/gitlab-${JH_BRANCH}.tar.gz?path=jh" - tar -xf "jh-folder.tar.gz" - mv "gitlab-${JH_BRANCH}-jh/jh/" ./ - cp Gemfile.lock jh/ diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 2d2977819dd..96af281acd6 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -f0fdc36e3820ea0722df4e31a00204a7709f129f +69984aee4bae5d28182946aba78737e0acbc635c diff --git a/app/assets/javascripts/clusters_list/components/clusters.vue b/app/assets/javascripts/clusters_list/components/clusters.vue index 7fb3aa3ff7e..59cfdde731d 100644 --- a/app/assets/javascripts/clusters_list/components/clusters.vue +++ b/app/assets/javascripts/clusters_list/components/clusters.vue @@ -6,7 +6,7 @@ import { GlPagination, GlDeprecatedSkeletonLoading as GlSkeletonLoading, GlSprintf, - GlTable, + GlTableLite, GlTooltipDirective, } from '@gitlab/ui'; import { mapState, mapActions } from 'vuex'; @@ -27,7 +27,7 @@ export default { GlPagination, GlSkeletonLoading, GlSprintf, - GlTable, + GlTableLite, NodeErrorHelpText, ClustersEmptyState, }, @@ -229,7 +229,7 @@ export default { <section v-else> <ancestor-notice /> - <gl-table + <gl-table-lite v-if="hasClusters" :items="clusters" :fields="fields" @@ -326,7 +326,7 @@ export default { {{ value }} </gl-badge> </template> - </gl-table> + </gl-table-lite> <clusters-empty-state v-else :is-child-component="isChildComponent" /> diff --git a/app/assets/javascripts/environments/components/delete_environment_modal.vue b/app/assets/javascripts/environments/components/delete_environment_modal.vue index d3d4c7d23d8..3173c2bd644 100644 --- a/app/assets/javascripts/environments/components/delete_environment_modal.vue +++ b/app/assets/javascripts/environments/components/delete_environment_modal.vue @@ -62,7 +62,8 @@ export default { mutation: deleteEnvironmentMutation, variables: { environment: this.environment }, }) - .then(([message]) => { + .then(({ data }) => { + const [message] = data?.deleteEvironment?.errors ?? []; if (message) { createFlash({ message }); } diff --git a/app/assets/javascripts/environments/graphql/resolvers.js b/app/assets/javascripts/environments/graphql/resolvers.js index dc763b77157..2544fd5273c 100644 --- a/app/assets/javascripts/environments/graphql/resolvers.js +++ b/app/assets/javascripts/environments/graphql/resolvers.js @@ -11,6 +11,7 @@ import environmentToRollbackQuery from './queries/environment_to_rollback.query. import environmentToStopQuery from './queries/environment_to_stop.query.graphql'; import environmentToDeleteQuery from './queries/environment_to_delete.query.graphql'; import environmentToChangeCanaryQuery from './queries/environment_to_change_canary.query.graphql'; +import isEnvironmentStoppingQuery from './queries/is_environment_stopping.query.graphql'; import pageInfoQuery from './queries/page_info.query.graphql'; const buildErrors = (errors = []) => ({ @@ -71,11 +72,21 @@ export const resolvers = (endpoint) => ({ }, }, Mutation: { - stopEnvironment(_, { environment }) { + stopEnvironment(_, { environment }, { client }) { + client.writeQuery({ + query: isEnvironmentStoppingQuery, + variables: { environment }, + data: { isEnvironmentStopping: true }, + }); return axios .post(environment.stopPath) .then(() => buildErrors()) .catch(() => { + client.writeQuery({ + query: isEnvironmentStoppingQuery, + variables: { environment }, + data: { isEnvironmentStopping: false }, + }); return buildErrors([ s__('Environments|An error occurred while stopping the environment, please try again'), ]); diff --git a/app/assets/javascripts/performance_bar/components/detailed_metric.vue b/app/assets/javascripts/performance_bar/components/detailed_metric.vue index 1bb82e1d8e6..0640faae8b7 100644 --- a/app/assets/javascripts/performance_bar/components/detailed_metric.vue +++ b/app/assets/javascripts/performance_bar/components/detailed_metric.vue @@ -1,5 +1,5 @@ <script> -import { GlButton, GlModal, GlModalDirective, GlSegmentedControl } from '@gitlab/ui'; +import { GlButton, GlDropdown, GlDropdownItem, GlModal, GlModalDirective } from '@gitlab/ui'; import { __, s__ } from '~/locale'; import { sortOrders, sortOrderOptions } from '../constants'; @@ -9,8 +9,9 @@ export default { components: { RequestWarning, GlButton, + GlDropdown, + GlDropdownItem, GlModal, - GlSegmentedControl, }, directives: { 'gl-modal': GlModalDirective, @@ -156,13 +157,19 @@ export default { </div> </div> </div> - <gl-segmented-control + <gl-dropdown v-if="displaySortOrder" + :text="$options.sortOrderOptions[sortOrder]" + right data-testid="performance-bar-sort-order" - :options="$options.sortOrderOptions" - :checked="sortOrder" - @input="changeSortOrder" - /> + > + <gl-dropdown-item + v-for="option in Object.keys($options.sortOrderOptions)" + :key="option" + @click="changeSortOrder(option)" + >{{ $options.sortOrderOptions[option] }}</gl-dropdown-item + > + </gl-dropdown> </div> <hr /> <table class="table gl-table"> diff --git a/app/assets/javascripts/performance_bar/constants.js b/app/assets/javascripts/performance_bar/constants.js index 9659383edd9..09745797424 100644 --- a/app/assets/javascripts/performance_bar/constants.js +++ b/app/assets/javascripts/performance_bar/constants.js @@ -5,13 +5,7 @@ export const sortOrders = { CHRONOLOGICAL: 'chronological', }; -export const sortOrderOptions = [ - { - value: sortOrders.DURATION, - text: s__('PerformanceBar|Sort by duration'), - }, - { - value: sortOrders.CHRONOLOGICAL, - text: s__('PerformanceBar|Sort chronologically'), - }, -]; +export const sortOrderOptions = { + [sortOrders.DURATION]: s__('PerformanceBar|Sort by duration'), + [sortOrders.CHRONOLOGICAL]: s__('PerformanceBar|Sort chronologically'), +}; diff --git a/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue b/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue index 05e0c3b0be3..41507ca94e2 100644 --- a/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue +++ b/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue @@ -116,7 +116,7 @@ export default { <div v-if="statusHtml" class="gl-mb-2" data-testid="user-popover-status"> <span v-safe-html:[$options.safeHtmlConfig]="statusHtml"></span> </div> - <div v-if="user.bot" class="gl-text-blue-500"> + <div v-if="user.bot && user.websiteUrl" class="gl-text-blue-500"> <gl-icon name="question" /> <gl-link data-testid="user-popover-bot-docs-link" :href="user.websiteUrl"> <gl-sprintf :message="__('Learn more about %{username}')"> diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb index 415f0b35f3a..52c83db3078 100644 --- a/app/models/application_setting_implementation.rb +++ b/app/models/application_setting_implementation.rb @@ -218,7 +218,8 @@ module ApplicationSettingImplementation valid_runner_registrars: VALID_RUNNER_REGISTRAR_TYPES, wiki_page_max_content_bytes: 50.megabytes, container_registry_delete_tags_service_timeout: 250, - container_registry_expiration_policies_worker_capacity: 0, + container_registry_expiration_policies_worker_capacity: 4, + container_registry_cleanup_tags_service_max_list_size: 200, container_registry_import_max_tags_count: 100, container_registry_import_max_retries: 3, container_registry_import_start_max_retries: 50, diff --git a/db/migrate/20220215164709_update_application_settings_container_registry_exp_pol_worker_capacity_default.rb b/db/migrate/20220215164709_update_application_settings_container_registry_exp_pol_worker_capacity_default.rb new file mode 100644 index 00000000000..4b743f84c4d --- /dev/null +++ b/db/migrate/20220215164709_update_application_settings_container_registry_exp_pol_worker_capacity_default.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true +class UpdateApplicationSettingsContainerRegistryExpPolWorkerCapacityDefault < Gitlab::Database::Migration[1.0] + class Settings < ActiveRecord::Base + self.table_name = 'application_settings' + end + + def up + change_column_default(:application_settings, :container_registry_expiration_policies_worker_capacity, from: 0, to: 4) + + current_settings = Settings.first + + if current_settings&.container_registry_expiration_policies_worker_capacity == 0 + current_settings.update!(container_registry_expiration_policies_worker_capacity: 4) + end + end + + def down + change_column_default(:application_settings, :container_registry_expiration_policies_worker_capacity, from: 4, to: 0) + end +end diff --git a/db/schema_migrations/20220215164709 b/db/schema_migrations/20220215164709 new file mode 100644 index 00000000000..60179eaeddd --- /dev/null +++ b/db/schema_migrations/20220215164709 @@ -0,0 +1 @@ +af6d142b77bc2787a520f8cbc63c287823a7a65a2edb3eb488e4f0f4efde9fa8
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index b089e0cde14..e54bf96f887 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -10552,7 +10552,7 @@ CREATE TABLE application_settings ( automatic_purchased_storage_allocation boolean DEFAULT false NOT NULL, encrypted_ci_jwt_signing_key text, encrypted_ci_jwt_signing_key_iv text, - container_registry_expiration_policies_worker_capacity integer DEFAULT 0 NOT NULL, + container_registry_expiration_policies_worker_capacity integer DEFAULT 4 NOT NULL, elasticsearch_analyzers_smartcn_enabled boolean DEFAULT false NOT NULL, elasticsearch_analyzers_smartcn_search boolean DEFAULT false NOT NULL, elasticsearch_analyzers_kuromoji_enabled boolean DEFAULT false NOT NULL, diff --git a/doc/ci/runners/configure_runners.md b/doc/ci/runners/configure_runners.md index 60e21653a45..e5f2b818f9a 100644 --- a/doc/ci/runners/configure_runners.md +++ b/doc/ci/runners/configure_runners.md @@ -59,7 +59,7 @@ How this feature works: ## Be careful with sensitive information -With some [runner executors](https://docs.gitlab.com/runner/executors/README.html), +With some [runner executors](https://docs.gitlab.com/runner/executors/), if you can run a job on the runner, you can get full access to the file system, and thus any code it runs as well as the token of the runner. With shared runners, this means that anyone that runs jobs on the runner, can access anyone else's code that runs on the @@ -70,7 +70,7 @@ to create a clone of a runner and submit false jobs, for example. The above is easily avoided by restricting the usage of shared runners on large public GitLab instances, controlling access to your GitLab instance, -and using more secure [runner executors](https://docs.gitlab.com/runner/executors/README.html). +and using more secure [runner executors](https://docs.gitlab.com/runner/executors/). ### Prevent runners from revealing sensitive information diff --git a/doc/development/pipelines.md b/doc/development/pipelines.md index f3a4f47eb22..012021280bb 100644 --- a/doc/development/pipelines.md +++ b/doc/development/pipelines.md @@ -247,7 +247,7 @@ appending `-jh` to the branch name. If a corresponding JH branch is found, rather than from the default branch. NOTE: -For now, CI will try to fetch the branch on the [GitLab JH mirror](https://gitlab.com/gitlab-org/gitlab-jh/gitlab), so it might take some time for the new JH branch to propagate to the mirror. +For now, CI will try to fetch the branch on the [GitLab JH mirror](https://gitlab.com/gitlab-org/gitlab-jh-mirrors/gitlab), so it might take some time for the new JH branch to propagate to the mirror. ## `undercover` RSpec test diff --git a/doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md b/doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md index 7fb95769fc2..49a9124253d 100644 --- a/doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md +++ b/doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md @@ -169,135 +169,6 @@ docker run \ Where `interface_ip_address` is your local network's interface IP, which you can find with the `ifconfig` command. The same would apply to GDK running with the instance address as `localhost` too. -## Guide to run and debug Monitor tests - -### How to set up - -To run the Monitor tests locally, against the GDK, please follow the preparation steps below: - -1. Complete the [Prerequisites](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/main/doc/howto/auto_devops/index.md#prerequisites-for-gitlab-team-members-only), at least through step 5. Note that the monitor tests do not require permissions to work with GKE because they use [k3s as a Kubernetes cluster provider](https://github.com/rancher/k3s). -1. The test setup deploys the app in a Kubernetes cluster, using the Auto DevOps deployment strategy. -To enable Auto DevOps in GDK, follow the [associated setup](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/main/doc/howto/auto_devops/index.md#setup) instructions. If you have problems, review the [troubleshooting guide](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/main/doc/howto/auto_devops/tips_and_troubleshooting.md) or reach out to the `#gdk` channel in the internal GitLab Slack. -1. Do [secure your GitLab instance](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/main/doc/howto/auto_devops/index.md#secure-your-gitlab-instance) since it is now publicly accessible on `https://[YOUR-PORT].qa-tunnel.gitlab.info`. -1. Install the Kubernetes command line tool known as `kubectl`. Use the [official installation instructions](https://kubernetes.io/docs/tasks/tools/). - -You might see NGINX issues when you run `gdk start` or `gdk restart`. In that case, run `sft login` to revalidate your credentials and regain access the QA Tunnel. - -### How to run - -Navigate to the folder in `/your-gdk/gitlab/qa` and issue the command: - -```shell -QA_DEBUG=true WEBDRIVER_HEADLESS=false GITLAB_ADMIN_USERNAME=rootusername GITLAB_ADMIN_PASSWORD=rootpassword GITLAB_QA_ACCESS_TOKEN=your_token_here GITLAB_QA_ADMIN_ACCESS_TOKEN=your_token_here CLUSTER_API_URL=https://kubernetes.docker.internal:6443 bundle exec bin/qa Test::Instance::All https://[YOUR-PORT].qa-tunnel.gitlab.info/ -- qa/specs/features/browser_ui/8_monitor/all_monitor_core_features_spec.rb --tag kubernetes --tag orchestrated --tag requires_admin -``` - -The following includes more information on the command: - -- `QA_DEBUG` - Set to `true` to verbosely log page object actions. -- `WEBDRIVER_HEADLESS` - When running locally, set to `false` to allow browser tests to be visible - watch your tests being run. -- `GITLAB_ADMIN_USERNAME` - Administrator username to use when adding a license. -- `GITLAB_ADMIN_PASSWORD` - Administrator password to use when adding a license. -- `GITLAB_QA_ACCESS_TOKEN` and `GITLAB_QA_ADMIN_ACCESS_TOKEN` - A valid personal access token with the `api` scope. This is used for API access during tests, and is used in the version that staging is currently running. The `ADMIN_ACCESS_TOKEN` is from a user with administrator access. Used for API access as an administrator during tests. -- `CLUSTER_API_URL` - Use the address `https://kubernetes.docker.internal:6443` . This address is used to enable the cluster to be network accessible while deploying using Auto DevOps. -- `https://[YOUR-PORT].qa-tunnel.gitlab.info/` - The address of your local GDK -- `qa/specs/features/browser_ui/8_monitor/all_monitor_core_features_spec.rb` - The path to the monitor core specs -- `--tag` - the meta-tags used to filter the specs correctly - -At the moment of this writing, there are two specs which run monitor tests: - -- `qa/specs/features/browser_ui/8_monitor/all_monitor_core_features_spec.rb` - has the specs of features in GitLab Free -- `qa/specs/features/ee/browser_ui/8_monitor/all_monitor_features_spec.rb` - has the specs of features for paid GitLab (Enterprise Edition) - -### How to debug - -The monitor tests follow this setup flow: - -1. Creates a k3s cluster on your local machine. -1. Creates a project that has Auto DevOps enabled and uses an Express template (NodeJS) for the app to be deployed. -1. Associates the created cluster to the project and installs GitLab Runner, Prometheus and Ingress which are the needed components for a successful deployment. -1. Creates a CI pipeline with 2 jobs (`build` and `production`) to deploy the app on the Kubernetes cluster. -1. Goes to Operation > Metrics menu to verify data is being received and the app is being monitored successfully. - -The test requires a number of components. The setup requires time to collect the metrics of a real deployment. -The complexity of the setup may lead to problems unrelated to the app. The following sections include common strategies to debug possible issues. - -#### Deployment with Auto DevOps - -When debugging issues in the CI or locally in the CLI, open the Kubernetes job in the pipeline. -In the job log window, click on the top right icon labeled as *"Show complete raw"* to reveal raw job logs. -You can now search through the logs for *Job log*, which matches delimited sections like this one: - -```shell -------- Job log: ------- -``` - -A Job log is a subsection within these logs, related to app deployment. We use two jobs: `build` and `production`. -You can find the root causes of deployment failures in these logs, which can compromise the entire test. -If a `build` job fails, the `production` job doesn't run, and the test fails. - -The long test setup does not take screenshots of failures, which is a known [issue](https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/270). -However, if the spec fails (after a successful deployment) then you should be able to find screenshots which display the feature failure. -To access them in CI, go to the main job log window, look on the left side panel's Job artifacts section, and click Browse. - -#### Common issues - -**Container Registry** - -When enabling Auto DevOps in the GDK, you may see issues with the Container Registry, which stores -images of the app to be deployed. - -You can access the Registry is available by opening an existing project. On the left hand menu, -select `Packages & Registries > Container Registries`. If the Registry is available, this page should load normally. - -Also, the Registry should be running in Docker: - -```shell -$ docker ps - -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -f035f339506c registry.gitlab.com/gitlab-org/build/cng/gitlab-container-registry:v2.9.1-gitlab "/bin/sh -c 'exec /b…" 3 hours ago Up 3 hours 0.0.0.0:5000->5000/tcp jovial_proskuriakova -``` - -The `gdk status` command shows if the registry is running: - -```shell -run: ./services/registry: (pid 2662) 10875s, normally down; run: log: (pid 65148) 177993s -run: ./services/tunnel_gitlab: (pid 2650) 10875s, normally down; run: log: (pid 65154) 177993s -run: ./services/tunnel_registry: (pid 2651) 10875s, normally down; run: log: (pid 65155) 177993s -``` - -Also, restarting Docker and then, on the Terminal, issue the command -`docker login https://[YOUR-REGISTRY-PORT].qa-tunnel.gitlab.info:443` and use -the GDK credentials to sign in. Note that the Registry port and GDK port aren't -the same. When configuring Auto DevOps in GDK, the `gdk reconfigure` command -outputs the port of the Registry: - -```shell -********************************************* -Tunnel URLs - -GitLab: https://[PORT].qa-tunnel.gitlab.info -Registry: https://[PORT].qa-tunnel.gitlab.info -********************************************* -``` - -These Tunnel URLs are used by the QA SSH Tunnel generated when enabling Auto DevOps on the GDK. - -**Pod Eviction** - -Pod eviction happens when a node in a Kubernetes cluster is running out of memory or disk. After many local deployments this issue can happen. The UI shows that installing Prometheus, GitLab Runner and Ingress failed. How to be sure it is an Eviction? While the test is running, open another Terminal window and debug the current Kubernetes cluster by `kubectl get pods --all-namespaces`. If you observe that Pods have *Evicted status* such as the install-runner here: - -```shell -$ kubectl get pods --all-namespaces - -NAMESPACE NAME READY STATUS RESTARTS AGE -gitlab-managed-apps install-ingress 0/1 Pending 0 25s -gitlab-managed-apps install-prometheus 0/1 Pending 0 12s -gitlab-managed-apps install-runner 0/1 Evicted 0 75s -``` - -You can free some memory with either of the following commands: `docker prune system` or `docker prune volume`. - ## Geo tests Geo end-to-end tests can run locally against a [Geo GDK setup](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/main/doc/howto/geo.md) or on Geo spun up in Docker containers. diff --git a/doc/user/packages/container_registry/reduce_container_registry_storage.md b/doc/user/packages/container_registry/reduce_container_registry_storage.md index a2a22f138e3..c72385a331d 100644 --- a/doc/user/packages/container_registry/reduce_container_registry_storage.md +++ b/doc/user/packages/container_registry/reduce_container_registry_storage.md @@ -182,11 +182,16 @@ the process can take time to finish. To prevent server resource starvation, the following application settings are available: -- `container_registry_expiration_policies_worker_capacity`. The maximum number of cleanup workers running concurrently. This must be greater than `1`. - We recommend starting with a low number and increasing it after monitoring the resources used by the background workers. -- `container_registry_delete_tags_service_timeout`. The maximum time, in seconds, that the cleanup process can take to delete a batch of tags. -- `container_registry_cleanup_tags_service_max_list_size`. The maximum number of tags that can be deleted in a single execution. Additional tags must be deleted in another execution. - We recommend starting with a low number, like `100`, and increasing it after monitoring that container images are properly deleted. +- `container_registry_expiration_policies_worker_capacity`: the maximum number of cleanup workers + running concurrently. This must be greater than or equal to `0`. We recommend starting with a low + number and increasing it after monitoring the resources used by the background workers. To remove + all workers and not execute the cleanup policies, set this to `0`. The default value is `4`. +- `container_registry_delete_tags_service_timeout`: the maximum time (in seconds) that the cleanup + process can take to delete a batch of tags. The default value is `250`. +- `container_registry_cleanup_tags_service_max_list_size`: the maximum number of tags that can be + deleted in a single execution. Additional tags must be deleted in another execution. We recommend + starting with a low number and increasing it after monitoring that container images are properly + deleted. The default value is `200`. For self-managed instances, those settings can be updated in the [Rails console](../../../administration/operations/rails_console.md#starting-a-rails-console-session): diff --git a/lib/gitlab/path_regex.rb b/lib/gitlab/path_regex.rb index 06a26c4830f..6f497c6376d 100644 --- a/lib/gitlab/path_regex.rb +++ b/lib/gitlab/path_regex.rb @@ -255,7 +255,7 @@ module Gitlab end def container_image_regex - @container_image_regex ||= %r{([\w\.-]+\/){0,1}[\w\.-]+}.freeze + @container_image_regex ||= %r{([\w\.-]+\/){0,4}[\w\.-]+}.freeze end def container_image_blob_sha_regex diff --git a/qa/qa/resource/project.rb b/qa/qa/resource/project.rb index 4550e00f87e..e0f9700ce5a 100644 --- a/qa/qa/resource/project.rb +++ b/qa/qa/resource/project.rb @@ -262,7 +262,7 @@ module QA reload!.api_response[:default_branch] || Runtime::Env.default_branch end - def import_status + def project_import_status response = get(request_url("/projects/#{id}/import")) unless response.code == HTTP_STATUS_OK @@ -276,7 +276,7 @@ module QA Runtime::Logger.error("Failed relations: #{result[:failed_relations]}") end - result[:import_status] + result end def commits(auto_paginate: false, attempts: 0) diff --git a/qa/qa/specs/features/api/1_manage/import_github_repo_spec.rb b/qa/qa/specs/features/api/1_manage/import_github_repo_spec.rb index a51d733d484..00a099d5730 100644 --- a/qa/qa/specs/features/api/1_manage/import_github_repo_spec.rb +++ b/qa/qa/specs/features/api/1_manage/import_github_repo_spec.rb @@ -31,11 +31,13 @@ module QA end it 'imports Github repo via api', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347670' do - imported_project # import the project + imported_project.reload! # import the project - expect { imported_project.reload!.import_status }.to eventually_eq('finished').within(max_duration: 90) + expect { imported_project.project_import_status[:import_status] }.to eventually_eq('finished') + .within(max_duration: 90, sleep_interval: 1) aggregate_failures do + verify_status_data verify_repository_import verify_commits_import verify_labels_import @@ -46,6 +48,19 @@ module QA end end + def verify_status_data + stats = imported_project.project_import_status.dig(:stats, :imported) + expect(stats).to eq( + label: 10, + milestone: 1, + issue: 2, + note: 2, + pull_request: 1, + pull_request_review: 2, + diff_note: 2 + ) + end + def verify_repository_import expect(imported_project.api_response).to include( description: 'A new repo for test', diff --git a/qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb b/qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb index c46de0ac514..3d8f832df9e 100644 --- a/qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb +++ b/qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb @@ -99,21 +99,29 @@ module QA import_time: @import_time, github: { project_name: github_repo, - branches: gh_branches, - commits: gh_commits, - labels: gh_labels, - milestones: gh_milestones, - prs: gh_prs, - issues: gh_issues + branches: gh_branches.length, + commits: gh_commits.length, + labels: gh_labels.length, + milestones: gh_milestones.length, + prs: gh_prs.length, + pr_comments: gh_prs.sum { |_k, v| v.length }, + issues: gh_issues.length, + issue_comments: gh_issues.sum { |_k, v| v.length } }, gitlab: { project_name: imported_project.path_with_namespace, - branches: gl_branches, - commits: gl_commits, - labels: gl_labels, - milestones: gl_milestones, - mrs: mrs, - issues: gl_issues + branches: gl_branches.length, + commits: gl_commits.length, + labels: gl_labels.length, + milestones: gl_milestones.length, + mrs: mrs.length, + mr_comments: mrs.sum { |_k, v| v.length }, + issues: gl_issues.length, + issue_comments: gl_issues.sum { |_k, v| v.length } + }, + not_imported: { + mrs: @mr_diff, + issues: @issue_diff } }.to_json ) @@ -125,14 +133,18 @@ module QA ) do start = Time.now - Runtime::Logger.info("Importing project '#{imported_project.full_path}'") # import the project and log path - fetch_github_objects # fetch all objects right after import has started + # import the project and log path + Runtime::Logger.info("Importing project '#{imported_project.reload!.full_path}'") + # fetch all objects right after import has started + fetch_github_objects import_status = lambda do - imported_project.reload!.import_status.tap do |status| + imported_project.project_import_status[:import_status].tap do |status| + # fail fast if import explicitly failed raise "Import of '#{imported_project.name}' failed!" if status == 'failed' end end + expect(import_status).to eventually_eq('finished').within(max_duration: import_max_duration, sleep_interval: 30) @import_time = Time.now - start @@ -177,7 +189,7 @@ module QA # @return [void] def verify_merge_requests_import logger.debug("== Verifying merge request import ==") - verify_mrs_or_issues('mr') + @mr_diff = verify_mrs_or_issues('mr') end # Verify imported issues and issue comments @@ -185,7 +197,7 @@ module QA # @return [void] def verify_issues_import logger.debug("== Verifying issue import ==") - verify_mrs_or_issues('issue') + @issue_diff = verify_mrs_or_issues('issue') end # Verify imported labels @@ -207,47 +219,67 @@ module QA private - # Verify imported mrs or issues + # Verify imported mrs or issues and return diff # # @param [String] type verification object, 'mrs' or 'issues' - # @return [void] + # @return [Hash] def verify_mrs_or_issues(type) - msg = ->(title) { "expected #{type} with title '#{title}' to have" } - # Compare length to have easy to read overview how many objects are missing + # expected = type == 'mr' ? mrs : gl_issues actual = type == 'mr' ? gh_prs : gh_issues count_msg = "Expected to contain same amount of #{type}s. Gitlab: #{expected.length}, Github: #{actual.length}" expect(expected.length).to eq(actual.length), count_msg logger.debug("= Comparing #{type}s =") - actual.each do |title, actual_item| - print "." # indicate that it is still going but don't spam the output with newlines + missing_comments = verify_comments(type, actual, expected) + { + "#{type}s": actual.keys - expected.keys, + "#{type}_comments": missing_comments + } + end + + # Verify imported comments + # + # @param [String] type verification object, 'mrs' or 'issues' + # @param [Hash] actual + # @param [Hash] expected + # @return [Hash] + def verify_comments(type, actual, expected) + actual.each_with_object({}) do |(title, actual_item), missing_comments| + msg = "expected #{type} with title '#{title}' to have" expected_item = expected[title] # Print title in the error message to see which object is missing - expect(expected_item).to be_truthy, "#{msg.call(title)} been imported" + # + expect(expected_item).to be_truthy, "#{msg} been imported" next unless expected_item # Print difference in the description + # expected_body = expected_item[:body] actual_body = actual_item[:body] body_msg = <<~MSG - #{msg.call(title)} same description. diff:\n#{differ.diff(expected_item[:body], actual_item[:body])} + #{msg} same description. diff:\n#{differ.diff(expected_item[:body], actual_item[:body])} MSG expect(expected_body).to include(actual_body), body_msg # Print amount difference first + # expected_comments = expected_item[:comments] actual_comments = actual_item[:comments] comment_count_msg = <<~MSG - #{msg.call(title)} same amount of comments. Gitlab: #{expected_comments.length}, Github: #{actual_comments.length} + #{msg} same amount of comments. Gitlab: #{expected_comments.length}, Github: #{actual_comments.length} MSG expect(expected_comments.length).to eq(actual_comments.length), comment_count_msg expect(expected_comments).to match_array(actual_comments) + + # Save missing comments + # + comment_diff = actual_comments - expected_comments + missing_comments[title] = comment_diff unless comment_diff.empty? end - puts # print newline after last print to make output pretty end # Imported project branches @@ -339,12 +371,12 @@ module QA end end - # Remove added prefixes by importer + # Remove added prefixes and legacy diff format # # @param [String] body # @return [String] def sanitize(body) - body.gsub(/\*Created by: \S+\*\n\n/, "") + body.gsub(/\*Created by: \S+\*\n\n/, "").gsub(/suggestion:-\d+\+\d+/, "suggestion\r") end # Save json as file diff --git a/scripts/setup/find-jh-branch.rb b/scripts/setup/find-jh-branch.rb index 89aa1492939..a7c1cafd74c 100755 --- a/scripts/setup/find-jh-branch.rb +++ b/scripts/setup/find-jh-branch.rb @@ -8,7 +8,7 @@ require_relative '../api/default_options' class FindJhBranch JH_DEFAULT_BRANCH = 'main-jh' - JH_PROJECT_PATH = 'gitlab-org/gitlab-jh/gitlab' + JH_PROJECT_PATH = 'gitlab-org/gitlab-jh-mirrors/gitlab' BranchNotFound = Class.new(RuntimeError) def run diff --git a/scripts/trigger-build.rb b/scripts/trigger-build.rb index 17cbd91a8ee..a3356c664d1 100755 --- a/scripts/trigger-build.rb +++ b/scripts/trigger-build.rb @@ -324,8 +324,8 @@ module Trigger def invoke!(post_comment: false, downstream_job_name: nil) pipeline = super gitlab = gitlab_client(:upstream) - project_path = base_variables['TOP_UPSTREAM_SOURCE_PROJECT'] - merge_request_id = base_variables['TOP_UPSTREAM_MERGE_REQUEST_IID'] + project_path = variables['TOP_UPSTREAM_SOURCE_PROJECT'] + merge_request_id = variables['TOP_UPSTREAM_MERGE_REQUEST_IID'] comment = "<!-- #{IDENTIFIABLE_NOTE_TAG} --> \nStarted database testing [pipeline](https://ops.gitlab.net/#{downstream_project_path}/-/pipelines/#{pipeline.id}) " \ "(limited access). This comment will be updated once the pipeline has finished running." diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb index 25ff4022454..2155d157186 100644 --- a/spec/features/admin/admin_runners_spec.rb +++ b/spec/features/admin/admin_runners_spec.rb @@ -11,6 +11,8 @@ RSpec.describe "Admin Runners" do admin = create(:admin) sign_in(admin) gitlab_enable_admin_mode_sign_in(admin) + + wait_for_requests end describe "Runners page", :js do diff --git a/spec/features/expand_collapse_diffs_spec.rb b/spec/features/expand_collapse_diffs_spec.rb index 63e16946a0b..98282e47488 100644 --- a/spec/features/expand_collapse_diffs_spec.rb +++ b/spec/features/expand_collapse_diffs_spec.rb @@ -13,6 +13,8 @@ RSpec.describe 'Expand and collapse diffs', :js do sign_in(admin) gitlab_enable_admin_mode_sign_in(admin) + wait_for_requests + # Ensure that undiffable.md is in .gitattributes project.repository.copy_gitattributes(branch) visit project_commit_path(project, project.commit(branch)) diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb index 1f1824c897e..7f5cf2359a3 100644 --- a/spec/features/triggers_spec.rb +++ b/spec/features/triggers_spec.rb @@ -17,6 +17,8 @@ RSpec.describe 'Triggers', :js do @project.add_guest(guest_user) visit project_settings_ci_cd_path(@project) + + wait_for_requests end shared_examples 'triggers page' do diff --git a/spec/frontend/clusters_list/components/clusters_spec.js b/spec/frontend/clusters_list/components/clusters_spec.js index 82e667093aa..7763cc8ade4 100644 --- a/spec/frontend/clusters_list/components/clusters_spec.js +++ b/spec/frontend/clusters_list/components/clusters_spec.js @@ -2,7 +2,7 @@ import { GlLoadingIcon, GlPagination, GlDeprecatedSkeletonLoading as GlSkeletonLoading, - GlTable, + GlTableLite, } from '@gitlab/ui'; import * as Sentry from '@sentry/browser'; import { mount } from '@vue/test-utils'; @@ -41,7 +41,7 @@ describe('Clusters', () => { const findLoader = () => wrapper.findComponent(GlLoadingIcon); const findPaginatedButtons = () => wrapper.findComponent(GlPagination); - const findTable = () => wrapper.findComponent(GlTable); + const findTable = () => wrapper.findComponent(GlTableLite); const findStatuses = () => findTable().findAll('.js-status'); const findEmptyState = () => wrapper.findComponent(ClustersEmptyState); @@ -51,7 +51,7 @@ describe('Clusters', () => { const createWrapper = ({ propsData = {} }) => { store = ClusterStore(entryData); - wrapper = mount(Clusters, { propsData, provide: provideData, store, stubs: { GlTable } }); + wrapper = mount(Clusters, { propsData, provide: provideData, store, stubs: { GlTableLite } }); return axios.waitForAll(); }; diff --git a/spec/frontend/environments/delete_environment_modal_spec.js b/spec/frontend/environments/delete_environment_modal_spec.js index 50c4ca00009..48e4f661c1d 100644 --- a/spec/frontend/environments/delete_environment_modal_spec.js +++ b/spec/frontend/environments/delete_environment_modal_spec.js @@ -5,8 +5,11 @@ import VueApollo from 'vue-apollo'; import { s__, sprintf } from '~/locale'; import DeleteEnvironmentModal from '~/environments/components/delete_environment_modal.vue'; import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import createFlash from '~/flash'; import { resolvedEnvironment } from './graphql/mock_data'; +jest.mock('~/flash'); Vue.use(VueApollo); describe('~/environments/components/delete_environment_modal.vue', () => { @@ -54,6 +57,34 @@ describe('~/environments/components/delete_environment_modal.vue', () => { await nextTick(); + expect(createFlash).not.toHaveBeenCalled(); + + expect(deleteResolver).toHaveBeenCalledWith( + expect.anything(), + { environment: resolvedEnvironment }, + expect.anything(), + expect.anything(), + ); + }); + + it('should flash a message on error', async () => { + createComponent({ apolloProvider: mockApollo }); + + deleteResolver.mockRejectedValue(); + + wrapper.findComponent(GlModal).vm.$emit('primary'); + + await waitForPromises(); + + expect(createFlash).toHaveBeenCalledWith( + expect.objectContaining({ + message: s__( + 'Environments|An error occurred while deleting the environment. Check if the environment stopped; if not, stop it and try again.', + ), + captureError: true, + }), + ); + expect(deleteResolver).toHaveBeenCalledWith( expect.anything(), { environment: resolvedEnvironment }, diff --git a/spec/frontend/environments/graphql/resolvers_spec.js b/spec/frontend/environments/graphql/resolvers_spec.js index 21d7e09bad5..32c0cc42a96 100644 --- a/spec/frontend/environments/graphql/resolvers_spec.js +++ b/spec/frontend/environments/graphql/resolvers_spec.js @@ -7,6 +7,7 @@ import environmentToDelete from '~/environments/graphql/queries/environment_to_d import environmentToStopQuery from '~/environments/graphql/queries/environment_to_stop.query.graphql'; import createMockApollo from 'helpers/mock_apollo_helper'; import pollIntervalQuery from '~/environments/graphql/queries/poll_interval.query.graphql'; +import isEnvironmentStoppingQuery from '~/environments/graphql/queries/is_environment_stopping.query.graphql'; import pageInfoQuery from '~/environments/graphql/queries/page_info.query.graphql'; import { TEST_HOST } from 'helpers/test_constants'; import { @@ -136,11 +137,36 @@ describe('~/frontend/environments/graphql/resolvers', () => { it('should post to the stop environment path', async () => { mock.onPost(ENDPOINT).reply(200); - await mockResolvers.Mutation.stopEnvironment(null, { environment: { stopPath: ENDPOINT } }); + const client = { writeQuery: jest.fn() }; + const environment = { stopPath: ENDPOINT }; + await mockResolvers.Mutation.stopEnvironment(null, { environment }, { client }); expect(mock.history.post).toContainEqual( expect.objectContaining({ url: ENDPOINT, method: 'post' }), ); + + expect(client.writeQuery).toHaveBeenCalledWith({ + query: isEnvironmentStoppingQuery, + variables: { environment }, + data: { isEnvironmentStopping: true }, + }); + }); + it('should set is stopping to false if stop fails', async () => { + mock.onPost(ENDPOINT).reply(500); + + const client = { writeQuery: jest.fn() }; + const environment = { stopPath: ENDPOINT }; + await mockResolvers.Mutation.stopEnvironment(null, { environment }, { client }); + + expect(mock.history.post).toContainEqual( + expect.objectContaining({ url: ENDPOINT, method: 'post' }), + ); + + expect(client.writeQuery).toHaveBeenCalledWith({ + query: isEnvironmentStoppingQuery, + variables: { environment }, + data: { isEnvironmentStopping: false }, + }); }); }); describe('rollbackEnvironment', () => { diff --git a/spec/frontend/performance_bar/components/detailed_metric_spec.js b/spec/frontend/performance_bar/components/detailed_metric_spec.js index c35bd772c86..2ae36740dfb 100644 --- a/spec/frontend/performance_bar/components/detailed_metric_spec.js +++ b/spec/frontend/performance_bar/components/detailed_metric_spec.js @@ -1,10 +1,11 @@ import { shallowMount } from '@vue/test-utils'; +import { GlDropdownItem } from '@gitlab/ui'; import { nextTick } from 'vue'; import { trimText } from 'helpers/text_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import DetailedMetric from '~/performance_bar/components/detailed_metric.vue'; import RequestWarning from '~/performance_bar/components/request_warning.vue'; -import { sortOrders } from '~/performance_bar/constants'; +import { sortOrders, sortOrderOptions } from '~/performance_bar/constants'; describe('detailedMetric', () => { let wrapper; @@ -29,7 +30,13 @@ describe('detailedMetric', () => { const findExpandBacktraceBtns = () => wrapper.findAllByTestId('backtrace-expand-btn'); const findExpandedBacktraceBtnAtIndex = (index) => findExpandBacktraceBtns().at(index); const findDetailsLabel = () => wrapper.findByTestId('performance-bar-details-label'); - const findSortOrderSwitcher = () => wrapper.findByTestId('performance-bar-sort-order'); + const findSortOrderDropdown = () => wrapper.findByTestId('performance-bar-sort-order'); + const clickSortOrderDropdownItem = (sortOrder) => + findSortOrderDropdown() + .findAllComponents(GlDropdownItem) + .filter((item) => item.text() === sortOrderOptions[sortOrder]) + .at(0) + .vm.$emit('click'); const findEmptyDetailNotice = () => wrapper.findByTestId('performance-bar-empty-detail-notice'); const findAllDetailDurations = () => wrapper.findAllByTestId('performance-item-duration').wrappers.map((w) => w.text()); @@ -86,7 +93,7 @@ describe('detailedMetric', () => { }); it('does not display sort by switcher', () => { - expect(findSortOrderSwitcher().exists()).toBe(false); + expect(findSortOrderDropdown().exists()).toBe(false); }); }); @@ -216,7 +223,7 @@ describe('detailedMetric', () => { }); it('does not display sort by switcher', () => { - expect(findSortOrderSwitcher().exists()).toBe(false); + expect(findSortOrderDropdown().exists()).toBe(false); }); it('adds a modal with a table of the details', () => { @@ -323,14 +330,15 @@ describe('detailedMetric', () => { }); it('displays sort by switcher', () => { - expect(findSortOrderSwitcher().exists()).toBe(true); + expect(findSortOrderDropdown().exists()).toBe(true); }); - it('allows switch sorting orders', async () => { - findSortOrderSwitcher().vm.$emit('input', sortOrders.CHRONOLOGICAL); + it('changes sortOrder on select', async () => { + clickSortOrderDropdownItem(sortOrders.CHRONOLOGICAL); await nextTick(); expect(findAllDetailDurations()).toEqual(['23ms', '100ms', '75ms']); - findSortOrderSwitcher().vm.$emit('input', sortOrders.DURATION); + + clickSortOrderDropdownItem(sortOrders.DURATION); await nextTick(); expect(findAllDetailDurations()).toEqual(['100ms', '75ms', '23ms']); }); diff --git a/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js b/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js index 09633daf587..3329199a46b 100644 --- a/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js +++ b/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js @@ -271,6 +271,12 @@ describe('User Popover Component', () => { expect(securityBotDocsLink.text()).toBe('Learn more about GitLab Security Bot'); }); + it("does not show a link to the bot's documentation if there is no website_url", () => { + createWrapper({ user: { ...SECURITY_BOT_USER, websiteUrl: null } }); + const securityBotDocsLink = findSecurityBotDocsLink(); + expect(securityBotDocsLink.exists()).toBe(false); + }); + it("doesn't escape user's name", () => { createWrapper({ user: { ...SECURITY_BOT_USER, name: '%<>\';"' } }); const securityBotDocsLink = findSecurityBotDocsLink(); diff --git a/spec/lib/gitlab/path_regex_spec.rb b/spec/lib/gitlab/path_regex_spec.rb index f0ba0f0459d..9876387512b 100644 --- a/spec/lib/gitlab/path_regex_spec.rb +++ b/spec/lib/gitlab/path_regex_spec.rb @@ -549,10 +549,11 @@ RSpec.describe Gitlab::PathRegex do it { is_expected.to match('gitlab-foss') } it { is_expected.to match('gitlab_foss') } it { is_expected.to match('gitlab-org/gitlab-foss') } + it { is_expected.to match('a/b/c/d/e') } it { is_expected.to match('100px.com/100px.ruby') } - it 'only matches at most one slash' do - expect(subject.match('foo/bar/baz')[0]).to eq('foo/bar') + it 'does not match beyond 4 slashes' do + expect(subject.match('foo/bar/baz/buz/zip/zap/zoo')[0]).to eq('foo/bar/baz/buz/zip') end it 'does not match other non-word characters' do diff --git a/spec/migrations/update_application_settings_container_registry_exp_pol_worker_capacity_default_spec.rb b/spec/migrations/update_application_settings_container_registry_exp_pol_worker_capacity_default_spec.rb new file mode 100644 index 00000000000..842456089fe --- /dev/null +++ b/spec/migrations/update_application_settings_container_registry_exp_pol_worker_capacity_default_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe UpdateApplicationSettingsContainerRegistryExpPolWorkerCapacityDefault do + let(:settings) { table(:application_settings) } + + context 'with no rows in the application_settings table' do + it 'does not insert a row' do + expect { migrate! }.to not_change { settings.count } + end + end + + context 'with a row in the application_settings table' do + before do + settings.create!(container_registry_expiration_policies_worker_capacity: capacity) + end + + context 'with container_registry_expiration_policy_worker_capacity set to a value different than 0' do + let(:capacity) { 1 } + + it 'does not update the row' do + expect { migrate! } + .to not_change { settings.count } + .and not_change { settings.first.container_registry_expiration_policies_worker_capacity } + end + end + + context 'with container_registry_expiration_policy_worker_capacity set to 0' do + let(:capacity) { 0 } + + it 'updates the existing row' do + expect { migrate! } + .to not_change { settings.count } + .and change { settings.first.container_registry_expiration_policies_worker_capacity }.from(0).to(4) + end + end + end +end diff --git a/workhorse/internal/upstream/metrics.go b/workhorse/internal/upstream/metrics.go index 1a11bdc8b53..27cc6bb045b 100644 --- a/workhorse/internal/upstream/metrics.go +++ b/workhorse/internal/upstream/metrics.go @@ -6,6 +6,8 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promhttp" + + "gitlab.com/gitlab-org/labkit/metrics" ) const ( @@ -13,95 +15,7 @@ const ( httpSubsystem = "http" ) -func secondsDurationBuckets() []float64 { - return []float64{ - 0.005, /* 5ms */ - 0.025, /* 25ms */ - 0.1, /* 100ms */ - 0.5, /* 500ms */ - 1.0, /* 1s */ - 10.0, /* 10s */ - 30.0, /* 30s */ - 60.0, /* 1m */ - 300.0, /* 10m */ - } -} - -func byteSizeBuckets() []float64 { - return []float64{ - 10, - 64, - 256, - 1024, /* 1kB */ - 64 * 1024, /* 64kB */ - 256 * 1024, /* 256kB */ - 1024 * 1024, /* 1mB */ - 64 * 1024 * 1024, /* 64mB */ - } -} - var ( - httpInFlightRequests = promauto.NewGauge(prometheus.GaugeOpts{ - Namespace: namespace, - Subsystem: httpSubsystem, - Name: "in_flight_requests", - Help: "A gauge of requests currently being served by workhorse.", - }) - - httpRequestsTotal = promauto.NewCounterVec( - prometheus.CounterOpts{ - Namespace: namespace, - Subsystem: httpSubsystem, - Name: "requests_total", - Help: "A counter for requests to workhorse.", - }, - []string{"code", "method", "route"}, - ) - - httpRequestDurationSeconds = promauto.NewHistogramVec( - prometheus.HistogramOpts{ - Namespace: namespace, - Subsystem: httpSubsystem, - Name: "request_duration_seconds", - Help: "A histogram of latencies for requests to workhorse.", - Buckets: secondsDurationBuckets(), - }, - []string{"code", "method", "route"}, - ) - - httpRequestSizeBytes = promauto.NewHistogramVec( - prometheus.HistogramOpts{ - Namespace: namespace, - Subsystem: httpSubsystem, - Name: "request_size_bytes", - Help: "A histogram of sizes of requests to workhorse.", - Buckets: byteSizeBuckets(), - }, - []string{"code", "method", "route"}, - ) - - httpResponseSizeBytes = promauto.NewHistogramVec( - prometheus.HistogramOpts{ - Namespace: namespace, - Subsystem: httpSubsystem, - Name: "response_size_bytes", - Help: "A histogram of response sizes for requests to workhorse.", - Buckets: byteSizeBuckets(), - }, - []string{"code", "method", "route"}, - ) - - httpTimeToWriteHeaderSeconds = promauto.NewHistogramVec( - prometheus.HistogramOpts{ - Namespace: namespace, - Subsystem: httpSubsystem, - Name: "time_to_write_header_seconds", - Help: "A histogram of request durations until the response headers are written.", - Buckets: secondsDurationBuckets(), - }, - []string{"code", "method", "route"}, - ) - httpGeoProxiedRequestsTotal = promauto.NewCounterVec( prometheus.CounterOpts{ Namespace: namespace, @@ -111,19 +25,12 @@ var ( }, []string{"code", "method", "route"}, ) + + buildHandler = metrics.NewHandlerFactory(metrics.WithNamespace(namespace), metrics.WithLabels("route")) ) func instrumentRoute(next http.Handler, method string, regexpStr string) http.Handler { - handler := next - - handler = promhttp.InstrumentHandlerCounter(httpRequestsTotal.MustCurryWith(map[string]string{"route": regexpStr}), handler) - handler = promhttp.InstrumentHandlerDuration(httpRequestDurationSeconds.MustCurryWith(map[string]string{"route": regexpStr}), handler) - handler = promhttp.InstrumentHandlerInFlight(httpInFlightRequests, handler) - handler = promhttp.InstrumentHandlerRequestSize(httpRequestSizeBytes.MustCurryWith(map[string]string{"route": regexpStr}), handler) - handler = promhttp.InstrumentHandlerResponseSize(httpResponseSizeBytes.MustCurryWith(map[string]string{"route": regexpStr}), handler) - handler = promhttp.InstrumentHandlerTimeToWriteHeader(httpTimeToWriteHeaderSeconds.MustCurryWith(map[string]string{"route": regexpStr}), handler) - - return handler + return buildHandler(next, metrics.WithLabelValues(map[string]string{"route": regexpStr})) } func instrumentGeoProxyRoute(next http.Handler, method string, regexpStr string) http.Handler { |