summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/create_cluster/gke_cluster/components/gke_machine_type_dropdown.vue15
-rw-r--r--app/assets/javascripts/create_cluster/gke_cluster/components/gke_submit_button.vue18
-rw-r--r--app/assets/javascripts/create_cluster/gke_cluster/index.js10
-rw-r--r--app/assets/javascripts/create_cluster/gke_cluster/store/getters.js2
-rw-r--r--app/assets/javascripts/mr_notes/init_notes.js14
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/index.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue14
-rw-r--r--app/controllers/projects/merge_requests_controller.rb1
-rw-r--r--app/serializers/merge_request_widget_entity.rb8
-rw-r--r--app/services/groups/import_export/export_service.rb6
-rw-r--r--app/views/clusters/clusters/gcp/_form.html.haml2
-rw-r--r--changelogs/unreleased/198030-fix-wrong-data.yml5
-rw-r--r--changelogs/unreleased/fix_group_export_permission.yml5
-rw-r--r--changelogs/unreleased/ph-42724-removeAsyncMRWidgetFlag.yml5
-rw-r--r--db/post_migrate/20200123155929_remove_invalid_jira_data.rb25
-rw-r--r--db/schema.rb2
-rw-r--r--doc/administration/packages/container_registry.md24
-rw-r--r--doc/development/README.md1
-rw-r--r--doc/development/import_project.md153
-rw-r--r--doc/user/analytics/code_review_analytics.md10
-rw-r--r--doc/user/analytics/index.md3
-rw-r--r--doc/user/application_security/dependency_scanning/index.md3
-rw-r--r--doc/user/application_security/license_compliance/index.md2
-rw-r--r--doc/user/application_security/sast/index.md2
-rw-r--r--doc/user/permissions.md14
-rw-r--r--doc/user/search/index.md2
-rw-r--r--locale/gitlab.pot12
-rw-r--r--spec/features/projects/clusters/gcp_spec.rb14
-rw-r--r--spec/frontend/create_cluster/gke_cluster/components/gke_submit_button_spec.js53
-rw-r--r--spec/javascripts/create_cluster/gke_cluster/stores/getters_spec.js52
-rw-r--r--spec/migrations/20200123155929_remove_invalid_jira_data_spec.rb70
-rw-r--r--spec/services/groups/import_export/export_service_spec.rb16
33 files changed, 471 insertions, 96 deletions
diff --git a/app/assets/javascripts/create_cluster/gke_cluster/components/gke_machine_type_dropdown.vue b/app/assets/javascripts/create_cluster/gke_cluster/components/gke_machine_type_dropdown.vue
index a9d9f0224e3..d6deda25752 100644
--- a/app/assets/javascripts/create_cluster/gke_cluster/components/gke_machine_type_dropdown.vue
+++ b/app/assets/javascripts/create_cluster/gke_cluster/components/gke_machine_type_dropdown.vue
@@ -16,9 +16,6 @@ export default {
]),
...mapState({ items: 'machineTypes' }),
...mapGetters(['hasZone', 'hasMachineType']),
- allDropdownsSelected() {
- return this.projectHasBillingEnabled && this.hasZone && this.hasMachineType;
- },
isDisabled() {
return (
this.isLoading ||
@@ -65,22 +62,10 @@ export default {
.catch(this.fetchFailureHandler);
}
},
- selectedMachineType() {
- this.enableSubmit();
- },
},
methods: {
...mapActions(['fetchMachineTypes']),
...mapActions({ setItem: 'setMachineType' }),
- enableSubmit() {
- if (this.allDropdownsSelected) {
- const submitButtonEl = document.querySelector('.js-gke-cluster-creation-submit');
-
- if (submitButtonEl) {
- submitButtonEl.removeAttribute('disabled');
- }
- }
- },
},
};
</script>
diff --git a/app/assets/javascripts/create_cluster/gke_cluster/components/gke_submit_button.vue b/app/assets/javascripts/create_cluster/gke_cluster/components/gke_submit_button.vue
new file mode 100644
index 00000000000..a7e08a5e97f
--- /dev/null
+++ b/app/assets/javascripts/create_cluster/gke_cluster/components/gke_submit_button.vue
@@ -0,0 +1,18 @@
+<script>
+import { mapGetters } from 'vuex';
+
+export default {
+ computed: {
+ ...mapGetters(['hasValidData']),
+ },
+};
+</script>
+<template>
+ <button
+ type="submit"
+ :disabled="!hasValidData"
+ class="js-gke-cluster-creation-submit btn btn-success"
+ >
+ {{ s__('ClusterIntegration|Create Kubernetes cluster') }}
+ </button>
+</template>
diff --git a/app/assets/javascripts/create_cluster/gke_cluster/index.js b/app/assets/javascripts/create_cluster/gke_cluster/index.js
index 729b9404b64..5a64eb09cad 100644
--- a/app/assets/javascripts/create_cluster/gke_cluster/index.js
+++ b/app/assets/javascripts/create_cluster/gke_cluster/index.js
@@ -4,6 +4,10 @@ import Flash from '~/flash';
import GkeProjectIdDropdown from './components/gke_project_id_dropdown.vue';
import GkeZoneDropdown from './components/gke_zone_dropdown.vue';
import GkeMachineTypeDropdown from './components/gke_machine_type_dropdown.vue';
+import GkeSubmitButton from './components/gke_submit_button.vue';
+
+import store from './store';
+
import * as CONSTANTS from './constants';
const mountComponent = (entryPoint, component, componentName, extraProps = {}) => {
@@ -14,6 +18,7 @@ const mountComponent = (entryPoint, component, componentName, extraProps = {}) =
return new Vue({
el,
+ store,
components: {
[componentName]: component,
},
@@ -50,6 +55,10 @@ const mountGkeMachineTypeDropdown = () => {
);
};
+const mountGkeSubmitButton = () => {
+ mountComponent('.js-gke-cluster-creation-submit-container', GkeSubmitButton, 'gke-submit-button');
+};
+
const gkeDropdownErrorHandler = () => {
Flash(CONSTANTS.GCP_API_ERROR);
};
@@ -72,6 +81,7 @@ const initializeGapiClient = () => {
mountGkeProjectIdDropdown();
mountGkeZoneDropdown();
mountGkeMachineTypeDropdown();
+ mountGkeSubmitButton();
})
.catch(gkeDropdownErrorHandler);
};
diff --git a/app/assets/javascripts/create_cluster/gke_cluster/store/getters.js b/app/assets/javascripts/create_cluster/gke_cluster/store/getters.js
index f9e2e2f74fb..4d4cd223832 100644
--- a/app/assets/javascripts/create_cluster/gke_cluster/store/getters.js
+++ b/app/assets/javascripts/create_cluster/gke_cluster/store/getters.js
@@ -1,3 +1,5 @@
export const hasProject = state => Boolean(state.selectedProject.projectId);
export const hasZone = state => Boolean(state.selectedZone);
export const hasMachineType = state => Boolean(state.selectedMachineType);
+export const hasValidData = (state, getters) =>
+ Boolean(state.projectHasBillingEnabled) && getters.hasZone && getters.hasMachineType;
diff --git a/app/assets/javascripts/mr_notes/init_notes.js b/app/assets/javascripts/mr_notes/init_notes.js
index 622db360d1f..a942bcc13da 100644
--- a/app/assets/javascripts/mr_notes/init_notes.js
+++ b/app/assets/javascripts/mr_notes/init_notes.js
@@ -4,6 +4,7 @@ import { mapActions, mapState, mapGetters } from 'vuex';
import store from 'ee_else_ce/mr_notes/stores';
import notesApp from '../notes/components/notes_app.vue';
import discussionKeyboardNavigator from '../notes/components/discussion_keyboard_navigator.vue';
+import initWidget from '../vue_merge_request_widget';
export default () => {
// eslint-disable-next-line no-new
@@ -32,11 +33,22 @@ export default () => {
...mapState({
activeTab: state => state.page.activeTab,
}),
+ isShowTabActive() {
+ return this.activeTab === 'show';
+ },
},
watch: {
discussionTabCounter() {
this.updateDiscussionTabCounter();
},
+ isShowTabActive: {
+ handler(newVal) {
+ if (newVal) {
+ initWidget();
+ }
+ },
+ immediate: true,
+ },
},
created() {
this.setActiveTab(window.mrTabs.getCurrentAction());
@@ -69,7 +81,7 @@ export default () => {
noteableData: this.noteableData,
notesData: this.notesData,
userData: this.currentUserData,
- shouldShow: this.activeTab === 'show',
+ shouldShow: this.isShowTabActive,
helpPagePath: this.helpPagePath,
},
}),
diff --git a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js
index 1f8befc07c8..c4cc667710a 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js
@@ -7,7 +7,6 @@ import initPipelines from '~/commit/pipelines/pipelines_bundle';
import initVueIssuableSidebarApp from '~/issuable_sidebar/sidebar_bundle';
import initSourcegraph from '~/sourcegraph';
import initPopover from '~/mr_tabs_popover';
-import initWidget from '../../../vue_merge_request_widget';
export default function() {
new ZenMode(); // eslint-disable-line no-new
@@ -20,7 +19,6 @@ export default function() {
new ShortcutsIssuable(true); // eslint-disable-line no-new
handleLocationHash();
howToMerge();
- initWidget();
initSourcegraph();
const tabHighlightEl = document.querySelector('.js-tabs-feature-highlight');
diff --git a/app/assets/javascripts/vue_merge_request_widget/index.js b/app/assets/javascripts/vue_merge_request_widget/index.js
index 0cedbdbdfef..7a9ef7e496e 100644
--- a/app/assets/javascripts/vue_merge_request_widget/index.js
+++ b/app/assets/javascripts/vue_merge_request_widget/index.js
@@ -5,6 +5,8 @@ import Translate from '../vue_shared/translate';
Vue.use(Translate);
export default () => {
+ if (gl.mrWidget) return;
+
gl.mrWidgetData.gitlabLogo = gon.gitlab_logo;
const vm = new Vue(MrWidgetOptions);
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
index 38a7c262b3e..a18da7386f6 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
@@ -135,15 +135,11 @@ export default {
},
},
mounted() {
- if (gon && gon.features && gon.features.asyncMrWidget) {
- MRWidgetService.fetchInitialData()
- .then(({ data }) => this.initWidget(data))
- .catch(() =>
- createFlash(__('Unable to load the merge request widget. Try reloading the page.')),
- );
- } else {
- this.initWidget();
- }
+ MRWidgetService.fetchInitialData()
+ .then(({ data }) => this.initWidget(data))
+ .catch(() =>
+ createFlash(__('Unable to load the merge request widget. Try reloading the page.')),
+ );
},
beforeDestroy() {
eventHub.$off('mr.discussion.updated', this.checkStatus);
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 22de8dd4109..7cdda071813 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -25,7 +25,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
before_action do
push_frontend_feature_flag(:vue_issuable_sidebar, @project.group)
- push_frontend_feature_flag(:async_mr_widget, @project)
end
around_action :allow_gitaly_ref_name_caching, only: [:index, :show, :discussions]
diff --git a/app/serializers/merge_request_widget_entity.rb b/app/serializers/merge_request_widget_entity.rb
index 2a81931c49f..adc4d08a6f6 100644
--- a/app/serializers/merge_request_widget_entity.rb
+++ b/app/serializers/merge_request_widget_entity.rb
@@ -67,14 +67,6 @@ class MergeRequestWidgetEntity < Grape::Entity
end
end
- def as_json(options = {})
- return super(options) if Feature.enabled?(:async_mr_widget)
-
- super(options)
- .merge(MergeRequestPollCachedWidgetEntity.new(object, **@options.opts_hash).as_json(options))
- .merge(MergeRequestPollWidgetEntity.new(object, **@options.opts_hash).as_json(options))
- end
-
private
delegate :current_user, to: :request
diff --git a/app/services/groups/import_export/export_service.rb b/app/services/groups/import_export/export_service.rb
index 26886fc67dc..2c3975961a8 100644
--- a/app/services/groups/import_export/export_service.rb
+++ b/app/services/groups/import_export/export_service.rb
@@ -11,6 +11,12 @@ module Groups
end
def execute
+ unless @current_user.can?(:admin_group, @group)
+ raise ::Gitlab::ImportExport::Error.new(
+ "User with ID: %s does not have permission to Group %s with ID: %s." %
+ [@current_user.id, @group.name, @group.id])
+ end
+
save!
end
diff --git a/app/views/clusters/clusters/gcp/_form.html.haml b/app/views/clusters/clusters/gcp/_form.html.haml
index ab01569b8fd..17f6a064c0e 100644
--- a/app/views/clusters/clusters/gcp/_form.html.haml
+++ b/app/views/clusters/clusters/gcp/_form.html.haml
@@ -79,6 +79,6 @@
= s_('ClusterIntegration|Allow GitLab to manage namespace and service accounts for this cluster.')
= link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'gitlab-managed-clusters'), target: '_blank'
- .form-group
+ .form-group.js-gke-cluster-creation-submit-container
= field.submit s_('ClusterIntegration|Create Kubernetes cluster'),
class: 'js-gke-cluster-creation-submit btn btn-success', disabled: true
diff --git a/changelogs/unreleased/198030-fix-wrong-data.yml b/changelogs/unreleased/198030-fix-wrong-data.yml
new file mode 100644
index 00000000000..d18203cdfef
--- /dev/null
+++ b/changelogs/unreleased/198030-fix-wrong-data.yml
@@ -0,0 +1,5 @@
+---
+title: Remove invalid data from jira_tracker_data table
+merge_request: 23621
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix_group_export_permission.yml b/changelogs/unreleased/fix_group_export_permission.yml
new file mode 100644
index 00000000000..e35b85ae2b0
--- /dev/null
+++ b/changelogs/unreleased/fix_group_export_permission.yml
@@ -0,0 +1,5 @@
+---
+title: Groups::ImportExport::ExportService to require admin_group permission
+merge_request: 23434
+author:
+type: changed
diff --git a/changelogs/unreleased/ph-42724-removeAsyncMRWidgetFlag.yml b/changelogs/unreleased/ph-42724-removeAsyncMRWidgetFlag.yml
new file mode 100644
index 00000000000..3debeb31405
--- /dev/null
+++ b/changelogs/unreleased/ph-42724-removeAsyncMRWidgetFlag.yml
@@ -0,0 +1,5 @@
+---
+title: Fetch merge request widget data asynchronous
+merge_request: 23594
+author:
+type: changed
diff --git a/db/post_migrate/20200123155929_remove_invalid_jira_data.rb b/db/post_migrate/20200123155929_remove_invalid_jira_data.rb
new file mode 100644
index 00000000000..7ddac15c3fb
--- /dev/null
+++ b/db/post_migrate/20200123155929_remove_invalid_jira_data.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class RemoveInvalidJiraData < ActiveRecord::Migration[5.2]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ sql = "DELETE FROM jira_tracker_data WHERE \
+ (length(encrypted_api_url) > 0 AND encrypted_api_url_iv IS NULL) \
+ OR (length(encrypted_url) > 0 AND encrypted_url_iv IS NULL) \
+ OR (length(encrypted_username) > 0 AND encrypted_username_iv IS NULL) \
+ OR (length(encrypted_password) > 0 AND encrypted_password_iv IS NULL)"
+
+ execute(sql)
+ end
+
+ def down
+ # We need to figure out why migrating data to jira_tracker_data table
+ # failed and then can recreate the data
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index f6b44f9de00..8ad4e080fe7 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2020_01_21_132641) do
+ActiveRecord::Schema.define(version: 2020_01_23_155929) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm"
diff --git a/doc/administration/packages/container_registry.md b/doc/administration/packages/container_registry.md
index 6ef1a3ec607..d3415913bab 100644
--- a/doc/administration/packages/container_registry.md
+++ b/doc/administration/packages/container_registry.md
@@ -144,7 +144,7 @@ otherwise you will run into conflicts.
1. Validate using:
- ```sh
+ ```shell
openssl s_client -showcerts -servername gitlab.example.com -connect gitlab.example.com:443 > cacert.pem
```
@@ -156,7 +156,7 @@ If your certificate provider provides the CA Bundle certificates, append them to
1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and
configure it with the following settings:
- ```
+ ```yaml
registry:
enabled: true
host: gitlab.example.com
@@ -408,7 +408,7 @@ when you [deployed your docker registry](https://docs.docker.com/registry/deploy
`s3` storage driver example:
-```yml
+```yaml
storage:
s3:
accesskey: 's3-access-key'
@@ -616,7 +616,7 @@ Before diving in to the following sections, here's some basic troubleshooting:
If you're using a self-signed certificate with your Container Registry, you
might encounter issues during the CI jobs like the following:
-```
+```plaintext
Error response from daemon: Get registry.example.com/v1/users/: x509: certificate signed by unknown authority
```
@@ -666,7 +666,7 @@ Container Registry > Authorization token duration (minutes)**.
When using AWS S3 with the GitLab registry, an error may occur when pushing
large images. Look in the Registry log for the following error:
-```
+```plaintext
level=error msg="response completed with error" err.code=unknown err.detail="unexpected EOF" err.message="unknown error"
```
@@ -813,7 +813,7 @@ diagnose a problem with the S3 setup.
A user attempted to enable an S3-backed Registry. The `docker login` step went
fine. However, when pushing an image, the output showed:
-```text
+```plaintext
The push refers to a repository [s3-testing.myregistry.com:4567/root/docker-test/docker-image]
dc5e59c14160: Pushing [==================================================>] 14.85 kB
03c20c1a019a: Pushing [==================================================>] 2.048 kB
@@ -859,27 +859,27 @@ The following installation instructions assume you are running Ubuntu:
Enter <kbd>CTRL</kbd>-<kbd>C</kbd> to quit.
1. Install the certificate from `~/.mitmproxy` to your system:
- ```sh
+ ```shell
sudo cp ~/.mitmproxy/mitmproxy-ca-cert.pem /usr/local/share/ca-certificates/mitmproxy-ca-cert.crt
sudo update-ca-certificates
```
If successful, the output should indicate that a certificate was added:
-```sh
+```shell
Updating certificates in /etc/ssl/certs... 1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d....done.
```
To verify that the certificates are properly installed, run:
-```sh
+```shell
mitmproxy --port 9000
```
This will run mitmproxy on port `9000`. In another window, run:
-```sh
+```shell
curl --proxy http://localhost:9000 https://httpbin.org/status/200
```
@@ -892,7 +892,7 @@ For Docker to connect through a proxy, you must start the Docker daemon with the
proper environment variables. The easiest way is to shutdown Docker (e.g. `sudo initctl stop docker`)
and then run Docker by hand. As root, run:
-```sh
+```shell
export HTTP_PROXY="http://localhost:9000"
export HTTPS_PROXY="https://localhost:9000"
docker daemon --debug
@@ -905,7 +905,7 @@ This will launch the Docker daemon and proxy all connections through mitmproxy.
Now that we have mitmproxy and Docker running, we can attempt to login and push
a container image. You may need to run as root to do this. For example:
-```sh
+```shell
docker login s3-testing.myregistry.com:4567
docker push s3-testing.myregistry.com:4567/root/docker-test/docker-image
```
diff --git a/doc/development/README.md b/doc/development/README.md
index d551e6f471e..c30b7cbc558 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -54,6 +54,7 @@ description: 'Learn how to contribute to GitLab.'
- [How to dump production data to staging](db_dump.md)
- [Working with the GitHub importer](github_importer.md)
- [Import/Export development documentation](import_export.md)
+- [Test Import Project](import_project.md)
- [Elasticsearch integration docs](elasticsearch.md)
- [Working with Merge Request diffs](diffs.md)
- [Kubernetes integration guidelines](kubernetes.md)
diff --git a/doc/development/import_project.md b/doc/development/import_project.md
new file mode 100644
index 00000000000..06c0bd02262
--- /dev/null
+++ b/doc/development/import_project.md
@@ -0,0 +1,153 @@
+# Test Import Project
+
+For testing, we can import our own [GitLab CE](https://gitlab.com/gitlab-org/gitlab-foss/) project (named `gitlabhq` in this case) under a group named `qa-perf-testing`. Project tarballs that can be used for testing can be found over on the [performance-data](https://gitlab.com/gitlab-org/quality/performance-data) project. A different project could be used if required.
+
+There are several options for importing the project into your GitLab environment. They are detailed as follows with the assumption that the recommended group `qa-perf-testing` and project `gitlabhq` are being set up.
+
+## Importing the project
+
+There are several ways to import a project.
+
+### Importing via UI
+
+The first option is to simply [import the Project tarball file via the GitLab UI](../user/project/settings/import_export.md#importing-the-project):
+
+1. Create the group `qa-perf-testing`
+1. Import the [GitLab FOSS project tarball](https://gitlab.com/gitlab-org/quality/performance-data/raw/master/gitlabhq_export.tar.gz) into the Group.
+
+It should take up to 15 minutes for the project to fully import. You can head to the project's main page for the current status.
+
+NOTE: **Note:** This method ignores all the errors silently (including the ones related to `GITALY_DISABLE_REQUEST_LIMITS`) and is used by GitLab's users. For development and testing, check the other methods below.
+
+### Importing via the `import-project` script
+
+A convenient script, [`bin/import-project`](https://gitlab.com/gitlab-org/quality/performance/blob/master/bin/import-project), is provided with [performance](https://gitlab.com/gitlab-org/quality/performance) project to import the Project tarball into a GitLab environment via API from the terminal.
+
+Note that to use the script, it will require some preparation if you haven't done so already:
+
+1. First, set up [`Ruby`](https://www.ruby-lang.org/en/documentation/installation/) and [`Ruby Bundler`](https://bundler.io) if they aren't already available on the machine.
+1. Next, install the required Ruby Gems via Bundler with `bundle install`.
+
+For details how to use `bin/import-project`, run:
+
+```sh
+bin/import-project --help
+```
+
+The process should take up to 15 minutes for the project to import fully. The script will keep checking periodically for the status and exit once import has completed.
+
+### Importing via GitHub
+
+There is also an option to [import the project via GitHub](../user/project/import/github.md):
+
+1. Create the group `qa-perf-testing`
+1. Import the GitLab FOSS repository that's [mirrored on GitHub](https://github.com/gitlabhq/gitlabhq) into the group via the UI.
+
+This method will take longer to import than the other methods and will depend on several factors. It's recommended to use the other methods.
+
+### Importing via a rake task
+
+[`import.rake`](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/tasks/gitlab/import_export/import.rake) was introduced for importing large GitLab project exports.
+
+As part of this script we also disable direct and background upload to avoid situations where a huge archive is being uploaded to GCS (while being inside a transaction, which can cause idle transaction timeouts).
+
+We can simply run this script from the terminal:
+
+```sh
+bundle exec rake "gitlab:import_export:import[root, root, testingprojectimport, /path/to/file.tar.gz]"
+```
+
+### Importing via the Rails console
+
+The last option is to import a project using a Rails console:
+
+1. Start a Ruby on Rails console:
+
+ ```sh
+ # Omnibus GitLab
+ gitlab-rails console
+
+ # For installations from source
+ sudo -u git -H bundle exec rails console RAILS_ENV=production
+ ```
+
+1. Create a project and run `ProjectTreeRestorer`:
+
+ ```ruby
+ shared_class = Struct.new(:export_path) do
+ def error(message)
+ raise message
+ end
+ end
+
+ user = User.first
+
+ shared = shared_class.new(path)
+
+ project = Projects::CreateService.new(user, { name: name, namespace: user.namespace }).execute
+ begin
+ #Enable Request store
+ RequestStore.begin!
+ Gitlab::ImportExport::ProjectTreeRestorer.new(user: user, shared: shared, project: project).restore
+ ensure
+ RequestStore.end!
+ RequestStore.clear!
+ end
+ ```
+
+1. In case you need the repository as well, you can restore it using:
+
+ ```ruby
+ repo_path = File.join(shared.export_path, Gitlab::ImportExport.project_bundle_filename)
+
+ Gitlab::ImportExport::RepoRestorer.new(path_to_bundle: repo_path,
+ shared: shared,
+ project: project).restore
+ ```
+
+ We are storing all import failures in the `import_failures` data table.
+
+ To make sure that the project import finished without any issues, check:
+
+ ```ruby
+ project.import_failures.all
+ ```
+
+## Performance testing
+
+For Performance testing, we should:
+
+- Import a quite large project, [`gitlabhq`](https://gitlab.com/gitlab-org/quality/performance-data#gitlab-performance-test-framework-data) should be a good example.
+- Measure the execution time of `ProjectTreeRestorer`.
+- Count the number of executed SQL queries during the restore.
+- Observe the number of GC cycles happening.
+
+You can use this [snippet](https://gitlab.com/gitlab-org/gitlab/snippets/1924954), which will restore the project, and measure the execution time of `ProjectTreeRestorer`, number of SQL queries and number of GC cycles happening.
+
+You can execute the script from the `gdk/gitlab` directory like this:
+
+```sh
+bundle exec rails r /path_to_sript/script.rb project_name /path_to_extracted_project request_store_enabled
+```
+
+## Troubleshooting
+
+In this section we'll detail any known issues we've seen when trying to import a project and how to manage them.
+
+### Gitaly calls error when importing
+
+If you're attempting to import a large project into a development environment, you may see Gitaly throw an error about too many calls or invocations, for example:
+
+```
+Error importing repository into qa-perf-testing/gitlabhq - GitalyClient#call called 31 times from single request. Potential n+1?
+```
+
+This is due to a [n+1 calls limit being set for development setups](gitaly.md#toomanyinvocationserror-errors). You can work around this by setting `GITALY_DISABLE_REQUEST_LIMITS=1` as an environment variable, restarting your development environment and importing again.
+
+## Access token setup
+
+Many of the tests also require a GitLab Personal Access Token. This is due to numerous endpoints themselves requiring authentication.
+
+[The official GitLab docs detail how to create this token](../user/profile/personal_access_tokens.md#creating-a-personal-access-token). The tests require that the token is generated by an admin user and that it has the `API` and `read_repository` permissions.
+
+Details on how to use the Access Token with each type of test are found in their respective documentation.
diff --git a/doc/user/analytics/code_review_analytics.md b/doc/user/analytics/code_review_analytics.md
index cd2b0dd2bf7..0b501b1a56d 100644
--- a/doc/user/analytics/code_review_analytics.md
+++ b/doc/user/analytics/code_review_analytics.md
@@ -47,13 +47,3 @@ and trends between them. For example:
- On [Starter or Bronze tier](https://about.gitlab.com/pricing/) and above.
- By users with Reporter access and above.
-
-## Disable with feature flag
-
-Code Review Analytics is [currently enabled by a feature flag](https://gitlab.com/gitlab-org/gitlab/issues/194165)
-that defaults to ON, meaning the feature is available. If you experience performance problems or
-otherwise wish to disable the feature, a GitLab administrator can execute a command in a Rails console:
-
-```ruby
-Feature.disable(:code_review_analytics)
-```
diff --git a/doc/user/analytics/index.md b/doc/user/analytics/index.md
index 07d756af582..3117a5dfbca 100644
--- a/doc/user/analytics/index.md
+++ b/doc/user/analytics/index.md
@@ -15,8 +15,7 @@ Once enabled, click on **Analytics** from the top navigation bar.
From the centralized analytics workspace, the following analytics are available:
-- [Code Review Analytics](code_review_analytics.md), enabled with the `code_review_analytics`
- [feature flag](../../development/feature_flags/development.md#enabling-a-feature-flag-in-development). **(STARTER)**
+- [Code Review Analytics](code_review_analytics.md). **(STARTER)**
- [Cycle Analytics](cycle_analytics.md), enabled with the `cycle_analytics`
[feature flag](../../development/feature_flags/development.md#enabling-a-feature-flag-in-development). **(PREMIUM)**
- [Productivity Analytics](productivity_analytics.md), enabled with the `productivity_analytics`
diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md
index 0c1896e210d..3f725089932 100644
--- a/doc/user/application_security/dependency_scanning/index.md
+++ b/doc/user/application_security/dependency_scanning/index.md
@@ -58,14 +58,13 @@ The following languages and dependency managers are supported.
| Java ([Gradle](https://gradle.org/)) | yes | [gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) |
| Java ([Maven](https://maven.apache.org/)) | yes | [gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) |
| JavaScript ([npm](https://www.npmjs.com/), [yarn](https://yarnpkg.com/en/)) | yes | [gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium), [Retire.js](https://retirejs.github.io/retire.js/) |
-| Go ([Golang](https://golang.org/)) | not currently ([issue](https://gitlab.com/gitlab-org/gitlab/issues/7132 "Dependency Scanning for Go")) | not available |
| PHP ([Composer](https://getcomposer.org/)) | yes | [gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) |
| Python ([pip](https://pip.pypa.io/en/stable/)) | yes | [gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) |
| Python ([Pipfile](https://pipenv.kennethreitz.org/en/latest/basics/)) | not currently ([issue](https://gitlab.com/gitlab-org/gitlab/issues/11756 "Pipfile.lock support for Dependency Scanning"))| not available |
| Python ([poetry](https://poetry.eustace.io/)) | not currently ([issue](https://gitlab.com/gitlab-org/gitlab/issues/7006 "Support Poetry in Dependency Scanning")) | not available |
| Ruby ([gem](https://rubygems.org/)) | yes | [gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium), [bundler-audit](https://github.com/rubysec/bundler-audit) |
| Scala ([sbt](https://www.scala-sbt.org/)) | yes | [gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) |
-| Go ([go](https://golang.org/)) | yes (alpha) | [gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) |
+| Go ([Golang](https://golang.org/)) | yes ([alpha](https://gitlab.com/gitlab-org/gitlab/issues/7132)) | [gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) |
## Configuration
diff --git a/doc/user/application_security/license_compliance/index.md b/doc/user/application_security/license_compliance/index.md
index 97804a451b9..fb6fa0632f6 100644
--- a/doc/user/application_security/license_compliance/index.md
+++ b/doc/user/application_security/license_compliance/index.md
@@ -52,7 +52,7 @@ The following languages and package managers are supported.
| JavaScript | [Bower](https://bower.io/), [npm](https://www.npmjs.com/), [yarn](https://yarnpkg.com/) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)) |[License Finder](https://github.com/pivotal/LicenseFinder)|
| Go | [Godep](https://github.com/tools/godep), go get ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), gvt ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), glide ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), dep ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), trash ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)) and govendor ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), [go mod](https://github.com/golang/go/wiki/Modules) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)) |[License Finder](https://github.com/pivotal/LicenseFinder)|
| Java | [Gradle](https://gradle.org/), [Maven](https://maven.apache.org/) |[License Finder](https://github.com/pivotal/LicenseFinder)|
-| .NET | [Nuget](https://www.nuget.org/) |[License Finder](https://github.com/pivotal/LicenseFinder)|
+| .NET | [Nuget](https://www.nuget.org/) (.NET Framework is supported via the [mono project](https://www.mono-project.com/). Windows specific dependencies are not supported at this time.) |[License Finder](https://github.com/pivotal/LicenseFinder)|
| Python | [pip](https://pip.pypa.io/en/stable/) |[License Finder](https://github.com/pivotal/LicenseFinder)|
| Ruby | [gem](https://rubygems.org/) |[License Finder](https://github.com/pivotal/LicenseFinder)|
| Erlang | [rebar](https://www.rebar3.org/) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types))|[License Finder](https://github.com/pivotal/LicenseFinder)|
diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md
index cd1dabb6ef4..9a6f61c9c8c 100644
--- a/doc/user/application_security/sast/index.md
+++ b/doc/user/application_security/sast/index.md
@@ -54,6 +54,8 @@ this is enabled by default.
Privileged mode is not necessary if you've [disabled Docker in Docker
for SAST](#disabling-docker-in-docker-for-sast)
+CAUTION: **Caution:** Our SAST jobs currently expect a Linux container type. Windows containers are not yet supported.
+
CAUTION: **Caution:**
If you use your own Runners, make sure that the Docker version you have installed
is **not** `19.03.00`. See [troubleshooting information](#error-response-from-daemon-error-processing-tar-file-docker-tar-relocation-error) for details.
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index 985c1babdb5..57873494a48 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -37,11 +37,12 @@ usernames. A GitLab administrator can configure the GitLab instance to
NOTE: **Note:**
In GitLab 11.0, the Master role was renamed to Maintainer.
-While Maintainer is the highest project-level role, some actions can only be performed by a personal namespace or group owner.
+While Maintainer is the highest project-level role, some actions can only be performed by a personal namespace or group owner,
+or an instance admin, who receives all permissions.
The following table depicts the various user permission levels in a project.
-| Action | Guest | Reporter | Developer |Maintainer| Owner |
+| Action | Guest | Reporter | Developer |Maintainer| Owner* |
|---------------------------------------------------|---------|------------|-------------|----------|--------|
| Download project | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| Leave comments | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
@@ -138,10 +139,11 @@ The following table depicts the various user permission levels in a project.
| Force push to protected branches (*4*) | | | | | |
| Remove protected branches (*4*) | | | | | |
-- (*1*): Guest users are able to perform this action on public and internal projects, but not private projects.
-- (*2*): Guest users can only view the confidential issues they created themselves
-- (*3*): If **Public pipelines** is enabled in **Project Settings > CI/CD**
-- (*4*): Not allowed for Guest, Reporter, Developer, Maintainer, or Owner. See [Protected Branches](./project/protected_branches.md).
+\* Owner permission is only available at the group or personal namespace level (and for instance admins) and is inherited by its projects.
+(*1*): Guest users are able to perform this action on public and internal projects, but not private projects.
+(*2*): Guest users can only view the confidential issues they created themselves.
+(*3*): If **Public pipelines** is enabled in **Project Settings > CI/CD**.
+(*4*): Not allowed for Guest, Reporter, Developer, Maintainer, or Owner. See [Protected Branches](./project/protected_branches.md).
## Project features permissions
diff --git a/doc/user/search/index.md b/doc/user/search/index.md
index d7ca43b1164..580406c5fe2 100644
--- a/doc/user/search/index.md
+++ b/doc/user/search/index.md
@@ -89,7 +89,7 @@ Some filters can be added multiple times. These include but are not limited to a
![multiple assignees filtering](img/multiple_assignees.png)
-### Shortcut
+## Shortcut
You'll also find a shortcut on the search field on the top-right of the project's dashboard to
quickly access issues and merge requests created or assigned to you within that project:
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 2755a061498..0562b7560bd 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -6399,15 +6399,9 @@ msgstr ""
msgid "Deselect all"
msgstr ""
-msgid "Design Management"
-msgstr ""
-
msgid "Design Management files and data"
msgstr ""
-msgid "Design Sync Not Enabled"
-msgstr ""
-
msgid "DesignManagement|%{current_design} of %{designs_count}"
msgstr ""
@@ -8528,9 +8522,6 @@ msgstr ""
msgid "Geo Settings"
msgstr ""
-msgid "Geo Troubleshooting"
-msgstr ""
-
msgid "Geo allows you to replicate your GitLab instance to other geographical locations."
msgstr ""
@@ -10012,9 +10003,6 @@ msgstr ""
msgid "If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
msgstr ""
-msgid "If you believe this page to be an error, check out the links below for more information."
-msgstr ""
-
msgid "If you lose your recovery codes you can generate new ones, invalidating all previous codes."
msgstr ""
diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb
index 4bc0aef0cd4..09cd1c6a765 100644
--- a/spec/features/projects/clusters/gcp_spec.rb
+++ b/spec/features/projects/clusters/gcp_spec.rb
@@ -14,6 +14,11 @@ describe 'Gcp Cluster', :js, :do_not_mock_admin_mode do
allow(Projects::ClustersController).to receive(:STATUS_POLLING_INTERVAL) { 100 }
end
+ def submit_form
+ execute_script('document.querySelector(".js-gke-cluster-creation-submit").removeAttribute("disabled")')
+ execute_script('document.querySelector(".js-gke-cluster-creation-submit").click()')
+ end
+
context 'when user has signed with Google' do
let(:project_id) { 'test-project-1234' }
@@ -34,7 +39,7 @@ describe 'Gcp Cluster', :js, :do_not_mock_admin_mode do
end
context 'when user filled form with valid parameters' do
- subject { click_button 'Create Kubernetes cluster' }
+ subject { submit_form }
before do
allow_any_instance_of(GoogleApi::CloudPlatform::Client)
@@ -47,8 +52,8 @@ describe 'Gcp Cluster', :js, :do_not_mock_admin_mode do
allow(WaitForClusterCreationWorker).to receive(:perform_in).and_return(nil)
- execute_script('document.querySelector(".js-gke-cluster-creation-submit").removeAttribute("disabled")')
- sleep 2 # wait for ajax
+ expect(page).to have_css('.js-gcp-project-id-dropdown')
+
execute_script('document.querySelector(".js-gcp-project-id-dropdown input").setAttribute("type", "text")')
execute_script('document.querySelector(".js-gcp-zone-dropdown input").setAttribute("type", "text")')
execute_script('document.querySelector(".js-gcp-machine-type-dropdown input").setAttribute("type", "text")')
@@ -86,8 +91,7 @@ describe 'Gcp Cluster', :js, :do_not_mock_admin_mode do
context 'when user filled form with invalid parameters' do
before do
- execute_script('document.querySelector(".js-gke-cluster-creation-submit").removeAttribute("disabled")')
- click_button 'Create Kubernetes cluster'
+ submit_form
end
it 'user sees a validation error' do
diff --git a/spec/frontend/create_cluster/gke_cluster/components/gke_submit_button_spec.js b/spec/frontend/create_cluster/gke_cluster/components/gke_submit_button_spec.js
new file mode 100644
index 00000000000..9401ba83ef4
--- /dev/null
+++ b/spec/frontend/create_cluster/gke_cluster/components/gke_submit_button_spec.js
@@ -0,0 +1,53 @@
+import Vuex from 'vuex';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import GkeSubmitButton from '~/create_cluster/gke_cluster/components/gke_submit_button.vue';
+
+const localVue = createLocalVue();
+
+localVue.use(Vuex);
+
+describe('GkeSubmitButton', () => {
+ let wrapper;
+ let store;
+ let hasValidData;
+
+ const buildStore = () =>
+ new Vuex.Store({
+ getters: {
+ hasValidData,
+ },
+ });
+
+ const buildWrapper = () =>
+ shallowMount(GkeSubmitButton, {
+ store,
+ localVue,
+ });
+
+ const bootstrap = () => {
+ store = buildStore();
+ wrapper = buildWrapper();
+ };
+
+ beforeEach(() => {
+ hasValidData = jest.fn();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('is disabled when hasValidData is false', () => {
+ hasValidData.mockReturnValueOnce(false);
+ bootstrap();
+
+ expect(wrapper.attributes('disabled')).toBe('disabled');
+ });
+
+ it('is not disabled when hasValidData is true', () => {
+ hasValidData.mockReturnValueOnce(true);
+ bootstrap();
+
+ expect(wrapper.attributes('disabled')).toBeFalsy();
+ });
+});
diff --git a/spec/javascripts/create_cluster/gke_cluster/stores/getters_spec.js b/spec/javascripts/create_cluster/gke_cluster/stores/getters_spec.js
index ac92716b0ab..39106c3f6ca 100644
--- a/spec/javascripts/create_cluster/gke_cluster/stores/getters_spec.js
+++ b/spec/javascripts/create_cluster/gke_cluster/stores/getters_spec.js
@@ -1,4 +1,9 @@
-import * as getters from '~/create_cluster/gke_cluster/store/getters';
+import {
+ hasProject,
+ hasZone,
+ hasMachineType,
+ hasValidData,
+} from '~/create_cluster/gke_cluster/store/getters';
import { selectedProjectMock, selectedZoneMock, selectedMachineTypeMock } from '../mock_data';
describe('GCP Cluster Dropdown Store Getters', () => {
@@ -7,6 +12,7 @@ describe('GCP Cluster Dropdown Store Getters', () => {
describe('valid states', () => {
beforeEach(() => {
state = {
+ projectHasBillingEnabled: true,
selectedProject: selectedProjectMock,
selectedZone: selectedZoneMock,
selectedMachineType: selectedMachineTypeMock,
@@ -15,19 +21,25 @@ describe('GCP Cluster Dropdown Store Getters', () => {
describe('hasProject', () => {
it('should return true when project is selected', () => {
- expect(getters.hasProject(state)).toEqual(true);
+ expect(hasProject(state)).toEqual(true);
});
});
describe('hasZone', () => {
it('should return true when zone is selected', () => {
- expect(getters.hasZone(state)).toEqual(true);
+ expect(hasZone(state)).toEqual(true);
});
});
describe('hasMachineType', () => {
it('should return true when machine type is selected', () => {
- expect(getters.hasMachineType(state)).toEqual(true);
+ expect(hasMachineType(state)).toEqual(true);
+ });
+ });
+
+ describe('hasValidData', () => {
+ it('should return true when a project, zone and machine type are selected', () => {
+ expect(hasValidData(state, { hasZone: true, hasMachineType: true })).toEqual(true);
});
});
});
@@ -46,19 +58,45 @@ describe('GCP Cluster Dropdown Store Getters', () => {
describe('hasProject', () => {
it('should return false when project is not selected', () => {
- expect(getters.hasProject(state)).toEqual(false);
+ expect(hasProject(state)).toEqual(false);
});
});
describe('hasZone', () => {
it('should return false when zone is not selected', () => {
- expect(getters.hasZone(state)).toEqual(false);
+ expect(hasZone(state)).toEqual(false);
});
});
describe('hasMachineType', () => {
it('should return false when machine type is not selected', () => {
- expect(getters.hasMachineType(state)).toEqual(false);
+ expect(hasMachineType(state)).toEqual(false);
+ });
+ });
+
+ describe('hasValidData', () => {
+ let getters;
+
+ beforeEach(() => {
+ getters = { hasZone: true, hasMachineType: true };
+ });
+
+ it('should return false when project is not billable', () => {
+ state.projectHasBillingEnabled = false;
+
+ expect(hasValidData(state, getters)).toEqual(false);
+ });
+
+ it('should return false when zone is not selected', () => {
+ getters.hasZone = false;
+
+ expect(hasValidData(state, getters)).toEqual(false);
+ });
+
+ it('should return false when machine type is not selected', () => {
+ getters.hasMachineType = false;
+
+ expect(hasValidData(state, getters)).toEqual(false);
});
});
});
diff --git a/spec/migrations/20200123155929_remove_invalid_jira_data_spec.rb b/spec/migrations/20200123155929_remove_invalid_jira_data_spec.rb
new file mode 100644
index 00000000000..0e640623ea9
--- /dev/null
+++ b/spec/migrations/20200123155929_remove_invalid_jira_data_spec.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20200123155929_remove_invalid_jira_data.rb')
+
+describe RemoveInvalidJiraData, :migration do
+ let(:jira_tracker_data) { table(:jira_tracker_data) }
+ let(:services) { table(:services) }
+
+ let(:service) { services.create(id: 1) }
+ let(:data) do
+ {
+ service_id: service.id,
+ encrypted_api_url: 'http:url.com',
+ encrypted_api_url_iv: 'somevalue',
+ encrypted_url: 'http:url.com',
+ encrypted_url_iv: 'somevalue',
+ encrypted_username: 'username',
+ encrypted_username_iv: 'somevalue',
+ encrypted_password: 'username',
+ encrypted_password_iv: 'somevalue'
+ }
+ end
+
+ let!(:valid_data) { jira_tracker_data.create(data) }
+ let!(:empty_data) { jira_tracker_data.create(service_id: service.id) }
+ let!(:invalid_api_url) do
+ data[:encrypted_api_url_iv] = nil
+ jira_tracker_data.create(data)
+ end
+ let!(:missing_api_url) do
+ data[:encrypted_api_url] = ''
+ data[:encrypted_api_url_iv] = nil
+ jira_tracker_data.create(data)
+ end
+ let!(:invalid_url) do
+ data[:encrypted_url_iv] = nil
+ jira_tracker_data.create(data)
+ end
+ let!(:missing_url) do
+ data[:encrypted_url] = ''
+ jira_tracker_data.create(data)
+ end
+ let!(:invalid_username) do
+ data[:encrypted_username_iv] = nil
+ jira_tracker_data.create(data)
+ end
+ let!(:missing_username) do
+ data[:encrypted_username] = nil
+ data[:encrypted_username_iv] = nil
+ jira_tracker_data.create(data)
+ end
+ let!(:invalid_password) do
+ data[:encrypted_password_iv] = nil
+ jira_tracker_data.create(data)
+ end
+ let!(:missing_password) do
+ data[:encrypted_password] = nil
+ data[:encrypted_username_iv] = nil
+ jira_tracker_data.create(data)
+ end
+
+ it 'removes the invalid data' do
+ valid_data_records = [valid_data, empty_data, missing_api_url, missing_url, missing_username, missing_password]
+
+ expect { migrate! }.to change { jira_tracker_data.count }.from(10).to(6)
+
+ expect(jira_tracker_data.all).to match_array(valid_data_records)
+ end
+end
diff --git a/spec/services/groups/import_export/export_service_spec.rb b/spec/services/groups/import_export/export_service_spec.rb
index 2024e1ed457..b1f76964722 100644
--- a/spec/services/groups/import_export/export_service_spec.rb
+++ b/spec/services/groups/import_export/export_service_spec.rb
@@ -10,6 +10,10 @@ describe Groups::ImportExport::ExportService do
let(:export_path) { shared.export_path }
let(:service) { described_class.new(group: group, user: user, params: { shared: shared }) }
+ before do
+ group.add_owner(user)
+ end
+
after do
FileUtils.rm_rf(export_path)
end
@@ -30,6 +34,18 @@ describe Groups::ImportExport::ExportService do
end
end
+ context 'when user does not have admin_group permission' do
+ let!(:another_user) { create(:user) }
+ let(:service) { described_class.new(group: group, user: another_user, params: { shared: shared }) }
+
+ it 'fails' do
+ expected_message =
+ "User with ID: %s does not have permission to Group %s with ID: %s." %
+ [another_user.id, group.name, group.id]
+ expect { service.execute }.to raise_error(Gitlab::ImportExport::Error).with_message(expected_message)
+ end
+ end
+
context 'when saving services fail' do
before do
allow(service).to receive_message_chain(:tree_exporter, :save).and_return(false)