diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 06a5b95f40e..a7e60b90841 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -410,7 +410,7 @@ class ApplicationSetting < ApplicationRecord if: :external_authorization_service_enabled validates :spam_check_endpoint_url, - addressable_url: { schemes: %w(grpc) }, allow_blank: true + addressable_url: { schemes: %w(tls grpc) }, allow_blank: true validates :spam_check_endpoint_url, presence: true, diff --git a/app/models/bulk_imports/file_transfer/base_config.rb b/app/models/bulk_imports/file_transfer/base_config.rb index e735503a47f..036d511bc59 100644 --- a/app/models/bulk_imports/file_transfer/base_config.rb +++ b/app/models/bulk_imports/file_transfer/base_config.rb @@ -6,6 +6,7 @@ module BulkImports include Gitlab::Utils::StrongMemoize UPLOADS_RELATION = 'uploads' + SELF_RELATION = 'self' def initialize(portable) @portable = portable @@ -28,7 +29,11 @@ module BulkImports end def portable_relations - tree_relations + file_relations - skipped_relations + tree_relations + file_relations + self_relation - skipped_relations + end + + def self_relation?(relation) + relation == SELF_RELATION end def tree_relation?(relation) @@ -45,6 +50,10 @@ module BulkImports portable_tree[:include].find { |include| include[relation.to_sym] } end + def portable_relations_tree + @portable_relations_tree ||= attributes_finder.find_relations_tree(portable_class_sym).deep_stringify_keys + end + private attr_reader :portable @@ -67,10 +76,6 @@ module BulkImports @portable_class_sym ||= portable_class.to_s.demodulize.underscore.to_sym end - def portable_relations_tree - @portable_relations_tree ||= attributes_finder.find_relations_tree(portable_class_sym).deep_stringify_keys - end - def import_export_yaml raise NotImplementedError end @@ -86,6 +91,10 @@ module BulkImports def skipped_relations [] end + + def self_relation + [SELF_RELATION] + end end end end diff --git a/app/services/bulk_imports/relation_export_service.rb b/app/services/bulk_imports/relation_export_service.rb index 4718b3914b2..14f073120c5 100644 --- a/app/services/bulk_imports/relation_export_service.rb +++ b/app/services/bulk_imports/relation_export_service.rb @@ -59,7 +59,7 @@ module BulkImports end def export_service - @export_service ||= if config.tree_relation?(relation) + @export_service ||= if config.tree_relation?(relation) || config.self_relation?(relation) TreeExportService.new(portable, config.export_path, relation) elsif config.file_relation?(relation) FileExportService.new(portable, config.export_path, relation) diff --git a/app/services/bulk_imports/tree_export_service.rb b/app/services/bulk_imports/tree_export_service.rb index b8e7ac4574b..8e885e590d1 100644 --- a/app/services/bulk_imports/tree_export_service.rb +++ b/app/services/bulk_imports/tree_export_service.rb @@ -10,6 +10,8 @@ module BulkImports end def execute + return serializer.serialize_root(config.class::SELF_RELATION) if self_relation? + relation_definition = config.tree_relation_definition_for(relation) raise BulkImports::Error, 'Unsupported relation export type' unless relation_definition @@ -18,6 +20,8 @@ module BulkImports end def exported_filename + return "#{relation}.json" if self_relation? + "#{relation}.ndjson" end @@ -39,5 +43,9 @@ module BulkImports def json_writer ::Gitlab::ImportExport::Json::NdjsonWriter.new(export_path) end + + def self_relation? + relation == config.class::SELF_RELATION + end end end diff --git a/config/feature_flags/development/admin_deploy_keys_vue.yml b/config/feature_flags/development/admin_deploy_keys_vue.yml index c57ed728ffb..21e1b501d7a 100644 --- a/config/feature_flags/development/admin_deploy_keys_vue.yml +++ b/config/feature_flags/development/admin_deploy_keys_vue.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/344855 milestone: '14.5' type: development group: group::access -default_enabled: false +default_enabled: true diff --git a/config/feature_flags/development/paginatable_namespace_drop_down_for_project_creation.yml b/config/feature_flags/development/paginatable_namespace_drop_down_for_project_creation.yml index 297a4f65aa4..f0f60d4d0b7 100644 --- a/config/feature_flags/development/paginatable_namespace_drop_down_for_project_creation.yml +++ b/config/feature_flags/development/paginatable_namespace_drop_down_for_project_creation.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/338930 milestone: '14.3' type: development group: group::project management -default_enabled: false +default_enabled: true diff --git a/db/migrate/20211130205719_add_uniqueness_for_evidence_occurrence_id.rb b/db/migrate/20211130205719_add_uniqueness_for_evidence_occurrence_id.rb new file mode 100644 index 00000000000..1aca3e7e8e2 --- /dev/null +++ b/db/migrate/20211130205719_add_uniqueness_for_evidence_occurrence_id.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class AddUniquenessForEvidenceOccurrenceId < Gitlab::Database::Migration[1.0] + disable_ddl_transaction! + + INDEX_NAME = 'finding_evidences_on_vulnerability_occurrence_id' + UNIQUE_INDEX_NAME = 'finding_evidences_on_unique_vulnerability_occurrence_id' + + def up + add_concurrent_index :vulnerability_finding_evidences, [:vulnerability_occurrence_id], unique: true, name: UNIQUE_INDEX_NAME + remove_concurrent_index :vulnerability_finding_evidences, [:vulnerability_occurrence_id], name: INDEX_NAME + end + + def down + add_concurrent_index :vulnerability_finding_evidences, [:vulnerability_occurrence_id], name: INDEX_NAME + remove_concurrent_index :vulnerability_finding_evidences, [:vulnerability_occurrence_id], name: UNIQUE_INDEX_NAME + end +end diff --git a/db/schema_migrations/20211130205719 b/db/schema_migrations/20211130205719 new file mode 100644 index 00000000000..aae23faabc9 --- /dev/null +++ b/db/schema_migrations/20211130205719 @@ -0,0 +1 @@ +567a80916756adcca93bdbe82d69a923e539aac74146e714b58a1b023134d2c9 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index e55eabd7783..260c84be257 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -25039,7 +25039,7 @@ CREATE INDEX finding_evidence_sources_on_finding_evidence_id ON vulnerability_fi CREATE INDEX finding_evidence_supporting_messages_on_finding_evidence_id ON vulnerability_finding_evidence_supporting_messages USING btree (vulnerability_finding_evidence_id); -CREATE INDEX finding_evidences_on_vulnerability_occurrence_id ON vulnerability_finding_evidences USING btree (vulnerability_occurrence_id); +CREATE UNIQUE INDEX finding_evidences_on_unique_vulnerability_occurrence_id ON vulnerability_finding_evidences USING btree (vulnerability_occurrence_id); CREATE INDEX finding_links_on_vulnerability_occurrence_id ON vulnerability_finding_links USING btree (vulnerability_occurrence_id); diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 4d869f198c0..7fa58f59717 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -398,6 +398,16 @@ four standard [pagination arguments](#connection-pagination-arguments): | `type` | [`TypeEnum`](#typeenum) | Type of snippet. | | `visibility` | [`VisibilityScopesEnum`](#visibilityscopesenum) | Visibility of the snippet. | +### `Query.subscriptionFutureEntries` + +Fields related to entries in future subscriptions. + +Returns [`SubscriptionFutureEntryConnection`](#subscriptionfutureentryconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, `last: Int`. + ### `Query.timelogs` Find timelogs visible to the current user. @@ -7566,6 +7576,29 @@ The edge type for [`Submodule`](#submodule). | `cursor` | [`String!`](#string) | A cursor for use in pagination. | | `node` | [`Submodule`](#submodule) | The item at the end of the edge. | +#### `SubscriptionFutureEntryConnection` + +The connection type for [`SubscriptionFutureEntry`](#subscriptionfutureentry). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `edges` | [`[SubscriptionFutureEntryEdge]`](#subscriptionfutureentryedge) | A list of edges. | +| `nodes` | [`[SubscriptionFutureEntry]`](#subscriptionfutureentry) | A list of nodes. | +| `pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. | + +#### `SubscriptionFutureEntryEdge` + +The edge type for [`SubscriptionFutureEntry`](#subscriptionfutureentry). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `cursor` | [`String!`](#string) | A cursor for use in pagination. | +| `node` | [`SubscriptionFutureEntry`](#subscriptionfutureentry) | The item at the end of the edge. | + #### `TerraformStateConnection` The connection type for [`TerraformState`](#terraformstate). @@ -14739,6 +14772,23 @@ Represents the Geo sync and verification state of a snippet repository. | `type` | [`EntryType!`](#entrytype) | Type of tree entry. | | `webUrl` | [`String`](#string) | Web URL for the sub-module. | +### `SubscriptionFutureEntry` + +Represents an entry from the future subscriptions. + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `company` | [`String`](#string) | Company of the licensee. | +| `email` | [`String`](#string) | Email of the licensee. | +| `expiresAt` | [`Date`](#date) | Date when the license expires. | +| `name` | [`String`](#string) | Name of the licensee. | +| `plan` | [`String!`](#string) | Name of the subscription plan. | +| `startsAt` | [`Date`](#date) | Date when the license started. | +| `type` | [`String!`](#string) | Type of license the subscription will yield. | +| `usersInLicenseCount` | [`Int`](#int) | Number of paid user seats. | + ### `TaskCompletionStatus` Completion status of tasks. diff --git a/doc/api/settings.md b/doc/api/settings.md index 1a3ea6b1fcf..90f6ca1ec11 100644 --- a/doc/api/settings.md +++ b/doc/api/settings.md @@ -405,9 +405,9 @@ listed in the descriptions of the relevant settings. | `sourcegraph_enabled` | boolean | no | Enables Sourcegraph integration. Default is `false`. **If enabled, requires** `sourcegraph_url`. | | `sourcegraph_public_only` | boolean | no | Blocks Sourcegraph from being loaded on private and internal projects. Default is `true`. | | `sourcegraph_url` | string | required by: `sourcegraph_enabled` | The Sourcegraph instance URL for integration. | -| `spam_check_endpoint_enabled` | boolean | no | Enables Spam Check via external API endpoint. Default is `false`. | -| `spam_check_endpoint_url` | string | no | URL of the external Spam Check service endpoint. | -| `spam_check_api_key` | string | no | The API key used by GitLab for accessing the Spam Check service endpoint. | +| `spam_check_endpoint_enabled` | boolean | no | Enables spam checking using external Spam Check API endpoint. Default is `false`. | +| `spam_check_endpoint_url` | string | no | URL of the external Spamcheck service endpoint. Valid URI schemes are `grpc` or `tls`. Specifying `tls` forces communication to be encrypted.| +| `spam_check_api_key` | string | no | API key used by GitLab for accessing the Spam Check service endpoint. | | `suggest_pipeline_enabled` | boolean | no | Enable pipeline suggestion banner. | | `terminal_max_session_time` | integer | no | Maximum time for web terminal websocket connection (in seconds). Set to `0` for unlimited time. | | `terms` | text | required by: `enforce_terms` | (**Required by:** `enforce_terms`) Markdown content for the ToS. | diff --git a/doc/user/packages/dependency_proxy/index.md b/doc/user/packages/dependency_proxy/index.md index 1e248b710c8..8b34634318c 100644 --- a/doc/user/packages/dependency_proxy/index.md +++ b/doc/user/packages/dependency_proxy/index.md @@ -204,7 +204,7 @@ on the GitLab server. The next time you pull the same image, GitLab gets the lat information about the image from Docker Hub, but serves the existing blobs from the GitLab server. -## Clear the Dependency Proxy cache +## Reduce storage usage Blobs are kept forever on the GitLab server, and there is no hard limit on how much data can be stored. @@ -219,6 +219,16 @@ If you clear the cache, the next time a pipeline runs it must pull an image or t ### Cleanup policies +#### Enable cleanup policies from within GitLab + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/340777) in GitLab 14.6 + +You can enable an automatic time-to-live (TTL) policy for the Dependency Proxy from the user +interface. To do this, navigate to your group's **Settings > Packages & Registries > Dependency Proxy** +and enable the setting to automatically clear items from the cache after 90 days. + +#### Enable cleanup policies with GraphQL + > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/294187) in GitLab 14.4. The cleanup policy is a scheduled job you can use to clear cached images that are no longer used, @@ -249,8 +259,7 @@ mutation { ``` See the [Getting started with GraphQL](../../../api/graphql/getting_started.md) -guide to learn how to make GraphQL queries. Support for enabling and configuring cleanup policies in -the UI is tracked in [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/340777). +guide to learn how to make GraphQL queries. When the policy is initially enabled, the default TTL setting is 90 days. Once enabled, stale dependency proxy files are queued for deletion each day. Deletion may not occur right away due to diff --git a/lib/bulk_imports/projects/pipelines/project_attributes_pipeline.rb b/lib/bulk_imports/projects/pipelines/project_attributes_pipeline.rb new file mode 100644 index 00000000000..4d742225ff7 --- /dev/null +++ b/lib/bulk_imports/projects/pipelines/project_attributes_pipeline.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +module BulkImports + module Projects + module Pipelines + class ProjectAttributesPipeline + include Pipeline + + transformer ::BulkImports::Common::Transformers::ProhibitedAttributesTransformer + + def extract(context) + download_service(tmp_dir, context).execute + decompression_service(tmp_dir).execute + project_attributes = json_decode(json_attributes) + + BulkImports::Pipeline::ExtractedData.new(data: project_attributes) + end + + def transform(_, data) + subrelations = config.portable_relations_tree.keys.map(&:to_s) + + Gitlab::ImportExport::AttributeCleaner.clean( + relation_hash: data, + relation_class: Project, + excluded_keys: config.relation_excluded_keys(:project) + ).except(*subrelations) + end + + def load(_, data) + portable.assign_attributes(data) + portable.reconcile_shared_runners_setting! + portable.drop_visibility_level! + portable.save! + end + + def after_run(_) + FileUtils.remove_entry(tmp_dir) + end + + def json_attributes + @json_attributes ||= File.read(File.join(tmp_dir, filename)) + end + + private + + def tmp_dir + @tmp_dir ||= Dir.mktmpdir + end + + def config + @config ||= BulkImports::FileTransfer.config_for(portable) + end + + def download_service(tmp_dir, context) + @download_service ||= BulkImports::FileDownloadService.new( + configuration: context.configuration, + relative_url: context.entity.relation_download_url_path(BulkImports::FileTransfer::BaseConfig::SELF_RELATION), + dir: tmp_dir, + filename: compressed_filename + ) + end + + def decompression_service(tmp_dir) + @decompression_service ||= BulkImports::FileDecompressionService.new(dir: tmp_dir, filename: compressed_filename) + end + + def compressed_filename + "#{filename}.gz" + end + + def filename + "#{BulkImports::FileTransfer::BaseConfig::SELF_RELATION}.json" + end + + def json_decode(string) + Gitlab::Json.parse(string) + rescue JSON::ParserError => e + Gitlab::ErrorTracking.log_exception(e) + + raise BulkImports::Error, 'Incorrect JSON format' + end + end + end + end +end diff --git a/lib/bulk_imports/projects/stage.rb b/lib/bulk_imports/projects/stage.rb index 61ecb46220e..1771503672a 100644 --- a/lib/bulk_imports/projects/stage.rb +++ b/lib/bulk_imports/projects/stage.rb @@ -15,6 +15,10 @@ module BulkImports pipeline: BulkImports::Projects::Pipelines::RepositoryPipeline, stage: 1 }, + project_attributes: { + pipeline: BulkImports::Projects::Pipelines::ProjectAttributesPipeline, + stage: 1 + }, labels: { pipeline: BulkImports::Common::Pipelines::LabelsPipeline, stage: 2 diff --git a/lib/gitlab/import_export/json/streaming_serializer.rb b/lib/gitlab/import_export/json/streaming_serializer.rb index 9ab8fa68d0e..fb8d6e7d89b 100644 --- a/lib/gitlab/import_export/json/streaming_serializer.rb +++ b/lib/gitlab/import_export/json/streaming_serializer.rb @@ -40,6 +40,13 @@ module Gitlab end end + def serialize_root(exportable_path = @exportable_path) + attributes = exportable.as_json( + relations_schema.merge(include: nil, preloads: nil)) + + json_writer.write_attributes(exportable_path, attributes) + end + def serialize_relation(definition) raise ArgumentError, 'definition needs to be Hash' unless definition.is_a?(Hash) raise ArgumentError, 'definition needs to have exactly one Hash element' unless definition.one? @@ -60,12 +67,6 @@ module Gitlab attr_reader :json_writer, :relations_schema, :exportable - def serialize_root - attributes = exportable.as_json( - relations_schema.merge(include: nil, preloads: nil)) - json_writer.write_attributes(@exportable_path, attributes) - end - def serialize_many_relations(key, records, options) enumerator = Enumerator.new do |items| key_preloads = preloads&.dig(key) diff --git a/lib/gitlab/spamcheck/client.rb b/lib/gitlab/spamcheck/client.rb index 925ca44dfc9..e7dce91ae08 100644 --- a/lib/gitlab/spamcheck/client.rb +++ b/lib/gitlab/spamcheck/client.rb @@ -21,14 +21,16 @@ module Gitlab update: ::Spamcheck::Action::UPDATE }.freeze + URL_SCHEME_REGEX = %r{^grpc://|^tls://}.freeze + def initialize @endpoint_url = Gitlab::CurrentSettings.current_application_settings.spam_check_endpoint_url - # remove the `grpc://` as it's only useful to ensure we're expecting to - # connect with Spamcheck - @endpoint_url = @endpoint_url.gsub(%r(^grpc:\/\/), '') + @creds = client_creds(@endpoint_url) - @creds = stub_creds + # remove the `grpc://` or 'tls://' as it's only useful to ensure we're expecting to + # connect with Spamcheck + @endpoint_url = @endpoint_url.sub(URL_SCHEME_REGEX, '') end def issue_spam?(spam_issue:, user:, context: {}) @@ -96,11 +98,11 @@ module Gitlab nanos: ar_timestamp.to_time.nsec) end - def stub_creds - if Rails.env.development? || Rails.env.test? - :this_channel_is_insecure + def client_creds(url) + if URI(url).scheme == 'tls' || Rails.env.production? + GRPC::Core::ChannelCredentials.new(::Gitlab::X509::Certificate.ca_certs_bundle) else - GRPC::Core::ChannelCredentials.new ::Gitlab::X509::Certificate.ca_certs_bundle + :this_channel_is_insecure end end diff --git a/spec/lib/bulk_imports/projects/pipelines/project_attributes_pipeline_spec.rb b/spec/lib/bulk_imports/projects/pipelines/project_attributes_pipeline_spec.rb new file mode 100644 index 00000000000..11c475318bb --- /dev/null +++ b/spec/lib/bulk_imports/projects/pipelines/project_attributes_pipeline_spec.rb @@ -0,0 +1,159 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe BulkImports::Projects::Pipelines::ProjectAttributesPipeline do + let_it_be(:project) { create(:project) } + let_it_be(:bulk_import) { create(:bulk_import) } + let_it_be(:entity) { create(:bulk_import_entity, :project_entity, project: project, bulk_import: bulk_import) } + let_it_be(:tracker) { create(:bulk_import_tracker, entity: entity) } + let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) } + + let(:tmpdir) { Dir.mktmpdir } + let(:extra) { {} } + let(:project_attributes) do + { + 'description' => 'description', + 'visibility_level' => 0, + 'archived' => false, + 'merge_requests_template' => 'test', + 'merge_requests_rebase_enabled' => true, + 'approvals_before_merge' => 0, + 'reset_approvals_on_push' => true, + 'merge_requests_ff_only_enabled' => true, + 'issues_template' => 'test', + 'shared_runners_enabled' => true, + 'build_coverage_regex' => 'build_coverage_regex', + 'build_allow_git_fetch' => true, + 'build_timeout' => 3600, + 'pending_delete' => false, + 'public_builds' => true, + 'last_repository_check_failed' => nil, + 'only_allow_merge_if_pipeline_succeeds' => true, + 'has_external_issue_tracker' => false, + 'request_access_enabled' => true, + 'has_external_wiki' => false, + 'ci_config_path' => nil, + 'only_allow_merge_if_all_discussions_are_resolved' => true, + 'printing_merge_request_link_enabled' => true, + 'auto_cancel_pending_pipelines' => 'enabled', + 'service_desk_enabled' => false, + 'delete_error' => nil, + 'disable_overriding_approvers_per_merge_request' => true, + 'resolve_outdated_diff_discussions' => true, + 'jobs_cache_index' => nil, + 'external_authorization_classification_label' => nil, + 'pages_https_only' => false, + 'merge_requests_author_approval' => false, + 'merge_requests_disable_committers_approval' => true, + 'require_password_to_approve' => true, + 'remove_source_branch_after_merge' => true, + 'autoclose_referenced_issues' => true, + 'suggestion_commit_message' => 'Test!' + }.merge(extra) + end + + subject(:pipeline) { described_class.new(context) } + + before do + allow(Dir).to receive(:mktmpdir).and_return(tmpdir) + end + + after do + FileUtils.remove_entry(tmpdir) if Dir.exist?(tmpdir) + end + + describe '#run' do + before do + allow(pipeline).to receive(:extract).and_return(BulkImports::Pipeline::ExtractedData.new(data: project_attributes)) + + pipeline.run + end + + it 'imports project attributes', :aggregate_failures do + project_attributes.each_pair do |key, value| + expect(project.public_send(key)).to eq(value) + end + end + + context 'when project is archived' do + let(:extra) { { 'archived' => true } } + + it 'sets project as archived' do + expect(project.archived).to eq(true) + end + end + end + + describe '#extract' do + before do + file_download_service = instance_double("BulkImports::FileDownloadService") + file_decompression_service = instance_double("BulkImports::FileDecompressionService") + + expect(BulkImports::FileDownloadService) + .to receive(:new) + .with( + configuration: context.configuration, + relative_url: "/#{entity.pluralized_name}/#{entity.source_full_path}/export_relations/download?relation=self", + dir: tmpdir, + filename: 'self.json.gz') + .and_return(file_download_service) + + expect(BulkImports::FileDecompressionService) + .to receive(:new) + .with(dir: tmpdir, filename: 'self.json.gz') + .and_return(file_decompression_service) + + expect(file_download_service).to receive(:execute) + expect(file_decompression_service).to receive(:execute) + end + + it 'downloads, decompresses & decodes json' do + allow(pipeline).to receive(:json_attributes).and_return("{\"test\":\"test\"}") + + extracted_data = pipeline.extract(context) + + expect(extracted_data.data).to match_array([{ 'test' => 'test' }]) + end + + context 'when json parsing error occurs' do + it 'raises an error' do + allow(pipeline).to receive(:json_attributes).and_return("invalid") + + expect { pipeline.extract(context) }.to raise_error(BulkImports::Error) + end + end + end + + describe '#transform' do + it 'removes prohibited attributes from hash' do + input = { 'description' => 'description', 'issues' => [], 'milestones' => [], 'id' => 5 } + + expect(Gitlab::ImportExport::AttributeCleaner).to receive(:clean).and_call_original + + expect(pipeline.transform(context, input)).to eq({ 'description' => 'description' }) + end + end + + describe '#load' do + it 'assigns attributes, drops visibility and reconciles shared runner setting' do + expect(project).to receive(:assign_attributes).with(project_attributes) + expect(project).to receive(:reconcile_shared_runners_setting!) + expect(project).to receive(:drop_visibility_level!) + expect(project).to receive(:save!) + + pipeline.load(context, project_attributes) + end + end + + describe '#json_attributes' do + it 'reads raw json from file' do + filepath = File.join(tmpdir, 'self.json') + + FileUtils.touch(filepath) + expect_file_read(filepath) + + pipeline.json_attributes + end + end +end diff --git a/spec/lib/bulk_imports/projects/stage_spec.rb b/spec/lib/bulk_imports/projects/stage_spec.rb index 62f941806c8..fd3280d0f99 100644 --- a/spec/lib/bulk_imports/projects/stage_spec.rb +++ b/spec/lib/bulk_imports/projects/stage_spec.rb @@ -9,6 +9,7 @@ RSpec.describe BulkImports::Projects::Stage do [ [0, BulkImports::Projects::Pipelines::ProjectPipeline], [1, BulkImports::Projects::Pipelines::RepositoryPipeline], + [1, BulkImports::Projects::Pipelines::ProjectAttributesPipeline], [2, BulkImports::Common::Pipelines::LabelsPipeline], [2, BulkImports::Common::Pipelines::MilestonesPipeline], [2, BulkImports::Common::Pipelines::BadgesPipeline], diff --git a/spec/lib/gitlab/spamcheck/client_spec.rb b/spec/lib/gitlab/spamcheck/client_spec.rb index e542ce455bb..0c392bf0b9d 100644 --- a/spec/lib/gitlab/spamcheck/client_spec.rb +++ b/spec/lib/gitlab/spamcheck/client_spec.rb @@ -32,6 +32,60 @@ RSpec.describe Gitlab::Spamcheck::Client do stub_application_setting(spam_check_endpoint_url: endpoint) end + describe 'url scheme' do + let(:stub) { double(:spamcheck_stub, check_for_spam_issue: response) } + + context 'is tls ' do + let(:endpoint) { 'tls://spamcheck.example.com'} + + it 'uses secure connection' do + expect(Spamcheck::SpamcheckService::Stub).to receive(:new).with(endpoint.sub(%r{^tls://}, ''), + instance_of(GRPC::Core::ChannelCredentials), + anything).and_return(stub) + subject + end + end + + context 'is grpc' do + it 'uses insecure connection' do + expect(Spamcheck::SpamcheckService::Stub).to receive(:new).with(endpoint.sub(%r{^grpc://}, ''), + :this_channel_is_insecure, + anything).and_return(stub) + subject + end + end + end + + describe "Rails environment" do + let(:stub) { double(:spamcheck_stub, check_for_spam_issue: response) } + + context "production" do + before do + allow(Rails.env).to receive(:production?).and_return(true) + end + + it 'uses secure connection' do + expect(Spamcheck::SpamcheckService::Stub).to receive(:new).with(endpoint.sub(%r{^grpc://}, ''), + instance_of(GRPC::Core::ChannelCredentials), + anything).and_return(stub) + subject + end + end + + context "not production" do + before do + allow(Rails.env).to receive(:production?).and_return(false) + end + + it 'uses insecure connection' do + expect(Spamcheck::SpamcheckService::Stub).to receive(:new).with(endpoint.sub(%r{^grpc://}, ''), + :this_channel_is_insecure, + anything).and_return(stub) + subject + end + end + end + describe '#issue_spam?' do before do allow_next_instance_of(::Spamcheck::SpamcheckService::Stub) do |instance| diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index 8f6e94cc46e..67314084c4f 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -247,6 +247,7 @@ RSpec.describe ApplicationSetting do end it { is_expected.to allow_value('grpc://example.org/spam_check').for(:spam_check_endpoint_url) } + it { is_expected.to allow_value('tls://example.org/spam_check').for(:spam_check_endpoint_url) } it { is_expected.not_to allow_value('https://example.org/spam_check').for(:spam_check_endpoint_url) } it { is_expected.not_to allow_value('nonsense').for(:spam_check_endpoint_url) } it { is_expected.not_to allow_value(nil).for(:spam_check_endpoint_url) } @@ -259,6 +260,7 @@ RSpec.describe ApplicationSetting do end it { is_expected.to allow_value('grpc://example.org/spam_check').for(:spam_check_endpoint_url) } + it { is_expected.to allow_value('tls://example.org/spam_check').for(:spam_check_endpoint_url) } it { is_expected.not_to allow_value('https://example.org/spam_check').for(:spam_check_endpoint_url) } it { is_expected.not_to allow_value('nonsense').for(:spam_check_endpoint_url) } it { is_expected.to allow_value(nil).for(:spam_check_endpoint_url) } diff --git a/spec/services/bulk_imports/tree_export_service_spec.rb b/spec/services/bulk_imports/tree_export_service_spec.rb index f2ed747b64e..ffb81fe2b5f 100644 --- a/spec/services/bulk_imports/tree_export_service_spec.rb +++ b/spec/services/bulk_imports/tree_export_service_spec.rb @@ -5,7 +5,8 @@ require 'spec_helper' RSpec.describe BulkImports::TreeExportService do let_it_be(:project) { create(:project) } let_it_be(:export_path) { Dir.mktmpdir } - let_it_be(:relation) { 'issues' } + + let(:relation) { 'issues' } subject(:service) { described_class.new(project, export_path, relation) } @@ -25,11 +26,31 @@ RSpec.describe BulkImports::TreeExportService do expect { service.execute }.to raise_error(BulkImports::Error, 'Unsupported relation export type') end end + + context 'when relation is self' do + let(:relation) { 'self' } + + it 'executes export on portable itself' do + expect_next_instance_of(Gitlab::ImportExport::Json::StreamingSerializer) do |serializer| + expect(serializer).to receive(:serialize_root) + end + + subject.execute + end + end end describe '#exported_filename' do it 'returns filename of the exported file' do expect(subject.exported_filename).to eq('issues.ndjson') end + + context 'when relation is self' do + let(:relation) { 'self' } + + it 'returns filename of the exported file' do + expect(subject.exported_filename).to eq('self.json') + end + end end end