From 427b23c12718bea233931431e7d9307881a960c0 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 3 Oct 2019 00:05:59 +0000 Subject: Add latest changes from gitlab-org/gitlab@master --- GITLAB_PAGES_VERSION | 2 +- .../components/eks_cluster_configuration_form.vue | 117 +++++++++++++++++-- .../eks_cluster/components/subnet_dropdown.vue | 0 .../eks_cluster/components/vpc_dropdown.vue | 0 .../eks_cluster/services/aws_services_facade.js | 41 +++++++ .../create_cluster/eks_cluster/store/actions.js | 8 ++ .../eks_cluster/store/cluster_dropdown/actions.js | 4 +- .../create_cluster/eks_cluster/store/index.js | 8 ++ .../eks_cluster/store/mutation_types.js | 3 +- .../create_cluster/eks_cluster/store/mutations.js | 6 + app/controllers/concerns/uploads_actions.rb | 2 +- app/models/ci/pipeline_schedule.rb | 2 + app/models/ci/trigger.rb | 2 + app/models/clusters/concerns/application_core.rb | 2 + app/models/commit_collection.rb | 11 +- app/models/upload.rb | 45 ++++++-- app/uploaders/avatar_uploader.rb | 2 +- app/uploaders/gitlab_uploader.rb | 11 ++ app/workers/import_issues_csv_worker.rb | 2 +- .../object_storage/background_move_worker.rb | 2 +- .../object_storage/migrate_uploads_worker.rb | 2 +- .../unreleased/ac-pull-mirror-branch-prefix.yml | 5 + .../unreleased/add-sorting-to-packages-list.yml | 5 + .../unreleased/bvl-fix-view-mr-deleted-repo.yml | 5 + changelogs/unreleased/pages-1-11-0.yml | 5 + com/lib/com/gitlab/patch/draw_route.rb | 16 +++ com/spec/lib/gitlab/patch/draw_route_spec.rb | 25 ++++ config/application.rb | 10 ++ config/initializers/0_inject_com_module.rb | 26 +++++ config/light_settings.rb | 32 ++++++ ...27_add_pull_mirror_branch_prefix_to_projects.rb | 9 ++ db/schema.rb | 1 + lib/gitlab.rb | 13 +-- .../background_migration/legacy_upload_mover.rb | 2 +- lib/gitlab/import_export/import_export.yml | 1 + lib/gitlab/import_export/uploads_manager.rb | 2 +- lib/gitlab/patch/draw_route.rb | 7 +- lib/gitlab/sanitizers/exif.rb | 2 +- lib/gitlab/verify/uploads.rb | 2 +- locale/gitlab.pot | 63 ++++++++++ spec/com_spec_helper.rb | 3 + spec/factories/uploads.rb | 25 ++-- spec/fast_spec_helper.rb | 2 + ...r_views_merge_request_from_deleted_fork_spec.rb | 31 +++++ .../commit_pipeline_status_component_spec.js.snap | 39 ------- .../commit_pipeline_status_component_spec.js | 39 ++++++- .../eks_cluster_configuration_form_spec.js | 127 ++++++++++++++++++++- .../eks_cluster/store/actions_spec.js | 27 +++-- .../eks_cluster/store/mutations_spec.js | 9 +- .../legacy_upload_mover_spec.rb | 2 +- .../legacy_uploads_migrator_spec.rb | 2 +- .../gitlab/import_export/uploads_manager_spec.rb | 2 +- .../gitlab/import_export/uploads_restorer_spec.rb | 4 +- spec/lib/gitlab/sanitizers/exif_spec.rb | 2 +- spec/lib/gitlab_spec.rb | 12 +- spec/models/commit_collection_spec.rb | 11 ++ spec/models/upload_spec.rb | 48 +++++++- spec/models/uploads/fog_spec.rb | 2 +- .../controllers/uploads_actions_shared_examples.rb | 2 +- .../models/with_uploads_shared_examples.rb | 3 +- spec/uploaders/file_uploader_spec.rb | 10 +- spec/uploaders/gitlab_uploader_spec.rb | 10 ++ .../object_storage/migrate_uploads_worker_spec.rb | 18 +-- 63 files changed, 795 insertions(+), 138 deletions(-) delete mode 100644 app/assets/javascripts/create_cluster/eks_cluster/components/subnet_dropdown.vue delete mode 100644 app/assets/javascripts/create_cluster/eks_cluster/components/vpc_dropdown.vue create mode 100644 changelogs/unreleased/ac-pull-mirror-branch-prefix.yml create mode 100644 changelogs/unreleased/add-sorting-to-packages-list.yml create mode 100644 changelogs/unreleased/bvl-fix-view-mr-deleted-repo.yml create mode 100644 changelogs/unreleased/pages-1-11-0.yml create mode 100644 com/lib/com/gitlab/patch/draw_route.rb create mode 100644 com/spec/lib/gitlab/patch/draw_route_spec.rb create mode 100644 config/initializers/0_inject_com_module.rb create mode 100644 config/light_settings.rb create mode 100644 db/migrate/20190924124627_add_pull_mirror_branch_prefix_to_projects.rb create mode 100644 spec/com_spec_helper.rb create mode 100644 spec/features/merge_request/user_views_merge_request_from_deleted_fork_spec.rb delete mode 100644 spec/frontend/commit/__snapshots__/commit_pipeline_status_component_spec.js.snap diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION index 81c871de46b..1cac385c6cb 100644 --- a/GITLAB_PAGES_VERSION +++ b/GITLAB_PAGES_VERSION @@ -1 +1 @@ -1.10.0 +1.11.0 diff --git a/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue b/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue index 7ed51068d42..6f6b9ad025a 100644 --- a/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue +++ b/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue @@ -1,39 +1,92 @@ @@ -54,8 +107,52 @@ export default { :regions="regions" :error="loadingRegionsError" :loading="isLoadingRegions" - @input="setRegion({ region: $event })" + @input="setRegionAndFetchVpcs($event)" + /> + +
+ + +

+
+
+ + +

diff --git a/app/assets/javascripts/create_cluster/eks_cluster/components/subnet_dropdown.vue b/app/assets/javascripts/create_cluster/eks_cluster/components/subnet_dropdown.vue deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/app/assets/javascripts/create_cluster/eks_cluster/components/vpc_dropdown.vue b/app/assets/javascripts/create_cluster/eks_cluster/components/vpc_dropdown.vue deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/app/assets/javascripts/create_cluster/eks_cluster/services/aws_services_facade.js b/app/assets/javascripts/create_cluster/eks_cluster/services/aws_services_facade.js index 5a13d32e0d2..030b6b384b1 100644 --- a/app/assets/javascripts/create_cluster/eks_cluster/services/aws_services_facade.js +++ b/app/assets/javascripts/create_cluster/eks_cluster/services/aws_services_facade.js @@ -17,4 +17,45 @@ export const fetchRegions = () => .send(); }); +export const fetchVpcs = () => + new Promise((resolve, reject) => { + const ec2 = new EC2(); + + ec2 + .describeVpcs() + .on('success', ({ data: { Vpcs: vpcs } }) => { + const transformedVpcs = vpcs.map(({ VpcId: id }) => ({ id, name: id })); + + resolve(transformedVpcs); + }) + .on('error', error => { + reject(error); + }) + .send(); + }); + +export const fetchSubnets = ({ vpc }) => + new Promise((resolve, reject) => { + const ec2 = new EC2(); + + ec2 + .describeSubnets({ + Filters: [ + { + Name: 'vpc-id', + Values: [vpc.id], + }, + ], + }) + .on('success', ({ data: { Subnets: subnets } }) => { + const transformedSubnets = subnets.map(({ SubnetId: id }) => ({ id, name: id })); + + resolve(transformedSubnets); + }) + .on('error', error => { + reject(error); + }) + .send(); + }); + export default () => {}; diff --git a/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js b/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js index 68493be37e0..0809fc2dfc4 100644 --- a/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js +++ b/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js @@ -4,4 +4,12 @@ export const setRegion = ({ commit }, payload) => { commit(types.SET_REGION, payload); }; +export const setVpc = ({ commit }, payload) => { + commit(types.SET_VPC, payload); +}; + +export const setSubnet = ({ commit }, payload) => { + commit(types.SET_SUBNET, payload); +}; + export default () => {}; diff --git a/app/assets/javascripts/create_cluster/eks_cluster/store/cluster_dropdown/actions.js b/app/assets/javascripts/create_cluster/eks_cluster/store/cluster_dropdown/actions.js index 4f1ecb22719..5d250b2e29e 100644 --- a/app/assets/javascripts/create_cluster/eks_cluster/store/cluster_dropdown/actions.js +++ b/app/assets/javascripts/create_cluster/eks_cluster/store/cluster_dropdown/actions.js @@ -4,10 +4,10 @@ export default fetchItems => ({ requestItems: ({ commit }) => commit(types.REQUEST_ITEMS), receiveItemsSuccess: ({ commit }, payload) => commit(types.RECEIVE_ITEMS_SUCCESS, payload), receiveItemsError: ({ commit }, payload) => commit(types.RECEIVE_ITEMS_ERROR, payload), - fetchItems: ({ dispatch }) => { + fetchItems: ({ dispatch }, payload) => { dispatch('requestItems'); - return fetchItems() + return fetchItems(payload) .then(items => dispatch('receiveItemsSuccess', { items })) .catch(error => dispatch('receiveItemsError', { error })); }, diff --git a/app/assets/javascripts/create_cluster/eks_cluster/store/index.js b/app/assets/javascripts/create_cluster/eks_cluster/store/index.js index 608c4ec9e1e..622095f2cc8 100644 --- a/app/assets/javascripts/create_cluster/eks_cluster/store/index.js +++ b/app/assets/javascripts/create_cluster/eks_cluster/store/index.js @@ -19,6 +19,14 @@ const createStore = () => namespaced: true, ...clusterDropdownStore(awsServices.fetchRegions), }, + vpcs: { + namespaced: true, + ...clusterDropdownStore(awsServices.fetchVpcs), + }, + subnets: { + namespaced: true, + ...clusterDropdownStore(awsServices.fetchSubnets), + }, }, }); diff --git a/app/assets/javascripts/create_cluster/eks_cluster/store/mutation_types.js b/app/assets/javascripts/create_cluster/eks_cluster/store/mutation_types.js index 80c1354482b..ada76b21f18 100644 --- a/app/assets/javascripts/create_cluster/eks_cluster/store/mutation_types.js +++ b/app/assets/javascripts/create_cluster/eks_cluster/store/mutation_types.js @@ -1,2 +1,3 @@ -// eslint-disable-next-line import/prefer-default-export export const SET_REGION = 'SET_REGION'; +export const SET_VPC = 'SET_VPC'; +export const SET_SUBNET = 'SET_SUBNET'; diff --git a/app/assets/javascripts/create_cluster/eks_cluster/store/mutations.js b/app/assets/javascripts/create_cluster/eks_cluster/store/mutations.js index 02f34d1bdcd..346716bb0df 100644 --- a/app/assets/javascripts/create_cluster/eks_cluster/store/mutations.js +++ b/app/assets/javascripts/create_cluster/eks_cluster/store/mutations.js @@ -4,4 +4,10 @@ export default { [types.SET_REGION](state, { region }) { state.selectedRegion = region; }, + [types.SET_VPC](state, { vpc }) { + state.selectedVpc = vpc; + }, + [types.SET_SUBNET](state, { subnet }) { + state.selectedSubnet = subnet; + }, }; diff --git a/app/controllers/concerns/uploads_actions.rb b/app/controllers/concerns/uploads_actions.rb index 60a68cec3c3..79bee6f89d8 100644 --- a/app/controllers/concerns/uploads_actions.rb +++ b/app/controllers/concerns/uploads_actions.rb @@ -91,7 +91,7 @@ module UploadsActions upload_paths = uploader.upload_paths(params[:filename]) upload = Upload.find_by(model: model, uploader: uploader_class.to_s, path: upload_paths) - upload&.build_uploader + upload&.retrieve_uploader end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/models/ci/pipeline_schedule.rb b/app/models/ci/pipeline_schedule.rb index 42d4e86fe8d..946241b7d4c 100644 --- a/app/models/ci/pipeline_schedule.rb +++ b/app/models/ci/pipeline_schedule.rb @@ -86,3 +86,5 @@ module Ci end end end + +Ci::PipelineSchedule.prepend_if_ee('EE::Ci::PipelineSchedule') diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb index 8792c5cf98b..68548bd2fdc 100644 --- a/app/models/ci/trigger.rb +++ b/app/models/ci/trigger.rb @@ -45,3 +45,5 @@ module Ci end end end + +Ci::Trigger.prepend_if_ee('EE::Ci::Trigger') diff --git a/app/models/clusters/concerns/application_core.rb b/app/models/clusters/concerns/application_core.rb index e748c0a855d..979cf0645f5 100644 --- a/app/models/clusters/concerns/application_core.rb +++ b/app/models/clusters/concerns/application_core.rb @@ -64,3 +64,5 @@ module Clusters end end end + +Clusters::Concerns::ApplicationCore.prepend_if_ee('EE::Clusters::Concerns::ApplicationCore') diff --git a/app/models/commit_collection.rb b/app/models/commit_collection.rb index 6b303c52283..d4c29aa295b 100644 --- a/app/models/commit_collection.rb +++ b/app/models/commit_collection.rb @@ -72,8 +72,15 @@ class CommitCollection end.compact] # Replace the commits, keeping the same order - @commits = @commits.map do |c| - replacements.fetch(c.id, c) + @commits = @commits.map do |original_commit| + # Return the original instance: if it didn't need to be batchloaded, it was + # already enriched. + batch_loaded_commit = replacements.fetch(original_commit.id, original_commit) + + # If batch loading the commit failed, fall back to the original commit. + # We need to explicitly check `.nil?` since otherwise a `BatchLoader` instance + # that looks like `nil` is returned. + batch_loaded_commit.nil? ? original_commit : batch_loaded_commit end self diff --git a/app/models/upload.rb b/app/models/upload.rb index 7560002ada8..384949ddb86 100644 --- a/app/models/upload.rb +++ b/app/models/upload.rb @@ -15,7 +15,7 @@ class Upload < ApplicationRecord scope :with_files_stored_remotely, -> { where(store: ObjectStorage::Store::REMOTE) } before_save :calculate_checksum!, if: :foreground_checksummable? - after_commit :schedule_checksum, if: :checksummable? + after_commit :schedule_checksum, if: :needs_checksum? # as the FileUploader is not mounted, the default CarrierWave ActiveRecord # hooks are not executed and the file will not be deleted @@ -53,20 +53,41 @@ class Upload < ApplicationRecord def calculate_checksum! self.checksum = nil - return unless checksummable? + return unless needs_checksum? self.checksum = Digest::SHA256.file(absolute_path).hexdigest end + # Initialize the associated Uploader class with current model + # + # @param [String] mounted_as + # @return [GitlabUploader] one of the subclasses, defined at the model's uploader attribute def build_uploader(mounted_as = nil) uploader_class.new(model, mounted_as || mount_point).tap do |uploader| uploader.upload = self + end + end + + # Initialize the associated Uploader class with current model and + # retrieve existing file from the store to a local cache + # + # @param [String] mounted_as + # @return [GitlabUploader] one of the subclasses, defined at the model's uploader attribute + def retrieve_uploader(mounted_as = nil) + build_uploader(mounted_as).tap do |uploader| uploader.retrieve_from_store!(identifier) end end + # This checks for existence of the upload on storage + # + # @return [Boolean] whether upload exists on storage def exist? - exist = File.exist?(absolute_path) + exist = if local? + File.exist?(absolute_path) + else + retrieve_uploader.exists? + end # Help sysadmins find missing upload files if persisted? && !exist @@ -91,18 +112,24 @@ class Upload < ApplicationRecord store == ObjectStorage::Store::LOCAL end + # Returns whether generating checksum is needed + # + # This takes into account whether file exists, if any checksum exists + # or if the storage has checksum generation code implemented + # + # @return [Boolean] whether generating a checksum is needed + def needs_checksum? + checksum.nil? && local? && exist? + end + private def delete_file! - build_uploader.remove! - end - - def checksummable? - checksum.nil? && local? && exist? + retrieve_uploader.remove! end def foreground_checksummable? - checksummable? && size <= CHECKSUM_THRESHOLD + needs_checksum? && size <= CHECKSUM_THRESHOLD end def schedule_checksum diff --git a/app/uploaders/avatar_uploader.rb b/app/uploaders/avatar_uploader.rb index 9af59b0aceb..d42c9dbedf4 100644 --- a/app/uploaders/avatar_uploader.rb +++ b/app/uploaders/avatar_uploader.rb @@ -19,7 +19,7 @@ class AvatarUploader < GitlabUploader end def absolute_path - self.class.absolute_path(model.avatar.upload) + self.class.absolute_path(upload) end private diff --git a/app/uploaders/gitlab_uploader.rb b/app/uploaders/gitlab_uploader.rb index cefcd3d3f5a..7dc211b14e4 100644 --- a/app/uploaders/gitlab_uploader.rb +++ b/app/uploaders/gitlab_uploader.rb @@ -99,6 +99,17 @@ class GitlabUploader < CarrierWave::Uploader::Base end end + # Used to replace an existing upload with another +file+ without modifying stored metadata + # Use this method only to repair/replace an existing upload, or to upload to a Geo secondary node + # + # @param [CarrierWave::SanitizedFile] file that will replace existing upload + # @return CarrierWave::SanitizedFile + def replace_file_without_saving!(file) + raise ArgumentError, 'should be a CarrierWave::SanitizedFile' unless file.is_a? CarrierWave::SanitizedFile + + storage.store!(file) + end + private # Designed to be overridden by child uploaders that have a dynamic path diff --git a/app/workers/import_issues_csv_worker.rb b/app/workers/import_issues_csv_worker.rb index d44fdfec8ae..b9d7099af71 100644 --- a/app/workers/import_issues_csv_worker.rb +++ b/app/workers/import_issues_csv_worker.rb @@ -12,7 +12,7 @@ class ImportIssuesCsvWorker @project = Project.find(project_id) @upload = Upload.find(upload_id) - importer = Issues::ImportCsvService.new(@user, @project, @upload.build_uploader) + importer = Issues::ImportCsvService.new(@user, @project, @upload.retrieve_uploader) importer.execute @upload.destroy diff --git a/app/workers/object_storage/background_move_worker.rb b/app/workers/object_storage/background_move_worker.rb index 8dff65e46e3..19ccae7739c 100644 --- a/app/workers/object_storage/background_move_worker.rb +++ b/app/workers/object_storage/background_move_worker.rb @@ -22,7 +22,7 @@ module ObjectStorage def build_uploader(subject, mount_point) case subject - when Upload then subject.build_uploader(mount_point) + when Upload then subject.retrieve_uploader(mount_point) else subject.send(mount_point) # rubocop:disable GitlabSecurity/PublicSend end diff --git a/app/workers/object_storage/migrate_uploads_worker.rb b/app/workers/object_storage/migrate_uploads_worker.rb index 55ac7cd9b3c..c9fd19cf9d7 100644 --- a/app/workers/object_storage/migrate_uploads_worker.rb +++ b/app/workers/object_storage/migrate_uploads_worker.rb @@ -119,7 +119,7 @@ module ObjectStorage end def build_uploaders(uploads) - uploads.map { |upload| upload.build_uploader(@mounted_as) } + uploads.map { |upload| upload.retrieve_uploader(@mounted_as) } end def migrate(uploads) diff --git a/changelogs/unreleased/ac-pull-mirror-branch-prefix.yml b/changelogs/unreleased/ac-pull-mirror-branch-prefix.yml new file mode 100644 index 00000000000..b39308ea38a --- /dev/null +++ b/changelogs/unreleased/ac-pull-mirror-branch-prefix.yml @@ -0,0 +1,5 @@ +--- +title: Add pull_mirror_branch_prefix column on projects table +merge_request: 17368 +author: +type: added diff --git a/changelogs/unreleased/add-sorting-to-packages-list.yml b/changelogs/unreleased/add-sorting-to-packages-list.yml new file mode 100644 index 00000000000..8eab37326a1 --- /dev/null +++ b/changelogs/unreleased/add-sorting-to-packages-list.yml @@ -0,0 +1,5 @@ +--- +title: Adds sorting of packages at the project level +merge_request: 15448 +author: +type: added diff --git a/changelogs/unreleased/bvl-fix-view-mr-deleted-repo.yml b/changelogs/unreleased/bvl-fix-view-mr-deleted-repo.yml new file mode 100644 index 00000000000..ffd7135eec1 --- /dev/null +++ b/changelogs/unreleased/bvl-fix-view-mr-deleted-repo.yml @@ -0,0 +1,5 @@ +--- +title: Fix viewing merge reqeust from a fork that's being deleted +merge_request: 17894 +author: +type: fixed diff --git a/changelogs/unreleased/pages-1-11-0.yml b/changelogs/unreleased/pages-1-11-0.yml new file mode 100644 index 00000000000..da798340ee2 --- /dev/null +++ b/changelogs/unreleased/pages-1-11-0.yml @@ -0,0 +1,5 @@ +--- +title: Update Pages to v1.11.0 +merge_request: 18010 +author: +type: other diff --git a/com/lib/com/gitlab/patch/draw_route.rb b/com/lib/com/gitlab/patch/draw_route.rb new file mode 100644 index 00000000000..8626ff06c28 --- /dev/null +++ b/com/lib/com/gitlab/patch/draw_route.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Com + module Gitlab + module Patch + module DrawRoute + extend ::Gitlab::Utils::Override + + override :draw_com + def draw_com(routes_name) + draw_route(route_path("com/config/routes/#{routes_name}.rb")) + end + end + end + end +end diff --git a/com/spec/lib/gitlab/patch/draw_route_spec.rb b/com/spec/lib/gitlab/patch/draw_route_spec.rb new file mode 100644 index 00000000000..823bebd5c0d --- /dev/null +++ b/com/spec/lib/gitlab/patch/draw_route_spec.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' +require 'com_spec_helper' + +describe Gitlab::Patch::DrawRoute do + subject do + Class.new do + include Gitlab::Patch::DrawRoute + + def route_path(route_name) + File.expand_path("../../../../../#{route_name}", __dir__) + end + end.new + end + + before do + allow(subject).to receive(:instance_eval) + end + + it 'raises an error when nothing is drawn' do + expect { subject.draw(:non_existing) } + .to raise_error(described_class::RoutesNotFound) + end +end diff --git a/config/application.rb b/config/application.rb index 5d7c52c5d81..192e836594a 100644 --- a/config/application.rb +++ b/config/application.rb @@ -22,6 +22,7 @@ module Gitlab require_dependency Rails.root.join('lib/gitlab/current_settings') require_dependency Rails.root.join('lib/gitlab/middleware/read_only') require_dependency Rails.root.join('lib/gitlab/middleware/basic_health_check') + require_dependency Rails.root.join('config/light_settings') # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers @@ -62,6 +63,15 @@ module Gitlab config.paths['app/views'].unshift "#{config.root}/ee/app/views" end + if LightSettings.com? + com_paths = config.eager_load_paths.each_with_object([]) do |path, memo| + com_path = config.root.join('com', Pathname.new(path).relative_path_from(config.root)) + memo << com_path.to_s + end + + config.eager_load_paths.push(*com_paths) + end + # Rake tasks ignore the eager loading settings, so we need to set the # autoload paths explicitly config.autoload_paths = config.eager_load_paths.dup diff --git a/config/initializers/0_inject_com_module.rb b/config/initializers/0_inject_com_module.rb new file mode 100644 index 00000000000..9802eb37ec3 --- /dev/null +++ b/config/initializers/0_inject_com_module.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require 'active_support/inflector' + +module InjectComModule + def prepend_if_com(constant, with_descendants: false) + return unless Gitlab.com? + + com_module = constant.constantize + prepend(com_module) + + if with_descendants + descendants.each { |descendant| descendant.prepend(com_module) } + end + end + + def extend_if_com(constant) + extend(constant.constantize) if Gitlab.com? + end + + def include_if_com(constant) + include(constant.constantize) if Gitlab.com? + end +end + +Module.prepend(InjectComModule) diff --git a/config/light_settings.rb b/config/light_settings.rb new file mode 100644 index 00000000000..aa0dd04b3fa --- /dev/null +++ b/config/light_settings.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +class LightSettings + GL_HOST ||= 'gitlab.com' + GL_SUBDOMAIN_REGEX ||= %r{\A[a-z0-9]+\.gitlab\.com\z}.freeze + + class << self + def com? + return Thread.current[:is_com] unless Thread.current[:is_com].nil? + + Thread.current[:is_com] = host == GL_HOST || gl_subdomain? + end + + private + + def config + YAML.safe_load(File.read(settings_path), aliases: true)[Rails.env] + end + + def settings_path + Rails.root.join('config', 'gitlab.yml') + end + + def host + config['gitlab']['host'] + end + + def gl_subdomain? + GL_SUBDOMAIN_REGEX === host + end + end +end diff --git a/db/migrate/20190924124627_add_pull_mirror_branch_prefix_to_projects.rb b/db/migrate/20190924124627_add_pull_mirror_branch_prefix_to_projects.rb new file mode 100644 index 00000000000..b9f729d7d66 --- /dev/null +++ b/db/migrate/20190924124627_add_pull_mirror_branch_prefix_to_projects.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddPullMirrorBranchPrefixToProjects < ActiveRecord::Migration[5.2] + DOWNTIME = false + + def change + add_column :projects, :pull_mirror_branch_prefix, :string, limit: 50 + end +end diff --git a/db/schema.rb b/db/schema.rb index f5eb39d2087..8c22fe80381 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -2922,6 +2922,7 @@ ActiveRecord::Schema.define(version: 2019_09_27_074328) do t.boolean "emails_disabled" t.integer "max_pages_size" t.integer "max_artifacts_size" + t.string "pull_mirror_branch_prefix", limit: 50 t.index "lower((name)::text)", name: "index_projects_on_lower_name" t.index ["archived", "pending_delete", "merge_requests_require_code_owner_approval"], name: "projects_requiring_code_owner_approval", where: "((pending_delete = false) AND (archived = false) AND (merge_requests_require_code_owner_approval = true))" t.index ["created_at"], name: "index_projects_on_created_at" diff --git a/lib/gitlab.rb b/lib/gitlab.rb index b337f5cbf2c..43b3642fd6b 100644 --- a/lib/gitlab.rb +++ b/lib/gitlab.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'pathname' +require_relative '../config/light_settings' module Gitlab def self.root @@ -37,24 +38,18 @@ module Gitlab COM_URL = 'https://gitlab.com' APP_DIRS_PATTERN = %r{^/?(app|config|ee|lib|spec|\(\w*\))}.freeze - SUBDOMAIN_REGEX = %r{\Ahttps://[a-z0-9]+\.gitlab\.com\z}.freeze VERSION = File.read(root.join("VERSION")).strip.freeze INSTALLATION_TYPE = File.read(root.join("INSTALLATION_TYPE")).strip.freeze HTTP_PROXY_ENV_VARS = %w(http_proxy https_proxy HTTP_PROXY HTTPS_PROXY).freeze def self.com? - # Check `gl_subdomain?` as well to keep parity with gitlab.com - Gitlab.config.gitlab.url == COM_URL || gl_subdomain? + LightSettings.com? end def self.org? Gitlab.config.gitlab.url == 'https://dev.gitlab.org' end - def self.gl_subdomain? - SUBDOMAIN_REGEX === Gitlab.config.gitlab.url - end - def self.dev_env_org_or_com? dev_env_or_com? || org? end @@ -79,6 +74,10 @@ module Gitlab yield if ee? end + def self.com + yield if com? + end + def self.http_proxy_env? HTTP_PROXY_ENV_VARS.any? { |name| ENV[name] } end diff --git a/lib/gitlab/background_migration/legacy_upload_mover.rb b/lib/gitlab/background_migration/legacy_upload_mover.rb index 051c1176edb..c9e47f210be 100644 --- a/lib/gitlab/background_migration/legacy_upload_mover.rb +++ b/lib/gitlab/background_migration/legacy_upload_mover.rb @@ -92,7 +92,7 @@ module Gitlab def legacy_file_uploader strong_memoize(:legacy_file_uploader) do - uploader = upload.build_uploader + uploader = upload.retrieve_uploader uploader.retrieve_from_store!(File.basename(upload.path)) uploader end diff --git a/lib/gitlab/import_export/import_export.yml b/lib/gitlab/import_export/import_export.yml index 12a9f72b8cb..141e73e6a47 100644 --- a/lib/gitlab/import_export/import_export.yml +++ b/lib/gitlab/import_export/import_export.yml @@ -139,6 +139,7 @@ excluded_attributes: - :mirror_trigger_builds - :only_mirror_protected_branches - :pull_mirror_available_overridden + - :pull_mirror_branch_prefix - :mirror_overwrites_diverged_branches - :packages_enabled - :mirror_last_update_at diff --git a/lib/gitlab/import_export/uploads_manager.rb b/lib/gitlab/import_export/uploads_manager.rb index e232198150a..dca8e3a7449 100644 --- a/lib/gitlab/import_export/uploads_manager.rb +++ b/lib/gitlab/import_export/uploads_manager.rb @@ -68,7 +68,7 @@ module Gitlab yield(@project.avatar) else project_uploads_except_avatar(avatar_path).find_each(batch_size: UPLOADS_BATCH_SIZE) do |upload| - yield(upload.build_uploader) + yield(upload.retrieve_uploader) end end end diff --git a/lib/gitlab/patch/draw_route.rb b/lib/gitlab/patch/draw_route.rb index 4c8ca015974..fc9d7ae805f 100644 --- a/lib/gitlab/patch/draw_route.rb +++ b/lib/gitlab/patch/draw_route.rb @@ -6,11 +6,12 @@ module Gitlab module Patch module DrawRoute prepend_if_ee('EE::Gitlab::Patch::DrawRoute') # rubocop: disable Cop/InjectEnterpriseEditionModule + prepend_if_com('Com::Gitlab::Patch::DrawRoute') RoutesNotFound = Class.new(StandardError) def draw(routes_name) - drawn_any = draw_ce(routes_name) | draw_ee(routes_name) + drawn_any = draw_ce(routes_name) | draw_ee(routes_name) | draw_com(routes_name) drawn_any || raise(RoutesNotFound.new("Cannot find #{routes_name}")) end @@ -23,6 +24,10 @@ module Gitlab true end + def draw_com(_) + false + end + def route_path(routes_name) Rails.root.join(routes_name) end diff --git a/lib/gitlab/sanitizers/exif.rb b/lib/gitlab/sanitizers/exif.rb index 2f3d14ecebd..5eeb8b00ff3 100644 --- a/lib/gitlab/sanitizers/exif.rb +++ b/lib/gitlab/sanitizers/exif.rb @@ -68,7 +68,7 @@ module Gitlab } relation.find_each(find_params) do |upload| - clean(upload.build_uploader, dry_run: dry_run) + clean(upload.retrieve_uploader, dry_run: dry_run) sleep sleep_time if sleep_time rescue => err logger.error "failed to sanitize #{upload_ref(upload)}: #{err.message}" diff --git a/lib/gitlab/verify/uploads.rb b/lib/gitlab/verify/uploads.rb index 875e8a120e9..afcdbd087d2 100644 --- a/lib/gitlab/verify/uploads.rb +++ b/lib/gitlab/verify/uploads.rb @@ -32,7 +32,7 @@ module Gitlab end def remote_object_exists?(upload) - upload.build_uploader.file.exists? + upload.retrieve_uploader.file.exists? end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 8a00584894b..0c7ddc1e07e 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -2421,6 +2421,9 @@ msgstr "" msgid "Branch not loaded - %{branchId}" msgstr "" +msgid "Branch prefix" +msgstr "" + msgid "BranchSwitcherPlaceholder|Search branches" msgstr "" @@ -3351,6 +3354,9 @@ msgstr "" msgid "ClusterIntegration|Choose a prefix to be used for your namespaces. Defaults to your project path." msgstr "" +msgid "ClusterIntegration|Choose the %{startLink}subnets%{endLink} in your VPC where your worker nodes will run." +msgstr "" + msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications." msgstr "" @@ -3390,9 +3396,15 @@ msgstr "" msgid "ClusterIntegration|Copy Service Token" msgstr "" +msgid "ClusterIntegration|Could not load VPCs for the selected region" +msgstr "" + msgid "ClusterIntegration|Could not load regions from your AWS account" msgstr "" +msgid "ClusterIntegration|Could not load subnets for the selected VPC" +msgstr "" + msgid "ClusterIntegration|Create Kubernetes cluster" msgstr "" @@ -3579,6 +3591,12 @@ msgstr "" msgid "ClusterIntegration|Loading Regions" msgstr "" +msgid "ClusterIntegration|Loading VPCs" +msgstr "" + +msgid "ClusterIntegration|Loading subnets" +msgstr "" + msgid "ClusterIntegration|Machine type" msgstr "" @@ -3591,6 +3609,9 @@ msgstr "" msgid "ClusterIntegration|No IAM Roles found" msgstr "" +msgid "ClusterIntegration|No VPCs found" +msgstr "" + msgid "ClusterIntegration|No machine types matched your search" msgstr "" @@ -3603,6 +3624,9 @@ msgstr "" msgid "ClusterIntegration|No region found" msgstr "" +msgid "ClusterIntegration|No subnet found" +msgstr "" + msgid "ClusterIntegration|No zones matched your search" msgstr "" @@ -3672,6 +3696,9 @@ msgstr "" msgid "ClusterIntegration|Search IAM Roles" msgstr "" +msgid "ClusterIntegration|Search VPCs" +msgstr "" + msgid "ClusterIntegration|Search machine types" msgstr "" @@ -3681,12 +3708,24 @@ msgstr "" msgid "ClusterIntegration|Search regions" msgstr "" +msgid "ClusterIntegration|Search subnets" +msgstr "" + msgid "ClusterIntegration|Search zones" msgstr "" msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster" msgstr "" +msgid "ClusterIntegration|Select a VPC to choose a subnet" +msgstr "" + +msgid "ClusterIntegration|Select a VPC to use for your EKS Cluster resources. To use a new VPC, first create one on %{startLink}Amazon Web Services%{endLink}." +msgstr "" + +msgid "ClusterIntegration|Select a region to choose a VPC" +msgstr "" + msgid "ClusterIntegration|Select machine type" msgstr "" @@ -3735,6 +3774,9 @@ msgstr "" msgid "ClusterIntegration|Specifying a domain will allow you to use Auto Review Apps and Auto Deploy stages for %{auto_devops_start}Auto DevOps%{auto_devops_end}. The domain should have a wildcard DNS configured matching the domain." msgstr "" +msgid "ClusterIntegration|Subnet" +msgstr "" + msgid "ClusterIntegration|The Kubernetes certificate used to authenticate to the cluster." msgstr "" @@ -3783,6 +3825,9 @@ msgstr "" msgid "ClusterIntegration|Uses the Cloud Run, Istio, and HTTP Load Balancing addons for this cluster." msgstr "" +msgid "ClusterIntegration|VPC" +msgstr "" + msgid "ClusterIntegration|Validating project billing status" msgstr "" @@ -3831,9 +3876,15 @@ msgstr "" msgid "ClusterIntegration|sign up" msgstr "" +msgid "ClusterIntergation|Select a VPC" +msgstr "" + msgid "ClusterIntergation|Select a region" msgstr "" +msgid "ClusterIntergation|Select a subnet" +msgstr "" + msgid "ClusterIntergation|Select role name" msgstr "" @@ -8640,6 +8691,9 @@ msgstr "" msgid "Introducing Your Conversational Development Index" msgstr "" +msgid "Invalid Git ref" +msgstr "" + msgid "Invalid Insights config file detected" msgstr "" @@ -10120,6 +10174,9 @@ msgstr "" msgid "Mirror user" msgstr "" +msgid "Mirrored branches will have this prefix. If you enabled 'Only mirror protected branches' you need to include this prefix on protected branches in this project or nothing will be mirrored." +msgstr "" + msgid "Mirrored repositories" msgstr "" @@ -14890,6 +14947,12 @@ msgstr "" msgid "SortOptions|Start soon" msgstr "" +msgid "SortOptions|Type" +msgstr "" + +msgid "SortOptions|Version" +msgstr "" + msgid "SortOptions|Weight" msgstr "" diff --git a/spec/com_spec_helper.rb b/spec/com_spec_helper.rb new file mode 100644 index 00000000000..f71ccde9509 --- /dev/null +++ b/spec/com_spec_helper.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +Settings.gitlab[:url] = "https://test.gitlab.com" diff --git a/spec/factories/uploads.rb b/spec/factories/uploads.rb index bfe02c6010b..ef464d3d6e0 100644 --- a/spec/factories/uploads.rb +++ b/spec/factories/uploads.rb @@ -2,7 +2,7 @@ FactoryBot.define do factory :upload do - model { build(:project) } + model { create(:project) } size { 100.kilobytes } uploader { "AvatarUploader" } mount_point { :avatar } @@ -11,23 +11,27 @@ FactoryBot.define do # we should build a mount agnostic upload by default transient do - filename { 'myfile.jpg' } + filename { 'avatar.jpg' } end - # this needs to comply with RecordsUpload::Concern#upload_path - path { File.join("uploads/-/system", model.class.underscore, mount_point.to_s, 'avatar.jpg') } + path do + uploader_instance = Object.const_get(uploader.to_s).new(model, mount_point) + File.join(uploader_instance.store_dir, filename) + end trait :personal_snippet_upload do - uploader { "PersonalFileUploader" } + model { create(:personal_snippet) } path { File.join(secret, filename) } - model { build(:personal_snippet) } + uploader { "PersonalFileUploader" } secret { SecureRandom.hex } + mount_point { nil } end trait :issuable_upload do uploader { "FileUploader" } path { File.join(secret, filename) } secret { SecureRandom.hex } + mount_point { nil } end trait :with_file do @@ -42,22 +46,23 @@ FactoryBot.define do end trait :namespace_upload do - model { build(:group) } + model { create(:group) } path { File.join(secret, filename) } uploader { "NamespaceFileUploader" } secret { SecureRandom.hex } + mount_point { nil } end trait :favicon_upload do - model { build(:appearance) } - path { File.join(secret, filename) } + model { create(:appearance) } uploader { "FaviconUploader" } secret { SecureRandom.hex } + mount_point { :favicon } end trait :attachment_upload do mount_point { :attachment } - model { build(:note) } + model { create(:note) } uploader { "AttachmentUploader" } end end diff --git a/spec/fast_spec_helper.rb b/spec/fast_spec_helper.rb index f5a487b4d57..a7810d72671 100644 --- a/spec/fast_spec_helper.rb +++ b/spec/fast_spec_helper.rb @@ -5,10 +5,12 @@ ENV['IN_MEMORY_APPLICATION_SETTINGS'] = 'true' require 'active_support/dependencies' require_relative '../config/initializers/0_inject_enterprise_edition_module' +require_relative '../config/initializers/0_inject_com_module' require_relative '../config/settings' require_relative 'support/rspec' require 'active_support/all' ActiveSupport::Dependencies.autoload_paths << 'lib' ActiveSupport::Dependencies.autoload_paths << 'ee/lib' +ActiveSupport::Dependencies.autoload_paths << 'com/lib' ActiveSupport::XmlMini.backend = 'Nokogiri' diff --git a/spec/features/merge_request/user_views_merge_request_from_deleted_fork_spec.rb b/spec/features/merge_request/user_views_merge_request_from_deleted_fork_spec.rb new file mode 100644 index 00000000000..26984a1fb5e --- /dev/null +++ b/spec/features/merge_request/user_views_merge_request_from_deleted_fork_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true +require 'spec_helper' + +# This is a feature spec because the problems arrise when rendering the view for +# an actual project for which the repository is removed but the cached not +# updated. +# This can occur when the fork a merge request is created from is in the process +# of being destroyed. +describe 'User views merged merge request from deleted fork' do + include ProjectForksHelper + + let(:project) { create(:project, :repository) } + let(:source_project) { fork_project(project, nil, repository: true) } + let(:user) { project.owner } + let!(:merge_request) { create(:merge_request, :merged, source_project: source_project, target_project: project) } + + before do + sign_in user + + fork_owner = source_project.namespace.owners.first + # Place the source_project in the weird in between state + source_project.update_attribute(:pending_delete, true) + Projects::DestroyService.new(source_project, fork_owner, {}).__send__(:trash_repositories!) + end + + it 'correctly shows the merge request' do + visit(merge_request_path(merge_request)) + + expect(page).to have_content(merge_request.title) + end +end diff --git a/spec/frontend/commit/__snapshots__/commit_pipeline_status_component_spec.js.snap b/spec/frontend/commit/__snapshots__/commit_pipeline_status_component_spec.js.snap deleted file mode 100644 index 9199db69fed..00000000000 --- a/spec/frontend/commit/__snapshots__/commit_pipeline_status_component_spec.js.snap +++ /dev/null @@ -1,39 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Commit pipeline status component when polling is not successful renders not found CI icon without loader 1`] = ` - -`; - -exports[`Commit pipeline status component when polling is successful renders CI icon without loader 1`] = ` - -`; diff --git a/spec/frontend/commit/commit_pipeline_status_component_spec.js b/spec/frontend/commit/commit_pipeline_status_component_spec.js index 1768fd745c9..1736d1d0df8 100644 --- a/spec/frontend/commit/commit_pipeline_status_component_spec.js +++ b/spec/frontend/commit/commit_pipeline_status_component_spec.js @@ -2,6 +2,7 @@ import Visibility from 'visibilityjs'; import { GlLoadingIcon } from '@gitlab/ui'; import Poll from '~/lib/utils/poll'; import flash from '~/flash'; +import CiIcon from '~/vue_shared/components/ci_icon.vue'; import CommitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue'; import { shallowMount } from '@vue/test-utils'; import { getJSONFixture } from '../helpers/fixtures'; @@ -36,6 +37,10 @@ describe('Commit pipeline status component', () => { }); }; + const findLoader = () => wrapper.find(GlLoadingIcon); + const findLink = () => wrapper.find('a'); + const findCiIcon = () => findLink().find(CiIcon); + afterEach(() => { wrapper.destroy(); wrapper = null; @@ -111,14 +116,14 @@ describe('Commit pipeline status component', () => { it('shows the loading icon at start', () => { createComponent(); - expect(wrapper.find(GlLoadingIcon).exists()).toBe(true); + expect(findLoader().exists()).toBe(true); pollConfig.successCallback({ data: { pipelines: [] }, }); return wrapper.vm.$nextTick().then(() => { - expect(wrapper.find(GlLoadingIcon).exists()).toBe(false); + expect(findLoader().exists()).toBe(false); }); }); @@ -130,8 +135,17 @@ describe('Commit pipeline status component', () => { return wrapper.vm.$nextTick(); }); - it('renders CI icon without loader', () => { - expect(wrapper.element).toMatchSnapshot(); + it('does not render loader', () => { + expect(findLoader().exists()).toBe(false); + }); + + it('renders link with href', () => { + expect(findLink().attributes('href')).toEqual(mockCiStatus.details_path); + }); + + it('renders CI icon', () => { + expect(findCiIcon().attributes('data-original-title')).toEqual('Pipeline: pending'); + expect(findCiIcon().props('status')).toEqual(mockCiStatus); }); }); @@ -140,8 +154,21 @@ describe('Commit pipeline status component', () => { pollConfig.errorCallback(); }); - it('renders not found CI icon without loader', () => { - expect(wrapper.element).toMatchSnapshot(); + it('does not render loader', () => { + expect(findLoader().exists()).toBe(false); + }); + + it('renders link with href', () => { + expect(findLink().attributes('href')).toBeUndefined(); + }); + + it('renders not found CI icon', () => { + expect(findCiIcon().attributes('data-original-title')).toEqual('Pipeline: not found'); + expect(findCiIcon().props('status')).toEqual({ + text: 'not found', + icon: 'status_notfound', + group: 'notfound', + }); }); it('displays flash error message', () => { diff --git a/spec/frontend/create_cluster/eks_cluster/components/eks_cluster_configuration_form_spec.js b/spec/frontend/create_cluster/eks_cluster/components/eks_cluster_configuration_form_spec.js index 1bd16d356b9..cc7c6735a80 100644 --- a/spec/frontend/create_cluster/eks_cluster/components/eks_cluster_configuration_form_spec.js +++ b/spec/frontend/create_cluster/eks_cluster/components/eks_cluster_configuration_form_spec.js @@ -4,6 +4,7 @@ import Vue from 'vue'; import EksClusterConfigurationForm from '~/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue'; import RegionDropdown from '~/create_cluster/eks_cluster/components/region_dropdown.vue'; +import eksClusterFormState from '~/create_cluster/eks_cluster/store/state'; import clusterDropdownStoreState from '~/create_cluster/eks_cluster/store/cluster_dropdown/state'; const localVue = createLocalVue(); @@ -12,29 +13,59 @@ localVue.use(Vuex); describe('EksClusterConfigurationForm', () => { let store; let actions; + let state; let regionsState; + let vpcsState; + let subnetsState; + let vpcsActions; let regionsActions; + let subnetsActions; let vm; beforeEach(() => { + state = eksClusterFormState(); actions = { setRegion: jest.fn(), setVpc: jest.fn(), + setSubnet: jest.fn(), }; regionsActions = { fetchItems: jest.fn(), }; + vpcsActions = { + fetchItems: jest.fn(), + }; + subnetsActions = { + fetchItems: jest.fn(), + }; regionsState = { ...clusterDropdownStoreState(), }; + vpcsState = { + ...clusterDropdownStoreState(), + }; + subnetsState = { + ...clusterDropdownStoreState(), + }; store = new Vuex.Store({ + state, actions, modules: { + vpcs: { + namespaced: true, + state: vpcsState, + actions: vpcsActions, + }, regions: { namespaced: true, state: regionsState, actions: regionsActions, }, + subnets: { + namespaced: true, + state: subnetsState, + actions: subnetsActions, + }, }, }); }); @@ -51,6 +82,8 @@ describe('EksClusterConfigurationForm', () => { }); const findRegionDropdown = () => vm.find(RegionDropdown); + const findVpcDropdown = () => vm.find('[field-id="eks-vpc"]'); + const findSubnetDropdown = () => vm.find('[field-id="eks-subnet"]'); describe('when mounted', () => { it('fetches available regions', () => { @@ -62,16 +95,72 @@ describe('EksClusterConfigurationForm', () => { regionsState.isLoadingItems = true; return Vue.nextTick().then(() => { - expect(findRegionDropdown().props('loading')).toEqual(regionsState.isLoadingItems); + expect(findRegionDropdown().props('loading')).toBe(regionsState.isLoadingItems); }); }); it('sets regions to RegionDropdown regions property', () => { - expect(findRegionDropdown().props('regions')).toEqual(regionsState.items); + expect(findRegionDropdown().props('regions')).toBe(regionsState.items); }); it('sets loadingRegionsError to RegionDropdown error property', () => { - expect(findRegionDropdown().props('error')).toEqual(regionsState.loadingItemsError); + expect(findRegionDropdown().props('error')).toBe(regionsState.loadingItemsError); + }); + + it('disables VpcDropdown when no region is selected', () => { + expect(findVpcDropdown().props('disabled')).toBe(true); + }); + + it('enables VpcDropdown when no region is selected', () => { + state.selectedRegion = { name: 'west-1 ' }; + + return Vue.nextTick().then(() => { + expect(findVpcDropdown().props('disabled')).toBe(false); + }); + }); + + it('sets isLoadingVpcs to VpcDropdown loading property', () => { + vpcsState.isLoadingItems = true; + + return Vue.nextTick().then(() => { + expect(findVpcDropdown().props('loading')).toBe(vpcsState.isLoadingItems); + }); + }); + + it('sets vpcs to VpcDropdown items property', () => { + expect(findVpcDropdown().props('items')).toBe(vpcsState.items); + }); + + it('sets loadingVpcsError to VpcDropdown hasErrors property', () => { + expect(findVpcDropdown().props('hasErrors')).toBe(vpcsState.loadingItemsError); + }); + + it('disables SubnetDropdown when no vpc is selected', () => { + expect(findSubnetDropdown().props('disabled')).toBe(true); + }); + + it('enables SubnetDropdown when a vpc is selected', () => { + state.selectedVpc = { name: 'vpc-1 ' }; + + return Vue.nextTick().then(() => { + expect(findSubnetDropdown().props('disabled')).toBe(false); + }); + }); + + it('sets isLoadingSubnets to SubnetDropdown loading property', () => { + subnetsState.isLoadingItems = true; + + return Vue.nextTick().then(() => { + expect(findSubnetDropdown().props('loading')).toBe(subnetsState.isLoadingItems); + }); + }); + + it('sets subnets to SubnetDropdown items property', () => { + expect(findSubnetDropdown().props('items')).toBe(subnetsState.items); + }); + + it('sets loadingSubnetsError to SubnetDropdown hasErrors property', () => { + expect(findSubnetDropdown().props('hasErrors')).toBe(subnetsState.loadingItemsError); }); describe('when region is selected', () => { @@ -84,5 +173,37 @@ describe('EksClusterConfigurationForm', () => { it('dispatches setRegion action', () => { expect(actions.setRegion).toHaveBeenCalledWith(expect.anything(), { region }, undefined); }); + + it('fetches available vpcs', () => { + expect(vpcsActions.fetchItems).toHaveBeenCalledWith(expect.anything(), { region }, undefined); + }); + }); + + describe('when vpc is selected', () => { + const vpc = { name: 'vpc-1' }; + + beforeEach(() => { + findVpcDropdown().vm.$emit('input', vpc); + }); + + it('dispatches setVpc action', () => { + expect(actions.setVpc).toHaveBeenCalledWith(expect.anything(), { vpc }, undefined); + }); + + it('dispatches fetchSubnets action', () => { + expect(subnetsActions.fetchItems).toHaveBeenCalledWith(expect.anything(), { vpc }, undefined); + }); + }); + + describe('when a subnet is selected', () => { + const subnet = { name: 'subnet-1' }; + + beforeEach(() => { + findSubnetDropdown().vm.$emit('input', subnet); + }); + + it('dispatches setSubnet action', () => { + expect(actions.setSubnet).toHaveBeenCalledWith(expect.anything(), { subnet }, undefined); + }); }); }); diff --git a/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js b/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js index 3744ddb3dc6..893c657e699 100644 --- a/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js +++ b/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js @@ -1,17 +1,28 @@ import testAction from 'helpers/vuex_action_helper'; import createState from '~/create_cluster/eks_cluster/store/state'; -import * as types from '~/create_cluster/eks_cluster/store/mutation_types'; import * as actions from '~/create_cluster/eks_cluster/store/actions'; +import { SET_REGION, SET_VPC, SET_SUBNET } from '~/create_cluster/eks_cluster/store/mutation_types'; describe('EKS Cluster Store Actions', () => { - describe('setRegion', () => { - it(`commits ${types.SET_REGION} mutation`, () => { - const region = { name: 'west-1' }; + let region; + let vpc; + let subnet; - testAction(actions.setRegion, { region }, createState(), [ - { type: types.SET_REGION, payload: { region } }, - ]); - }); + beforeEach(() => { + region = { name: 'regions-1' }; + vpc = { name: 'vpc-1' }; + subnet = { name: 'subnet-1' }; + }); + + it.each` + action | mutation | payload | payloadDescription + ${'setRegion'} | ${SET_REGION} | ${{ region }} | ${'region'} + ${'setVpc'} | ${SET_VPC} | ${{ vpc }} | ${'vpc'} + ${'setSubnet'} | ${SET_SUBNET} | ${{ subnet }} | ${'subnet'} + `(`$action commits $mutation with $payloadDescription payload`, data => { + const { action, mutation, payload } = data; + + testAction(actions[action], payload, createState(), [{ type: mutation, payload }]); }); }); diff --git a/spec/frontend/create_cluster/eks_cluster/store/mutations_spec.js b/spec/frontend/create_cluster/eks_cluster/store/mutations_spec.js index 99f05b0f449..38199471f79 100644 --- a/spec/frontend/create_cluster/eks_cluster/store/mutations_spec.js +++ b/spec/frontend/create_cluster/eks_cluster/store/mutations_spec.js @@ -1,19 +1,26 @@ -import { SET_REGION } from '~/create_cluster/eks_cluster/store/mutation_types'; +import { SET_REGION, SET_VPC, SET_SUBNET } from '~/create_cluster/eks_cluster/store/mutation_types'; import createState from '~/create_cluster/eks_cluster/store/state'; import mutations from '~/create_cluster/eks_cluster/store/mutations'; describe('Create EKS cluster store mutations', () => { let state; let region; + let vpc; + let subnet; beforeEach(() => { region = { name: 'regions-1' }; + vpc = { name: 'vpc-1' }; + subnet = { name: 'subnet-1' }; + state = createState(); }); it.each` mutation | mutatedProperty | payload | expectedValue | expectedValueDescription ${SET_REGION} | ${'selectedRegion'} | ${{ region }} | ${region} | ${'selected region payload'} + ${SET_VPC} | ${'selectedVpc'} | ${{ vpc }} | ${vpc} | ${'selected vpc payload'} + ${SET_SUBNET} | ${'selectedSubnet'} | ${{ subnet }} | ${subnet} | ${'selected sybnet payload'} `(`$mutation sets $mutatedProperty to $expectedValueDescription`, data => { const { mutation, mutatedProperty, payload, expectedValue } = data; diff --git a/spec/lib/gitlab/background_migration/legacy_upload_mover_spec.rb b/spec/lib/gitlab/background_migration/legacy_upload_mover_spec.rb index 7d67dc0251d..c1eaf1d3433 100644 --- a/spec/lib/gitlab/background_migration/legacy_upload_mover_spec.rb +++ b/spec/lib/gitlab/background_migration/legacy_upload_mover_spec.rb @@ -32,7 +32,7 @@ describe Gitlab::BackgroundMigration::LegacyUploadMover do if with_file upload = create(:upload, :with_file, :attachment_upload, params) - model.update(attachment: upload.build_uploader) + model.update(attachment: upload.retrieve_uploader) model.attachment.upload else create(:upload, :attachment_upload, params) diff --git a/spec/lib/gitlab/background_migration/legacy_uploads_migrator_spec.rb b/spec/lib/gitlab/background_migration/legacy_uploads_migrator_spec.rb index ed8cbfeb11f..cabca3dbef9 100644 --- a/spec/lib/gitlab/background_migration/legacy_uploads_migrator_spec.rb +++ b/spec/lib/gitlab/background_migration/legacy_uploads_migrator_spec.rb @@ -24,7 +24,7 @@ describe Gitlab::BackgroundMigration::LegacyUploadsMigrator do if with_file upload = create(:upload, :with_file, :attachment_upload, params) - model.update(attachment: upload.build_uploader) + model.update(attachment: upload.retrieve_uploader) model.attachment.upload else create(:upload, :attachment_upload, params) diff --git a/spec/lib/gitlab/import_export/uploads_manager_spec.rb b/spec/lib/gitlab/import_export/uploads_manager_spec.rb index 792117e1df1..f13f639d6b7 100644 --- a/spec/lib/gitlab/import_export/uploads_manager_spec.rb +++ b/spec/lib/gitlab/import_export/uploads_manager_spec.rb @@ -83,7 +83,7 @@ describe Gitlab::ImportExport::UploadsManager do it 'restores the file' do manager.restore - expect(project.uploads.map { |u| u.build_uploader.filename }).to include('dummy.txt') + expect(project.uploads.map { |u| u.retrieve_uploader.filename }).to include('dummy.txt') end end end diff --git a/spec/lib/gitlab/import_export/uploads_restorer_spec.rb b/spec/lib/gitlab/import_export/uploads_restorer_spec.rb index 6072f18b8c7..e2e8204b2fa 100644 --- a/spec/lib/gitlab/import_export/uploads_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/uploads_restorer_spec.rb @@ -27,7 +27,7 @@ describe Gitlab::ImportExport::UploadsRestorer do it 'copies the uploads to the project path' do subject.restore - expect(project.uploads.map { |u| u.build_uploader.filename }).to include('dummy.txt') + expect(project.uploads.map { |u| u.retrieve_uploader.filename }).to include('dummy.txt') end end @@ -43,7 +43,7 @@ describe Gitlab::ImportExport::UploadsRestorer do it 'copies the uploads to the project path' do subject.restore - expect(project.uploads.map { |u| u.build_uploader.filename }).to include('dummy.txt') + expect(project.uploads.map { |u| u.retrieve_uploader.filename }).to include('dummy.txt') end end end diff --git a/spec/lib/gitlab/sanitizers/exif_spec.rb b/spec/lib/gitlab/sanitizers/exif_spec.rb index f882dbbdb5c..11e430e0be4 100644 --- a/spec/lib/gitlab/sanitizers/exif_spec.rb +++ b/spec/lib/gitlab/sanitizers/exif_spec.rb @@ -58,7 +58,7 @@ describe Gitlab::Sanitizers::Exif do end describe '#clean' do - let(:uploader) { create(:upload, :with_file, :issuable_upload).build_uploader } + let(:uploader) { create(:upload, :with_file, :issuable_upload).retrieve_uploader } context "no dry run" do it "removes exif from the image" do diff --git a/spec/lib/gitlab_spec.rb b/spec/lib/gitlab_spec.rb index ccb5cb3aa43..7e318017a05 100644 --- a/spec/lib/gitlab_spec.rb +++ b/spec/lib/gitlab_spec.rb @@ -71,26 +71,30 @@ describe Gitlab do end describe '.com?' do + before do + Thread.current[:is_com] = nil + end + it 'is true when on GitLab.com' do - stub_config_setting(url: 'https://gitlab.com') + allow(LightSettings).to receive(:host).and_return('gitlab.com') expect(described_class.com?).to eq true end it 'is true when on staging' do - stub_config_setting(url: 'https://staging.gitlab.com') + allow(LightSettings).to receive(:host).and_return('staging.gitlab.com') expect(described_class.com?).to eq true end it 'is true when on other gitlab subdomain' do - stub_config_setting(url: 'https://example.gitlab.com') + allow(LightSettings).to receive(:host).and_return('example.gitlab.com') expect(described_class.com?).to eq true end it 'is false when not on GitLab.com' do - stub_config_setting(url: 'http://example.com') + allow(LightSettings).to receive(:host).and_return('example.com') expect(described_class.com?).to eq false end diff --git a/spec/models/commit_collection_spec.rb b/spec/models/commit_collection_spec.rb index a8957bbfdd0..d49b71db5f8 100644 --- a/spec/models/commit_collection_spec.rb +++ b/spec/models/commit_collection_spec.rb @@ -149,6 +149,17 @@ describe CommitCollection do collection.enrich! end + + it 'returns the original commit if the commit could not be lazy loaded' do + collection = described_class.new(project, [hash_commit]) + unexisting_lazy_commit = Commit.lazy(project, Gitlab::Git::BLANK_SHA) + + expect(Commit).to receive(:lazy).with(project, hash_commit.id).and_return(unexisting_lazy_commit) + + collection.enrich! + + expect(collection.commits).to contain_exactly(hash_commit) + end end end diff --git a/spec/models/upload_spec.rb b/spec/models/upload_spec.rb index d97bb8cfb90..03434c95218 100644 --- a/spec/models/upload_spec.rb +++ b/spec/models/upload_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Upload do - describe 'assocations' do + describe 'associations' do it { is_expected.to belong_to(:model) } end @@ -107,6 +107,52 @@ describe Upload do end end + describe '#build_uploader' do + it 'returns a uploader object with current upload associated with it' do + subject = build(:upload) + uploader = subject.build_uploader + + expect(uploader.upload).to eq(subject) + expect(uploader.mounted_as).to eq(subject.send(:mount_point)) + expect(uploader.file).to be_nil + end + end + + describe '#retrieve_uploader' do + it 'returns a uploader object with current uploader associated with and cache retrieved' do + subject = build(:upload) + uploader = subject.retrieve_uploader + + expect(uploader.upload).to eq(subject) + expect(uploader.mounted_as).to eq(subject.send(:mount_point)) + expect(uploader.file).not_to be_nil + end + end + + describe '#needs_checksum?' do + context 'with local storage' do + it 'returns true when no checksum exists' do + subject = create(:upload, :with_file, checksum: nil) + + expect(subject.needs_checksum?).to be_truthy + end + + it 'returns false when checksum is already present' do + subject = create(:upload, :with_file, checksum: 'something') + + expect(subject.needs_checksum?).to be_falsey + end + end + + context 'with remote storage' do + subject { build(:upload, :object_storage) } + + it 'returns false' do + expect(subject.needs_checksum?).to be_falsey + end + end + end + describe '#exist?' do it 'returns true when the file exists' do upload = described_class.new(path: __FILE__, store: ObjectStorage::Store::LOCAL) diff --git a/spec/models/uploads/fog_spec.rb b/spec/models/uploads/fog_spec.rb index 4a44cf5ab0f..b93d9449da9 100644 --- a/spec/models/uploads/fog_spec.rb +++ b/spec/models/uploads/fog_spec.rb @@ -44,7 +44,7 @@ describe Uploads::Fog do subject { data_store.delete_keys(keys) } before do - uploads.each { |upload| upload.build_uploader.migrate!(2) } + uploads.each { |upload| upload.retrieve_uploader.migrate!(2) } end it 'deletes multiple data' do diff --git a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb index 4bc22861d58..0b4ab9941fc 100644 --- a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb +++ b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb @@ -104,7 +104,7 @@ shared_examples 'handle uploads' do context "when neither the uploader nor the model exists" do before do - allow_any_instance_of(Upload).to receive(:build_uploader).and_return(nil) + allow_any_instance_of(Upload).to receive(:retrieve_uploader).and_return(nil) allow(controller).to receive(:find_model).and_return(nil) end diff --git a/spec/support/shared_examples/models/with_uploads_shared_examples.rb b/spec/support/shared_examples/models/with_uploads_shared_examples.rb index eb1ade03017..822836c771e 100644 --- a/spec/support/shared_examples/models/with_uploads_shared_examples.rb +++ b/spec/support/shared_examples/models/with_uploads_shared_examples.rb @@ -41,7 +41,8 @@ shared_examples_for 'model with uploads' do |supports_fileuploads| end it 'deletes remote files' do - expect_any_instance_of(Uploads::Fog).to receive(:delete_keys).with(uploads.map(&:path)) + expected_array = array_including(*uploads.map(&:path)) + expect_any_instance_of(Uploads::Fog).to receive(:delete_keys).with(expected_array) model_object.destroy end diff --git a/spec/uploaders/file_uploader_spec.rb b/spec/uploaders/file_uploader_spec.rb index 04206de3dc6..3c14edc7e0e 100644 --- a/spec/uploaders/file_uploader_spec.rb +++ b/spec/uploaders/file_uploader_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe FileUploader do let(:group) { create(:group, name: 'awesome') } let(:project) { create(:project, :legacy_storage, namespace: group, name: 'project') } - let(:uploader) { described_class.new(project) } + let(:uploader) { described_class.new(project, :avatar) } let(:upload) { double(model: project, path: 'secret/foo.jpg') } subject { uploader } @@ -184,6 +184,14 @@ describe FileUploader do end end + describe '#replace_file_without_saving!' do + let(:replacement) { Tempfile.create('replacement.jpg') } + + it 'replaces an existing file without changing its metadata' do + expect { subject.replace_file_without_saving! CarrierWave::SanitizedFile.new(replacement) }.not_to change { subject.upload } + end + end + context 'when remote file is used' do let(:temp_file) { Tempfile.new("test") } diff --git a/spec/uploaders/gitlab_uploader_spec.rb b/spec/uploaders/gitlab_uploader_spec.rb index 3bee4875348..4425dd947c0 100644 --- a/spec/uploaders/gitlab_uploader_spec.rb +++ b/spec/uploaders/gitlab_uploader_spec.rb @@ -69,6 +69,16 @@ describe GitlabUploader do end end + describe '#replace_file_without_saving!' do + it 'allows file to be replaced without triggering any callbacks' do + new_file = CarrierWave::SanitizedFile.new(Tempfile.new) + + expect(subject).not_to receive(:with_callbacks) + + subject.replace_file_without_saving!(new_file) + end + end + describe '#open' do context 'when trace is stored in File storage' do context 'when file exists' do diff --git a/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb b/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb index 6c2544d2efd..97e8a43f7fd 100644 --- a/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb +++ b/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb @@ -42,33 +42,23 @@ describe ObjectStorage::MigrateUploadsWorker, :sidekiq do end describe '.sanity_check!' do - shared_examples 'raises a SanityCheckError' do + shared_examples 'raises a SanityCheckError' do |expected_message| let(:mount_point) { nil } it do expect { described_class.sanity_check!(uploads, model_class, mount_point) } - .to raise_error(described_class::SanityCheckError) + .to raise_error(described_class::SanityCheckError).with_message(expected_message) end end - before do - stub_const("WrongModel", Class.new) - end - context 'uploader types mismatch' do let!(:outlier) { create(:upload, uploader: 'GitlabUploader') } - include_examples 'raises a SanityCheckError' - end - - context 'model types mismatch' do - let!(:outlier) { create(:upload, model_type: 'WrongModel') } - - include_examples 'raises a SanityCheckError' + include_examples 'raises a SanityCheckError', /Multiple uploaders found/ end context 'mount point not found' do - include_examples 'raises a SanityCheckError' do + include_examples 'raises a SanityCheckError', /Mount point [a-z:]+ not found in/ do let(:mount_point) { :potato } end end -- cgit v1.2.3