Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-04-06 06:09:19 +00:00
parent f098e6d3d2
commit cce8cf03d3
21 changed files with 567 additions and 236 deletions

View File

@ -31,6 +31,15 @@ Attach the screenshot and HTML snapshot of the page from the job's artifacts:
1. Open the `gitlab-qa-run-2020-*/gitlab-{ce,ee}-qa-*/{,ee}/{api,browser_ui}/<path to failed test>` folder.
1. Select the `.png` and `.html` files that appears in the job logs (look for `HTML screenshot: /path/to/html/page.html` / `Image screenshot: `/path/to/html/page.png`).
1. Drag and drop them here.
Note: You don't need to include a screenshot if the information it contains can be included as text. Include the text instead.
E.g., error 500/404, "Retry later" errors, etc.
If you include multiple screenshots it can be helpful to hide all but the first in a details/summary element, to avoid excessive scrolling:
<details><summary>Expand for screenshot</summary>
drag and drop the screenshot here
</details>
-->
### Possible fixes

View File

@ -0,0 +1,79 @@
# frozen_string_literal: true
class JiraImportState < ApplicationRecord
include AfterCommitQueue
self.table_name = 'jira_imports'
STATUSES = { initial: 0, scheduled: 1, started: 2, failed: 3, finished: 4 }.freeze
belongs_to :project
belongs_to :user
belongs_to :label
validates :project, presence: true
validates :jira_project_key, presence: true
validates :jira_project_name, presence: true
validates :jira_project_xid, presence: true
validates :project, uniqueness: {
conditions: -> { where.not(status: STATUSES.values_at(:failed, :finished)) },
message: _('Cannot have multiple Jira imports running at the same time')
}
state_machine :status, initial: :initial do
event :schedule do
transition initial: :scheduled
end
event :start do
transition scheduled: :started
end
event :finish do
transition started: :finished
end
event :do_fail do
transition [:initial, :scheduled, :started] => :failed
end
after_transition initial: :scheduled do |state, _|
state.run_after_commit do
job_id = Gitlab::JiraImport::Stage::StartImportWorker.perform_async(project.id)
state.update(jid: job_id) if job_id
end
end
after_transition any => :finished do |state, _|
if state.jid.present?
Gitlab::SidekiqStatus.unset(state.jid)
state.update_column(:jid, nil)
end
end
# Supress warning:
# both JiraImportState and its :status machine have defined a different default for "status".
# although both have same value but represented in 2 ways: integer(0) and symbol(:initial)
def owner_class_attribute_default
'initial'
end
end
enum status: STATUSES
def in_progress?
scheduled? || started?
end
def refresh_jid_expiration
return unless jid
Gitlab::SidekiqStatus.set(jid, StuckImportJobsWorker::IMPORT_JOBS_EXPIRATION)
end
def self.jid_by(project_id:, status:)
select(:jid).with_status(status).find_by(project_id: project_id)
end
end

View File

@ -314,6 +314,7 @@ class Project < ApplicationRecord
has_one :pages_metadatum, class_name: 'ProjectPagesMetadatum', inverse_of: :project
has_many :import_failures, inverse_of: :project
has_many :jira_imports, -> { order 'jira_imports.created_at' }, class_name: 'JiraImportState', inverse_of: :project
has_many :daily_report_results, class_name: 'Ci::DailyReportResult'
@ -2424,6 +2425,10 @@ class Project < ApplicationRecord
environments.where("name LIKE (#{::Gitlab::SQL::Glob.to_like(quoted_scope)})") # rubocop:disable GitlabSecurity/SqlInjection
end
def latest_jira_import
jira_imports.last
end
private
def find_service(services, name)

View File

@ -30,7 +30,7 @@ You can find these nightly pipelines at `https://gitlab.com/gitlab-org/quality/s
#### Using the `package-and-qa` job
It is possible to run end-to-end tests for a merge request, eventually being run in
a pipeline in the [`gitlab-qa`](https://gitlab.com/gitlab-org/gitlab-qa/) project,
a pipeline in the [`gitlab-qa-mirror`](https://gitlab.com/gitlab-org/gitlab-qa-mirror/) project,
by triggering the `package-and-qa` manual action in the `test` stage (not
available for forks).
@ -49,8 +49,8 @@ pipelines.
```mermaid
graph LR
A1 -.->|1. Triggers an omnibus-gitlab pipeline and wait for it to be done| A2
B2[`Trigger-qa` stage<br>`Trigger:qa-test` job] -.->|2. Triggers a gitlab-qa pipeline and wait for it to be done| A3
A1 -.->|1. Triggers an omnibus-gitlab-mirror pipeline and wait for it to be done| A2
B2[`Trigger-qa` stage<br>`Trigger:qa-test` job] -.->|2. Triggers a gitlab-qa-mirror pipeline and wait for it to be done| A3
subgraph "gitlab-foss/gitlab pipeline"
A1[`test` stage<br>`package-and-qa` job]
@ -60,7 +60,7 @@ subgraph "omnibus-gitlab pipeline"
A2[`Trigger-docker` stage<br>`Trigger:gitlab-docker` job] -->|once done| B2
end
subgraph "gitlab-qa pipeline"
subgraph "gitlab-qa-mirror pipeline"
A3>QA jobs run] -.->|3. Reports back the pipeline result to the `package-and-qa` job<br>and post the result on the original commit tested| A1
end
```
@ -68,7 +68,7 @@ subgraph "gitlab-qa pipeline"
1. Developer triggers a manual action, that can be found in CE / EE merge
requests. This starts a chain of pipelines in multiple projects.
1. The script being executed triggers a pipeline in [Omnibus GitLab][omnibus-gitlab]
1. The script being executed triggers a pipeline in [Omnibus GitLab Mirror][omnibus-gitlab-mirror]
and waits for the resulting status. We call this a _status attribution_.
1. GitLab packages are being built in the [Omnibus GitLab][omnibus-gitlab]
@ -76,7 +76,7 @@ subgraph "gitlab-qa pipeline"
1. When packages are ready, and available in the registry, a final step in the
[Omnibus GitLab][omnibus-gitlab] pipeline, triggers a new
GitLab QA pipeline (those with access can view them at `https://gitlab.com/gitlab-org/gitlab-qa/pipelines`). It also waits for a resulting status.
GitLab QA pipeline (those with access can view them at `https://gitlab.com/gitlab-org/gitlab-qa-mirror/pipelines`). It also waits for a resulting status.
1. GitLab QA pulls images from the registry, spins-up containers and runs tests
against a test environment that has been just orchestrated by the `gitlab-qa`
@ -86,12 +86,12 @@ subgraph "gitlab-qa pipeline"
propagated upstream, through Omnibus, back to the CE / EE merge request.
Please note, we plan to [add more specific information](https://gitlab.com/gitlab-org/quality/team-tasks/issues/156)
about the tests included in each job/scenario that runs in `gitlab-qa`.
about the tests included in each job/scenario that runs in `gitlab-qa-mirror`.
#### With Pipeline for Merged Results
In a Pipeline for Merged Results, the pipeline runs on a new ref that contains the merge result of the source and target branch.
However, this ref is not available to the `gitlab-qa` pipeline.
However, this ref is not available to the `gitlab-qa-mirror` pipeline.
For this reason, the end-to-end tests on a Pipeline for Merged Results would use the head of the merge request source branch.
@ -112,7 +112,7 @@ C --> D["Pipeline for merged results"]
##### Running custom tests
The [existing scenarios](https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/what_tests_can_be_run.md)
that run in the downstream `gitlab-qa` pipeline include many tests, but there are times when you might want to run a
that run in the downstream `gitlab-qa-mirror` pipeline include many tests, but there are times when you might want to run a
test or a group of tests that are different than the groups in any of the existing scenarios.
For example, when we [dequarantine](https://about.gitlab.com/handbook/engineering/quality/guidelines/debugging-qa-test-failures/#dequarantining-tests)
@ -197,6 +197,7 @@ you can find an issue you would like to work on in
[the `gitlab-qa` issue tracker][gitlab-qa-issues].
[omnibus-gitlab]: https://gitlab.com/gitlab-org/omnibus-gitlab
[omnibus-gitlab-mirror]: https://gitlab.com/gitlab-org/omnibus-gitlab-mirror
[gitlab-qa]: https://gitlab.com/gitlab-org/gitlab-qa
[gitlab-qa-readme]: https://gitlab.com/gitlab-org/gitlab-qa/tree/master/README.md
[review-apps]: ../review_apps.md

View File

@ -64,7 +64,7 @@ source projects, GitLab grants access to **Gold** features for all GitLab.com
#### Self-managed
A self-managed subscription uses a hybrid model. You pay for a subscription according to the maximum number of users enabled during the subscription period. For instances that aren't air-gapped or on a closed network, the maximum number of simultaneous users in the self-managed installation is checked each quarter, using [Seat Link](#seat-link).
A self-managed subscription uses a hybrid model. You pay for a subscription according to the maximum number of users enabled during the subscription period. For instances that aren't offline or on a closed network, the maximum number of simultaneous users in the self-managed installation is checked each quarter, using [Seat Link](#seat-link).
Every occupied seat, whether by person, job, or bot is counted in the subscription, with the following exceptions:
@ -255,7 +255,7 @@ Seat Link provides **only** the following information to GitLab:
- Historical maximum user count
- Active users count
For air-gapped or closed network customers, the existing [true-up model](#users-over-license) will be used. Prorated charges are not possible without user count data.
For offline or closed network customers, the existing [true-up model](#users-over-license) will be used. Prorated charges are not possible without user count data.
<details>
<summary>Click here to view example content of a Seat Link POST request.</summary>

View File

@ -1,7 +1,7 @@
# Air-gapped GitLab
# Offline GitLab
Computers in an air-gapped network are isolated from the public internet as a security measure.
This page lists all the information available for running GitLab in an air-gapped environment.
Computers in an offline environment are isolated from the public internet as a security measure. This
page lists all the information available for running GitLab in an offline environment.
## Quick start
@ -14,7 +14,7 @@ Follow these best practices to use GitLab's features in an offline environment:
- [Operating the GitLab Secure scanners in an offline environment](../../user/application_security/offline_deployments/index.md).
## Loading Docker images onto your air-gapped host
## Loading Docker images onto your offline host
To use many GitLab features, including
[security scans](../../user/application_security/index.md#working-in-an-offline-environment)
@ -22,13 +22,13 @@ and [Auto Devops](../autodevops/), the GitLab Runner must be able to fetch the
relevant Docker images.
The process for making these images available without direct access to the public internet
involves downloading the images then packaging and transferring them to the air-gapped host.
Here's an example of such a transfer:
involves downloading the images then packaging and transferring them to the offline host. Here's an
example of such a transfer:
1. Download Docker images from public internet.
1. Package Docker images as tar archives.
1. Transfer images to air-gapped environment.
1. Load transferred images into air-gapped Docker registry.
1. Transfer images to offline environment.
1. Load transferred images into offline Docker registry.
### Example image packager script
@ -51,7 +51,7 @@ done
### Example image loader script
This example loads the images from a bastion host to an air-gapped host. In certain configurations,
This example loads the images from a bastion host to an offline host. In certain configurations,
physical media may be needed for such a transfer:
```sh

View File

@ -1,4 +1,4 @@
# Getting started with an air-gapped GitLab Installation
# Getting started with an offline GitLab Installation
This is a step-by-step guide that helps you install, configure, and use a self-managed GitLab
instance entirely offline.

View File

@ -180,7 +180,7 @@ using environment variables.
| `CLAIR_DB_CONNECTION_STRING` | This variable represents the [connection string](https://www.postgresql.org/docs/9.3/libpq-connect.html#AEN39692) to the [PostgreSQL server hosting the vulnerabilities definitions](https://hub.docker.com/r/arminc/clair-db) database and **shouldn't be changed** unless you're running the image locally as described in the [Running the standalone Container Scanning Tool](#running-the-standalone-container-scanning-tool) section. The host value for the connection string must match the [alias](https://gitlab.com/gitlab-org/gitlab/-/blob/898c5da43504eba87b749625da50098d345b60d6/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml#L23) value of the `Container-Scanning.gitlab-ci.yml` template file, which defaults to `clair-vulnerabilities-db`. | `postgresql://postgres:password@clair-vulnerabilities-db:5432/postgres?sslmode=disable&statement_timeout=60000` |
| `CI_APPLICATION_REPOSITORY` | Docker repository URL for the image to be scanned. | `$CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG` |
| `CI_APPLICATION_TAG` | Docker respository tag for the image to be scanned. | `$CI_COMMIT_SHA` |
| `CLAIR_DB_IMAGE` | The Docker image name and tag for the [PostgreSQL server hosting the vulnerabilities definitions](https://hub.docker.com/r/arminc/clair-db). It can be useful to override this value with a specific version, for example, to provide a consistent set of vulnerabilities for integration testing purposes, or to refer to a locally hosted vulnerabilities database for an on-premise air-gapped installation. | `arminc/clair-db:latest` |
| `CLAIR_DB_IMAGE` | The Docker image name and tag for the [PostgreSQL server hosting the vulnerabilities definitions](https://hub.docker.com/r/arminc/clair-db). It can be useful to override this value with a specific version, for example, to provide a consistent set of vulnerabilities for integration testing purposes, or to refer to a locally hosted vulnerabilities database for an on-premise offline installation. | `arminc/clair-db:latest` |
| `CLAIR_DB_IMAGE_TAG` | (**DEPRECATED - use `CLAIR_DB_IMAGE` instead**) The Docker image tag for the [PostgreSQL server hosting the vulnerabilities definitions](https://hub.docker.com/r/arminc/clair-db). It can be useful to override this value with a specific version, for example, to provide a consistent set of vulnerabilities for integration testing purposes. | `latest` |
| `DOCKERFILE_PATH` | The path to the `Dockerfile` to be used for generating remediations. By default, the scanner will look for a file named `Dockerfile` in the root directory of the project, so this variable should only be configured if your `Dockerfile` is in a non-standard location, such as a subdirectory. See [Solutions for vulnerabilities](#solutions-for-vulnerabilities-auto-remediation) for more details. | `Dockerfile` |
| `ADDITIONAL_CA_CERT_BUNDLE` | Bundle of CA certs that you want to trust. | "" |
@ -210,7 +210,7 @@ If you want to whitelist specific vulnerabilities, you'll need to:
in the [whitelist example file](https://github.com/arminc/clair-scanner/blob/v12/example-whitelist.yaml).
1. Add the `clair-whitelist.yml` file to the Git repository of your project.
### Running Container Scanning in an offline environment deployment
### Running Container Scanning in an offline environment
Container Scanning can be executed on an offline GitLab Ultimate installation by using the following process:

View File

@ -461,7 +461,7 @@ dast:
The DAST job does not require the project's repository to be present when running, so by default
[`GIT_STRATEGY`](../../../ci/yaml/README.md#git-strategy) is set to `none`.
## Running DAST in an offline environment deployment
## Running DAST in an offline environment
DAST can be executed on an offline GitLab Ultimate installation by using the following process:

View File

@ -167,7 +167,7 @@ The following variables are used for configuring specific analyzers (used for a
| `DS_PIP_DEPENDENCY_PATH` | `gemnasium-python` | | Path to load Python pip dependencies from. ([Introduced](https://gitlab.com/gitlab-org/gitlab/issues/12412) in GitLab 12.2) |
| `DS_PYTHON_VERSION` | `retire.js` | | Version of Python. If set to 2, dependencies are installed using Python 2.7 instead of Python 3.6. ([Introduced](https://gitlab.com/gitlab-org/gitlab/issues/12296) in GitLab 12.1)|
| `MAVEN_CLI_OPTS` | `gemnasium-maven` | `"-DskipTests --batch-mode"` | List of command line arguments that will be passed to `maven` by the analyzer. See an example for [using private repos](#using-private-maven-repos). |
| `BUNDLER_AUDIT_UPDATE_DISABLED` | `bundler-audit` | `false` | Disable automatic updates for the `bundler-audit` analyzer. Useful if you're running Dependency Scanning in an offline, air-gapped environment.|
| `BUNDLER_AUDIT_UPDATE_DISABLED` | `bundler-audit` | `false` | Disable automatic updates for the `bundler-audit` analyzer. Useful if you're running Dependency Scanning in an offline environment. |
| `BUNDLER_AUDIT_ADVISORY_DB_URL` | `bundler-audit` | `https://github.com/rubysec/ruby-advisory-db` | URL of the advisory database used by bundler-audit. |
| `BUNDLER_AUDIT_ADVISORY_DB_REF_NAME` | `bundler-audit` | `master` | Git ref for the advisory database specified by `BUNDLER_AUDIT_ADVISORY_DB_URL`. |
| `RETIREJS_JS_ADVISORY_DB` | `retire.js` | `https://raw.githubusercontent.com/RetireJS/retire.js/master/repository/jsrepository.json` | Path or URL to Retire.js js vulnerability data file. |

View File

@ -2,15 +2,15 @@
type: reference, howto
---
# Offline environment deployments
# Offline environments
It is possible to run most of the GitLab security scanners when not
connected to the internet.
It's possible to run most of the GitLab security scanners when not connected to the internet.
This document describes how to operate Secure Categories (that is, scanner types) in an offline environment. These instructions also apply to
self-managed installations that are secured, have security policies (for example, firewall policies), or are otherwise restricted from
accessing the full internet. GitLab refers to these deployments as _offline environment deployments_.
Other common names include:
This document describes how to operate Secure Categories (that is, scanner types) in an offline
environment. These instructions also apply to self-managed installations that are secured, have
security policies (for example, firewall policies), or are otherwise restricted from accessing the
full internet. GitLab refers to these environments as _offline environments_. Other common names
include:
- Air-gapped environments
- Limited connectivity environments
@ -21,13 +21,13 @@ These environments have physical barriers or security policies (for example, fir
or limit internet access. These instructions are designed for physically disconnected networks, but
can also be followed in these other use cases.
## Offline environments
## Defining offline environments
In this situation, the GitLab instance can be one or more servers and services that can communicate
on a local network, but with no or very restricted access to the internet. Assume anything within
the GitLab instance and supporting infrastructure (for example, a private Maven repository) can be
accessed through a local network connection. Assume any files from the internet must come in through
physical media (USB drive, hard drive, writeable DVD, etc.).
In an offline environment, the GitLab instance can be one or more servers and services that can
communicate on a local network, but with no or very restricted access to the internet. Assume
anything within the GitLab instance and supporting infrastructure (for example, a private Maven
repository) can be accessed through a local network connection. Assume any files from the internet
must come in through physical media (USB drive, hard drive, writeable DVD, etc.).
## Overview
@ -43,7 +43,7 @@ an internet-connected GitLab installation, GitLab checks the GitLab.com-hosted
container registry to check that you have the latest versions of these Docker images
and possibly connect to package repositories to install necessary dependencies.
In an air-gapped environment, these checks must be disabled so that GitLab.com is not
In an offline environment, these checks must be disabled so that GitLab.com isn't
queried. Because the GitLab.com registry and repositories are not available,
you must update each of the scanners to either reference a different,
internally-hosted registry or provide access to the individual scanner images.
@ -55,9 +55,11 @@ mirroring the packages inside your own offline network.
### Interacting with the vulnerabilities
Once a vulnerability is found, you can interact with it. Read more on how to [interact with the vulnerabilities](../index.md#interacting-with-the-vulnerabilities).
Once a vulnerability is found, you can interact with it. Read more on how to
[interact with the vulnerabilities](../index.md#interacting-with-the-vulnerabilities).
Please note that in some cases the reported vulnerabilities provide metadata that can contain external links exposed in the UI. These links might not be accessible within an air-gapped (or offline) environment.
Please note that in some cases the reported vulnerabilities provide metadata that can contain
external links exposed in the UI. These links might not be accessible within an offline environment.
### Scanner signature and rule updates
@ -73,6 +75,6 @@ hosted within your network.
Each individual scanner may be slightly different than the steps described
above. You can find more info at each of the pages below:
- [Container scanning offline directions](../container_scanning/index.md#running-container-scanning-in-an-offline-environment-deployment)
- [SAST offline directions](../sast/index.md#gitlab-sast-in-an-offline-environment-deployment)
- [DAST offline directions](../dast/index.md#running-dast-in-an-offline-environment-deployment)
- [Container scanning offline directions](../container_scanning/index.md#running-container-scanning-in-an-offline-environment)
- [SAST offline directions](../sast/index.md#gitlab-sast-in-an-offline-environment)
- [DAST offline directions](../dast/index.md#running-dast-in-an-offline-environment)

View File

@ -491,10 +491,10 @@ Once a vulnerability is found, you can interact with it. Read more on how to
For more information about the vulnerabilities database update, check the
[maintenance table](../index.md#maintenance-and-update-of-the-vulnerabilities-database).
## GitLab SAST in an offline environment deployment
## GitLab SAST in an offline environment
For self-managed GitLab instances in an environment with limited, restricted, or intermittent access
to external resources via the internet, some adjustments are required for the SAST job to
to external resources through the internet, some adjustments are required for the SAST job to
successfully run.
### Requirements for offline SAST

View File

@ -93,7 +93,7 @@ A single `git push` is limited to 5GB. LFS is not affected by this limit.
## IP range
GitLab.com is using the IP range `34.74.90.64/28` for traffic from its Web/API
fleet. You can expect connections from webhooks or repository mirroring to come
fleet. This whole range is solely allocated to GitLab. You can expect connections from webhooks or repository mirroring to come
from those IPs and whitelist them.
GitLab.com is fronted by Cloudflare. For incoming connections to GitLab.com you might need to whitelist CIDR blocks of Cloudflare ([IPv4](https://www.cloudflare.com/ips-v4) and [IPv6](https://www.cloudflare.com/ips-v6))

View File

@ -5,7 +5,6 @@ module API
module Internal
class Pages < Grape::API
before do
not_found! unless Feature.enabled?(:pages_internal_api)
authenticate_gitlab_pages_request!
end

View File

@ -3353,6 +3353,9 @@ msgstr ""
msgid "Cannot create the abuse report. This user has been blocked."
msgstr ""
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
msgid "Cannot make epic confidential if it contains not-confidential issues"
msgstr ""

View File

@ -0,0 +1,28 @@
# frozen_string_literal: true
FactoryBot.define do
factory :jira_import_state do
project
user { project&.creator }
label
jira_project_name { generate(:name) }
jira_project_key { generate(:name) }
jira_project_xid { 1234 }
end
trait :scheduled do
status { :scheduled }
end
trait :started do
status { :started }
end
trait :failed do
status { :failed }
end
trait :finished do
status { :finished }
end
end

View File

@ -476,6 +476,7 @@ project:
- requirements
- export_jobs
- daily_report_results
- jira_imports
award_emoji:
- awardable
- user

View File

@ -0,0 +1,149 @@
# frozen_string_literal: true
require 'spec_helper'
describe JiraImportState do
describe "associations" do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:user) }
it { is_expected.to belong_to(:label) }
end
describe 'modules' do
subject { described_class }
it { is_expected.to include_module(AfterCommitQueue) }
end
describe 'validations' do
it { is_expected.to validate_presence_of(:project) }
it { is_expected.to validate_presence_of(:jira_project_key) }
it { is_expected.to validate_presence_of(:jira_project_name) }
it { is_expected.to validate_presence_of(:jira_project_xid) }
context 'when trying to run multiple imports' do
let(:project) { create(:project) }
context 'when project has an initial jira_import' do
let!(:jira_import) { create(:jira_import_state, project: project)}
it_behaves_like 'multiple running imports not allowed'
end
context 'when project has a scheduled jira_import' do
let!(:jira_import) { create(:jira_import_state, :scheduled, project: project)}
it_behaves_like 'multiple running imports not allowed'
end
context 'when project has a started jira_import' do
let!(:jira_import) { create(:jira_import_state, :started, project: project)}
it_behaves_like 'multiple running imports not allowed'
end
context 'when project has a failed jira_import' do
let!(:jira_import) { create(:jira_import_state, :failed, project: project)}
it 'returns valid' do
new_import = build(:jira_import_state, project: project)
expect(new_import).to be_valid
expect(new_import.errors[:project]).to be_empty
end
end
context 'when project has a finished jira_import' do
let!(:jira_import) { create(:jira_import_state, :finished, project: project)}
it 'returns valid' do
new_import = build(:jira_import_state, project: project)
expect(new_import).to be_valid
expect(new_import.errors[:project]).to be_empty
end
end
end
end
describe '#in_progress?' do
context 'statuses that return in progress' do
it_behaves_like 'in progress', :scheduled
it_behaves_like 'in progress', :started
end
context 'statuses that return not in progress' do
it_behaves_like 'not in progress', :initial
it_behaves_like 'not in progress', :failed
it_behaves_like 'not in progress', :finished
end
end
describe 'states transition flow' do
let(:project) { create(:project) }
context 'when jira import is in initial state' do
let!(:jira_import) { build(:jira_import_state, project: project)}
it_behaves_like 'can transition', [:schedule, :do_fail]
it_behaves_like 'cannot transition', [:start, :finish]
end
context 'when jira import is in scheduled state' do
let!(:jira_import) { build(:jira_import_state, :scheduled, project: project)}
it_behaves_like 'can transition', [:start, :do_fail]
it_behaves_like 'cannot transition', [:finish]
end
context 'when jira import is in started state' do
let!(:jira_import) { build(:jira_import_state, :started, project: project)}
it_behaves_like 'can transition', [:finish, :do_fail]
it_behaves_like 'cannot transition', [:schedule]
end
context 'when jira import is in failed state' do
let!(:jira_import) { build(:jira_import_state, :failed, project: project)}
it_behaves_like 'cannot transition', [:schedule, :finish, :do_fail]
end
context 'when jira import is in finished state' do
let!(:jira_import) { build(:jira_import_state, :finished, project: project)}
it_behaves_like 'cannot transition', [:schedule, :do_fail, :start]
end
context 'after transition to scheduled' do
let!(:jira_import) { build(:jira_import_state, project: project)}
it 'triggers the import job' do
expect(Gitlab::JiraImport::Stage::StartImportWorker).to receive(:perform_async).and_return('some-job-id')
jira_import.schedule
expect(jira_import.jid).to eq('some-job-id')
end
end
context 'after transition to finished' do
let!(:jira_import) { build(:jira_import_state, :started, jid: 'some-other-jid', project: project)}
it 'triggers the import job' do
jira_import.finish
expect(jira_import.jid).to be_nil
end
it 'triggers the import job' do
jira_import.update!(status: :scheduled)
jira_import.finish
expect(jira_import.status).to eq('scheduled')
expect(jira_import.jid).to eq('some-other-jid')
end
end
end
end

View File

@ -110,6 +110,7 @@ describe Project do
it { is_expected.to have_many(:source_pipelines) }
it { is_expected.to have_many(:prometheus_alert_events) }
it { is_expected.to have_many(:self_managed_prometheus_alert_events) }
it { is_expected.to have_many(:jira_imports) }
it_behaves_like 'model with repository' do
let_it_be(:container) { create(:project, :repository, path: 'somewhere') }
@ -5987,6 +5988,34 @@ describe Project do
end
end
describe '#latest_jira_import' do
let_it_be(:project) { create(:project) }
context 'when no jira imports' do
it 'returns nil' do
expect(project.latest_jira_import).to be nil
end
end
context 'when single jira import' do
let!(:jira_import1) { create(:jira_import_state, project: project) }
it 'returns the jira import' do
expect(project.latest_jira_import).to eq(jira_import1)
end
end
context 'when multiple jira imports' do
let!(:jira_import1) { create(:jira_import_state, :finished, created_at: 1.day.ago, project: project) }
let!(:jira_import2) { create(:jira_import_state, :failed, created_at: 2.days.ago, project: project) }
let!(:jira_import3) { create(:jira_import_state, :started, created_at: 3.days.ago, project: project) }
it 'returns latest jira import by created_at' do
expect(project.jira_imports.pluck(:id)).to eq([jira_import3.id, jira_import2.id, jira_import1.id])
expect(project.latest_jira_import).to eq(jira_import1)
end
end
end
def finish_job(export_job)
export_job.start
export_job.finish

View File

@ -14,241 +14,227 @@ describe API::Internal::Pages do
get api("/internal/pages"), headers: headers, params: { host: host }
end
context 'feature flag disabled' do
before do
stub_feature_flags(pages_internal_api: false)
end
it 'responds with 404 Not Found' do
context 'not authenticated' do
it 'responds with 401 Unauthorized' do
query_host('pages.gitlab.io')
expect(response).to have_gitlab_http_status(:not_found)
expect(response).to have_gitlab_http_status(:unauthorized)
end
end
context 'feature flag enabled' do
context 'not authenticated' do
it 'responds with 401 Unauthorized' do
context 'authenticated' do
def query_host(host)
jwt_token = JWT.encode({ 'iss' => 'gitlab-pages' }, Gitlab::Pages.secret, 'HS256')
headers = { Gitlab::Pages::INTERNAL_API_REQUEST_HEADER => jwt_token }
super(host, headers)
end
def deploy_pages(project)
project.mark_pages_as_deployed
end
context 'domain does not exist' do
it 'responds with 204 no content' do
query_host('pages.gitlab.io')
expect(response).to have_gitlab_http_status(:unauthorized)
expect(response).to have_gitlab_http_status(:no_content)
expect(response.body).to be_empty
end
end
context 'authenticated' do
def query_host(host)
jwt_token = JWT.encode({ 'iss' => 'gitlab-pages' }, Gitlab::Pages.secret, 'HS256')
headers = { Gitlab::Pages::INTERNAL_API_REQUEST_HEADER => jwt_token }
context 'serverless domain' do
let(:namespace) { create(:namespace, name: 'gitlab-org') }
let(:project) { create(:project, namespace: namespace, name: 'gitlab-ce') }
let(:environment) { create(:environment, project: project) }
let(:pages_domain) { create(:pages_domain, domain: 'serverless.gitlab.io') }
let(:knative_without_ingress) { create(:clusters_applications_knative) }
let(:knative_with_ingress) { create(:clusters_applications_knative, external_ip: '10.0.0.1') }
super(host, headers)
end
context 'without a knative ingress gateway IP' do
let!(:serverless_domain_cluster) do
create(
:serverless_domain_cluster,
uuid: 'abcdef12345678',
pages_domain: pages_domain,
knative: knative_without_ingress
)
end
def deploy_pages(project)
project.mark_pages_as_deployed
end
let(:serverless_domain) do
create(
:serverless_domain,
serverless_domain_cluster: serverless_domain_cluster,
environment: environment
)
end
context 'domain does not exist' do
it 'responds with 204 no content' do
query_host('pages.gitlab.io')
query_host(serverless_domain.uri.host)
expect(response).to have_gitlab_http_status(:no_content)
expect(response.body).to be_empty
end
end
context 'serverless domain' do
let(:namespace) { create(:namespace, name: 'gitlab-org') }
let(:project) { create(:project, namespace: namespace, name: 'gitlab-ce') }
let(:environment) { create(:environment, project: project) }
let(:pages_domain) { create(:pages_domain, domain: 'serverless.gitlab.io') }
let(:knative_without_ingress) { create(:clusters_applications_knative) }
let(:knative_with_ingress) { create(:clusters_applications_knative, external_ip: '10.0.0.1') }
context 'without a knative ingress gateway IP' do
let!(:serverless_domain_cluster) do
create(
:serverless_domain_cluster,
uuid: 'abcdef12345678',
pages_domain: pages_domain,
knative: knative_without_ingress
)
end
let(:serverless_domain) do
create(
:serverless_domain,
serverless_domain_cluster: serverless_domain_cluster,
environment: environment
)
end
it 'responds with 204 no content' do
query_host(serverless_domain.uri.host)
expect(response).to have_gitlab_http_status(:no_content)
expect(response.body).to be_empty
end
context 'with a knative ingress gateway IP' do
let!(:serverless_domain_cluster) do
create(
:serverless_domain_cluster,
uuid: 'abcdef12345678',
pages_domain: pages_domain,
knative: knative_with_ingress
)
end
context 'with a knative ingress gateway IP' do
let!(:serverless_domain_cluster) do
create(
:serverless_domain_cluster,
uuid: 'abcdef12345678',
pages_domain: pages_domain,
knative: knative_with_ingress
)
end
let(:serverless_domain) do
create(
:serverless_domain,
serverless_domain_cluster: serverless_domain_cluster,
environment: environment
)
end
let(:serverless_domain) do
create(
:serverless_domain,
serverless_domain_cluster: serverless_domain_cluster,
environment: environment
)
end
it 'responds with proxy configuration' do
query_host(serverless_domain.uri.host)
it 'responds with proxy configuration' do
query_host(serverless_domain.uri.host)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('internal/serverless/virtual_domain')
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('internal/serverless/virtual_domain')
expect(json_response['certificate']).to eq(pages_domain.certificate)
expect(json_response['key']).to eq(pages_domain.key)
expect(json_response['certificate']).to eq(pages_domain.certificate)
expect(json_response['key']).to eq(pages_domain.key)
expect(json_response['lookup_paths']).to eq(
[
{
'source' => {
'type' => 'serverless',
'service' => "test-function.#{project.name}-#{project.id}-#{environment.slug}.#{serverless_domain_cluster.knative.hostname}",
'cluster' => {
'hostname' => serverless_domain_cluster.knative.hostname,
'address' => serverless_domain_cluster.knative.external_ip,
'port' => 443,
'cert' => serverless_domain_cluster.certificate,
'key' => serverless_domain_cluster.key
}
expect(json_response['lookup_paths']).to eq(
[
{
'source' => {
'type' => 'serverless',
'service' => "test-function.#{project.name}-#{project.id}-#{environment.slug}.#{serverless_domain_cluster.knative.hostname}",
'cluster' => {
'hostname' => serverless_domain_cluster.knative.hostname,
'address' => serverless_domain_cluster.knative.external_ip,
'port' => 443,
'cert' => serverless_domain_cluster.certificate,
'key' => serverless_domain_cluster.key
}
}
]
)
end
}
]
)
end
end
end
context 'custom domain' do
let(:namespace) { create(:namespace, name: 'gitlab-org') }
let(:project) { create(:project, namespace: namespace, name: 'gitlab-ce') }
let!(:pages_domain) { create(:pages_domain, domain: 'pages.io', project: project) }
context 'when there are no pages deployed for the related project' do
it 'responds with 204 No Content' do
query_host('pages.io')
expect(response).to have_gitlab_http_status(:no_content)
end
end
context 'custom domain' do
let(:namespace) { create(:namespace, name: 'gitlab-org') }
let(:project) { create(:project, namespace: namespace, name: 'gitlab-ce') }
let!(:pages_domain) { create(:pages_domain, domain: 'pages.io', project: project) }
context 'when there are pages deployed for the related project' do
it 'domain lookup is case insensitive' do
deploy_pages(project)
context 'when there are no pages deployed for the related project' do
it 'responds with 204 No Content' do
query_host('pages.io')
query_host('Pages.IO')
expect(response).to have_gitlab_http_status(:no_content)
end
expect(response).to have_gitlab_http_status(:ok)
end
context 'when there are pages deployed for the related project' do
it 'domain lookup is case insensitive' do
deploy_pages(project)
it 'responds with the correct domain configuration' do
deploy_pages(project)
query_host('Pages.IO')
query_host('pages.io')
expect(response).to have_gitlab_http_status(:ok)
end
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('internal/pages/virtual_domain')
it 'responds with the correct domain configuration' do
deploy_pages(project)
expect(json_response['certificate']).to eq(pages_domain.certificate)
expect(json_response['key']).to eq(pages_domain.key)
query_host('pages.io')
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('internal/pages/virtual_domain')
expect(json_response['certificate']).to eq(pages_domain.certificate)
expect(json_response['key']).to eq(pages_domain.key)
expect(json_response['lookup_paths']).to eq(
[
{
'project_id' => project.id,
'access_control' => false,
'https_only' => false,
'prefix' => '/',
'source' => {
'type' => 'file',
'path' => 'gitlab-org/gitlab-ce/public/'
}
expect(json_response['lookup_paths']).to eq(
[
{
'project_id' => project.id,
'access_control' => false,
'https_only' => false,
'prefix' => '/',
'source' => {
'type' => 'file',
'path' => 'gitlab-org/gitlab-ce/public/'
}
]
)
end
}
]
)
end
end
end
context 'namespaced domain' do
let(:group) { create(:group, name: 'mygroup') }
before do
allow(Settings.pages).to receive(:host).and_return('gitlab-pages.io')
allow(Gitlab.config.pages).to receive(:url).and_return("http://gitlab-pages.io")
end
context 'regular project' do
it 'responds with the correct domain configuration' do
project = create(:project, group: group, name: 'myproject')
deploy_pages(project)
query_host('mygroup.gitlab-pages.io')
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('internal/pages/virtual_domain')
expect(json_response['lookup_paths']).to eq(
[
{
'project_id' => project.id,
'access_control' => false,
'https_only' => false,
'prefix' => '/myproject/',
'source' => {
'type' => 'file',
'path' => 'mygroup/myproject/public/'
}
}
]
)
end
end
context 'namespaced domain' do
let(:group) { create(:group, name: 'mygroup') }
context 'group root project' do
it 'responds with the correct domain configuration' do
project = create(:project, group: group, name: 'mygroup.gitlab-pages.io')
deploy_pages(project)
before do
allow(Settings.pages).to receive(:host).and_return('gitlab-pages.io')
allow(Gitlab.config.pages).to receive(:url).and_return("http://gitlab-pages.io")
end
query_host('mygroup.gitlab-pages.io')
context 'regular project' do
it 'responds with the correct domain configuration' do
project = create(:project, group: group, name: 'myproject')
deploy_pages(project)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('internal/pages/virtual_domain')
query_host('mygroup.gitlab-pages.io')
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('internal/pages/virtual_domain')
expect(json_response['lookup_paths']).to eq(
[
{
'project_id' => project.id,
'access_control' => false,
'https_only' => false,
'prefix' => '/myproject/',
'source' => {
'type' => 'file',
'path' => 'mygroup/myproject/public/'
}
expect(json_response['lookup_paths']).to eq(
[
{
'project_id' => project.id,
'access_control' => false,
'https_only' => false,
'prefix' => '/',
'source' => {
'type' => 'file',
'path' => 'mygroup/mygroup.gitlab-pages.io/public/'
}
]
)
end
end
context 'group root project' do
it 'responds with the correct domain configuration' do
project = create(:project, group: group, name: 'mygroup.gitlab-pages.io')
deploy_pages(project)
query_host('mygroup.gitlab-pages.io')
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('internal/pages/virtual_domain')
expect(json_response['lookup_paths']).to eq(
[
{
'project_id' => project.id,
'access_control' => false,
'https_only' => false,
'prefix' => '/',
'source' => {
'type' => 'file',
'path' => 'mygroup/mygroup.gitlab-pages.io/public/'
}
}
]
)
end
}
]
)
end
end
end

View File

@ -0,0 +1,40 @@
# frozen_string_literal: true
shared_examples 'multiple running imports not allowed' do
it 'returns not valid' do
new_import = build(:jira_import_state, project: project)
expect(new_import).not_to be_valid
expect(new_import.errors[:project]).not_to be_nil
end
end
shared_examples 'in progress' do |status|
it 'returns true' do
jira_import_state = build(:jira_import_state, status: status)
expect(jira_import_state).to be_in_progress
end
end
shared_examples 'not in progress' do |status|
it 'returns false' do
jira_import_state = build(:jira_import_state, status: status)
expect(jira_import_state).not_to be_in_progress
end
end
shared_examples 'can transition' do |states|
states.each do |state|
it 'returns true' do
expect(jira_import.send(state)).to be true
end
end
end
shared_examples 'cannot transition' do |states|
states.each do |state|
it 'returns false' do
expect(jira_import.send(state)).to be false
end
end
end