Commit b232a5b0 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 5d1839b2 cfdb739b
# frozen_string_literal: true
class DependencyProxy::Manifest < ApplicationRecord
include FileStoreMounter
belongs_to :group
validates :group, presence: true
validates :file, presence: true
validates :file_name, presence: true
validates :digest, presence: true
mount_file_store_uploader DependencyProxy::FileUploader
end
---
title: Add dependency_proxy_manifests table and associations
merge_request: 48535
author:
type: added
# frozen_string_literal: true
class CreateDependencyProxyManifests < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
with_lock_retries do
create_table :dependency_proxy_manifests, if_not_exists: true do |t|
t.timestamps_with_timezone
t.references :group, index: false, null: false, foreign_key: { to_table: :namespaces, on_delete: :cascade }, type: :bigint
t.bigint :size
t.integer :file_store, limit: 2
t.text :file_name, null: false
t.text :file, null: false
t.text :digest, null: false
t.index [:group_id, :digest], name: 'index_dependency_proxy_manifests_on_group_id_and_digest'
end
end
add_text_limit :dependency_proxy_manifests, :file_name, 255
add_text_limit :dependency_proxy_manifests, :file, 255
add_text_limit :dependency_proxy_manifests, :digest, 255
end
def down
drop_table :dependency_proxy_manifests
end
end
e19c6d019f1478e5998b2a264c5327dc82da7fde7edd19b15da70a30c5779844
\ No newline at end of file
......@@ -11617,6 +11617,30 @@ CREATE SEQUENCE dependency_proxy_group_settings_id_seq
ALTER SEQUENCE dependency_proxy_group_settings_id_seq OWNED BY dependency_proxy_group_settings.id;
CREATE TABLE dependency_proxy_manifests (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
group_id bigint NOT NULL,
size bigint,
file_store smallint,
file_name text NOT NULL,
file text NOT NULL,
digest text NOT NULL,
CONSTRAINT check_079b293a7b CHECK ((char_length(file) <= 255)),
CONSTRAINT check_c579e3f586 CHECK ((char_length(file_name) <= 255)),
CONSTRAINT check_f5d9996bf1 CHECK ((char_length(digest) <= 255))
);
CREATE SEQUENCE dependency_proxy_manifests_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE dependency_proxy_manifests_id_seq OWNED BY dependency_proxy_manifests.id;
CREATE TABLE deploy_keys_projects (
id integer NOT NULL,
deploy_key_id integer NOT NULL,
......@@ -18014,6 +18038,8 @@ ALTER TABLE ONLY dependency_proxy_blobs ALTER COLUMN id SET DEFAULT nextval('dep
ALTER TABLE ONLY dependency_proxy_group_settings ALTER COLUMN id SET DEFAULT nextval('dependency_proxy_group_settings_id_seq'::regclass);
ALTER TABLE ONLY dependency_proxy_manifests ALTER COLUMN id SET DEFAULT nextval('dependency_proxy_manifests_id_seq'::regclass);
ALTER TABLE ONLY deploy_keys_projects ALTER COLUMN id SET DEFAULT nextval('deploy_keys_projects_id_seq'::regclass);
ALTER TABLE ONLY deploy_tokens ALTER COLUMN id SET DEFAULT nextval('deploy_tokens_id_seq'::regclass);
......@@ -19136,6 +19162,9 @@ ALTER TABLE ONLY dependency_proxy_blobs
ALTER TABLE ONLY dependency_proxy_group_settings
ADD CONSTRAINT dependency_proxy_group_settings_pkey PRIMARY KEY (id);
ALTER TABLE ONLY dependency_proxy_manifests
ADD CONSTRAINT dependency_proxy_manifests_pkey PRIMARY KEY (id);
ALTER TABLE ONLY deploy_keys_projects
ADD CONSTRAINT deploy_keys_projects_pkey PRIMARY KEY (id);
......@@ -20876,6 +20905,8 @@ CREATE INDEX index_dependency_proxy_blobs_on_group_id_and_file_name ON dependenc
CREATE INDEX index_dependency_proxy_group_settings_on_group_id ON dependency_proxy_group_settings USING btree (group_id);
CREATE INDEX index_dependency_proxy_manifests_on_group_id_and_digest ON dependency_proxy_manifests USING btree (group_id, digest);
CREATE INDEX index_deploy_key_id_on_protected_branch_push_access_levels ON protected_branch_push_access_levels USING btree (deploy_key_id);
CREATE INDEX index_deploy_keys_projects_on_deploy_key_id ON deploy_keys_projects USING btree (deploy_key_id);
......@@ -24474,6 +24505,9 @@ ALTER TABLE ONLY user_permission_export_uploads
ALTER TABLE ONLY repository_languages
ADD CONSTRAINT fk_rails_a750ec87a8 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY dependency_proxy_manifests
ADD CONSTRAINT fk_rails_a758021fb0 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY resource_milestone_events
ADD CONSTRAINT fk_rails_a788026e85 FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE;
......
......@@ -23,8 +23,7 @@ module BillingPlansHelper
end
def use_new_purchase_flow?(namespace)
namespace.group? &&
namespace.actual_plan_name == Plan::FREE
namespace.group? && (namespace.actual_plan_name == Plan::FREE || namespace.trial_active?)
end
def show_contact_sales_button?(purchase_link_action)
......
......@@ -39,6 +39,22 @@ module EE
scope :include_gitlab_subscription_with_hosted_plan, -> { includes(gitlab_subscription: :hosted_plan) }
scope :join_gitlab_subscription, -> { joins("LEFT OUTER JOIN gitlab_subscriptions ON gitlab_subscriptions.namespace_id=namespaces.id") }
scope :top_most, -> { where(parent_id: nil) }
scope :in_active_trial, -> do
left_joins(gitlab_subscription: :hosted_plan)
.where(gitlab_subscriptions: { trial: true, trial_ends_on: Date.today.. })
end
scope :in_default_plan, -> do
left_joins(gitlab_subscription: :hosted_plan)
.where(plans: { name: [nil, *::Plan.default_plans] })
end
scope :eligible_for_subscription, -> do
top_most.in_active_trial.or(top_most.in_default_plan)
end
scope :eligible_for_trial, -> do
left_joins(gitlab_subscription: :hosted_plan)
.where(
......
......@@ -256,11 +256,7 @@ module EE
end
def manageable_groups_eligible_for_subscription
manageable_groups
.where(parent_id: nil)
.left_joins(:gitlab_subscription)
.merge(GitlabSubscription.left_joins(:hosted_plan).where(plans: { name: [nil, *::Plan.default_plans] }))
.order(:name)
manageable_groups.eligible_for_subscription.order(:name)
end
def manageable_groups_eligible_for_trial
......
---
title: New subscription purchase for trial namespaces follow new flow
merge_request: 47880
author:
type: changed
......@@ -200,14 +200,6 @@ module EE
ldap_keys: count(::LDAPKey),
ldap_users: count(::User.ldap, 'users.id'),
pod_logs_usages_total: redis_usage_data { ::Gitlab::UsageCounters::PodLogs.usage_totals[:total] },
projects_enforcing_code_owner_approval: count(::Project.without_deleted.non_archived.requiring_code_owner_approval),
merge_requests_with_added_rules: distinct_count(::ApprovalMergeRequestRule.with_added_approval_rules,
:merge_request_id,
start: approval_merge_request_rule_minimum_id,
finish: approval_merge_request_rule_maximum_id),
merge_requests_with_optional_codeowners: distinct_count(::ApprovalMergeRequestRule.code_owner_approval_optional, :merge_request_id),
merge_requests_with_overridden_project_rules: merge_requests_with_overridden_project_rules,
merge_requests_with_required_codeowners: distinct_count(::ApprovalMergeRequestRule.code_owner_approval_required, :merge_request_id),
merged_merge_requests_using_approval_rules: count(::MergeRequest.merged.joins(:approval_rules), # rubocop: disable CodeReuse/ActiveRecord
start: merge_request_minimum_id,
finish: merge_request_maximum_id),
......
......@@ -57,7 +57,8 @@ RSpec.describe BillingPlansHelper do
describe '#use_new_purchase_flow?' do
where type: ['Group', nil],
plan: Plan.all_plans
plan: Plan.all_plans,
trial_active: [true, false]
with_them do
let_it_be(:user) { create(:user) }
......@@ -68,12 +69,13 @@ RSpec.describe BillingPlansHelper do
before do
allow(helper).to receive(:current_user).and_return(user)
allow(namespace).to receive(:trial_active?).and_return(trial_active)
end
subject { helper.use_new_purchase_flow?(namespace) }
it do
result = type == 'Group' && plan == Plan::FREE
result = type == 'Group' && (plan == Plan::FREE || trial_active)
is_expected.to be(result)
end
......
......@@ -219,24 +219,6 @@ RSpec.describe Gitlab::UsageData do
end
end
describe 'code owner approval required' do
before do
create(:protected_branch, code_owner_approval_required: true)
create(:protected_branch,
code_owner_approval_required: true,
project: create(:project, :archived))
create(:protected_branch,
code_owner_approval_required: true,
project: create(:project, pending_delete: true))
end
it 'counts the projects actively requiring code owner approval' do
expect(described_class.system_usage_data[:counts][:projects_enforcing_code_owner_approval]).to eq(1)
end
end
describe 'merge requests merged using approval rules' do
before do
create(:approval_merge_request_rule, merge_request: create(:merge_request, :merged))
......
......@@ -171,6 +171,130 @@ RSpec.describe Namespace do
end
end
describe '.top_most' do
let_it_be(:namespace) { create(:namespace) }
let_it_be(:sub_namespace) { create(:namespace, parent: namespace) }
subject { described_class.top_most.ids }
it 'only contains root namespace' do
is_expected.to eq([namespace.id])
end
end
describe '.in_active_trial' do
let_it_be(:namespaces) do
[
create(:namespace),
create(:namespace_with_plan),
create(:namespace_with_plan, trial_ends_on: Date.tomorrow)
]
end
it 'is consistent to trial_active? method' do
namespaces.each do |ns|
consistent = described_class.in_active_trial.include?(ns) == !!ns.trial_active?
expect(consistent).to be true
end
end
end
describe '.in_default_plan' do
subject { described_class.in_default_plan.ids }
where(:plan_name, :expect_in_default_plan) do
::Plan::FREE | true
::Plan::DEFAULT | true
::Plan::BRONZE | false
::Plan::SILVER | false
::Plan::GOLD | false
end
with_them do
it 'returns expected result' do
namespace = create(:namespace_with_plan, plan: "#{plan_name}_plan")
is_expected.to eq(expect_in_default_plan ? [namespace.id] : [])
end
end
it 'includes namespace with no subscription' do
namespace = create(:namespace)
is_expected.to eq([namespace.id])
end
end
describe '.eligible_for_subscription' do
let_it_be(:namespace) { create :namespace }
let_it_be(:sub_namespace) { create(:namespace, parent: namespace) }
subject { described_class.eligible_for_subscription.ids }
context 'when there is no subscription' do
it { is_expected.to eq([namespace.id]) }
end
context 'when there is a subscription' do
context 'with a plan that is eligible for a trial' do
where(plan: ::Plan::PLANS_ELIGIBLE_FOR_TRIAL)
with_them do
context 'and has not yet been trialed' do
before do
create :gitlab_subscription, plan, namespace: namespace
create :gitlab_subscription, plan, namespace: sub_namespace
end
it { is_expected.to eq([namespace.id]) }
end
context 'but has already had a trial' do
before do
create :gitlab_subscription, plan, :expired_trial, namespace: namespace
create :gitlab_subscription, plan, :expired_trial, namespace: sub_namespace
end
it { is_expected.to eq([namespace.id]) }
end
context 'but is currently being trialed' do
before do
create :gitlab_subscription, plan, :active_trial, namespace: namespace
create :gitlab_subscription, plan, :active_trial, namespace: sub_namespace
end
it { is_expected.to eq([namespace.id]) }
end
end
end
context 'in active trial gold plan' do
before do
create :gitlab_subscription, ::Plan::GOLD, :active_trial, namespace: namespace
create :gitlab_subscription, ::Plan::GOLD, :active_trial, namespace: sub_namespace
end
it { is_expected.to eq([namespace.id]) }
end
context 'with a paid plan and not in trial' do
where(plan: ::Plan::PAID_HOSTED_PLANS)
with_them do
context 'and has not yet been trialed' do
before do
create :gitlab_subscription, plan, namespace: namespace
end
it { is_expected.to be_empty }
end
end
end
end
end
describe '.eligible_for_trial' do
let_it_be(:namespace) { create :namespace }
......
......@@ -1019,6 +1019,7 @@ RSpec.describe User do
let_it_be(:free_group_z) { create(:group, name: 'AZ', gitlab_subscription: create(:gitlab_subscription, :free)) }
let_it_be(:free_group_a) { create(:group, name: 'AA', gitlab_subscription: create(:gitlab_subscription, :free)) }
let_it_be(:sub_group) { create(:group, name: 'SubGroup', parent: free_group_a) }
let_it_be(:trial_group) { create(:group, name: 'AB', gitlab_subscription: create(:gitlab_subscription, :active_trial, :gold)) }
subject { user.manageable_groups_eligible_for_subscription }
......@@ -1068,6 +1069,30 @@ RSpec.describe User do
it { is_expected.not_to include(sub_group) }
end
context 'developer of a trial group' do
before do
trial_group.add_developer(user)
end
it { is_expected.not_to include(trial_group) }
end
context 'owner of a trial group' do
before do
trial_group.add_owner(user)
end
it { is_expected.to include(trial_group) }
end
context 'maintainer of a trial group' do
before do
trial_group.add_maintainer(user)
end
it { is_expected.to include(trial_group) }
end
end
describe '#manageable_groups_eligible_for_trial' do
......
......@@ -6,4 +6,11 @@ FactoryBot.define do
file { fixture_file_upload('spec/fixtures/dependency_proxy/a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4.gz') }
file_name { 'a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4.gz' }
end
factory :dependency_proxy_manifest, class: 'DependencyProxy::Manifest' do
group
file { fixture_file_upload('spec/fixtures/dependency_proxy/manifest') }
digest { 'sha256:5ab5a6872b264fe4fd35d63991b9b7d8425f4bc79e7cf4d563c10956581170c9' }
file_name { 'manifest' }
end
end
{
"schemaVersion": 1,
"name": "library/alpine",
"tag": "latest",
"architecture": "amd64",
"fsLayers": [
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:188c0c94c7c576fff0792aca7ec73d67a2f7f4cb3a6e53a84559337260b36964"
}
],
"history": [
{
"v1Compatibility": "{\"architecture\":\"amd64\",\"config\":{\"Hostname\":\"\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/bin/sh\"],\"ArgsEscaped\":true,\"Image\":\"sha256:3543079adc6fb5170279692361be8b24e89ef1809a374c1b4429e1d560d1459c\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":null,\"Labels\":null},\"container\":\"8c59eb170e19b8c3768b8d06c91053b0debf4a6fa6a452df394145fe9b885ea5\",\"container_config\":{\"Hostname\":\"8c59eb170e19\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/bin/sh\",\"-c\",\"#(nop) \",\"CMD [\\\"/bin/sh\\\"]\"],\"ArgsEscaped\":true,\"Image\":\"sha256:3543079adc6fb5170279692361be8b24e89ef1809a374c1b4429e1d560d1459c\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":null,\"Labels\":{}},\"created\":\"2020-10-22T02:19:24.499382102Z\",\"docker_version\":\"18.09.7\",\"id\":\"c5f1aab5bb88eaf1aa62bea08ea6654547d43fd4d15b1a476c77e705dd5385ba\",\"os\":\"linux\",\"parent\":\"dc0b50cc52bc340d7848a62cfe8a756f4420592f4984f7a680ef8f9d258176ed\",\"throwaway\":true}"
},
{
"v1Compatibility": "{\"id\":\"dc0b50cc52bc340d7848a62cfe8a756f4420592f4984f7a680ef8f9d258176ed\",\"created\":\"2020-10-22T02:19:24.33416307Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ADD file:f17f65714f703db9012f00e5ec98d0b2541ff6147c2633f7ab9ba659d0c507f4 in / \"]}}"
}
],
"signatures": [
{
"header": {
"jwk": {
"crv": "P-256",
"kid": "XOTE:DZ4C:YBPJ:3O3L:YI4B:NYXU:T4VR:USH6:CXXN:SELU:CSCC:FVPE",
"kty": "EC",
"x": "cR1zye_3354mdbD7Dn-mtXNXvtPtmLlUVDa5vH6Lp74",
"y": "rldUXSllLit6_2BW6AV8aqkwWJXHoYPG9OwkIBouwxQ"
},
"alg": "ES256"
},
"signature": "DYB2iB-XKIisqp5Q0OXFOBIOlBOuRV7pnZuKy0cxVB2Qj1VFRhWX4Tq336y0VMWbF6ma1he5A1E_Vk4jazrJ9g",
"protected": "eyJmb3JtYXRMZW5ndGgiOjIxMzcsImZvcm1hdFRhaWwiOiJDbjAiLCJ0aW1lIjoiMjAyMC0xMS0yNFQyMjowMTo1MVoifQ"
}
]
}
\ No newline at end of file
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe DependencyProxy::Manifest, type: :model do
describe 'relationships' do
it { is_expected.to belong_to(:group) }
end
describe 'validations' do
it { is_expected.to validate_presence_of(:group) }
it { is_expected.to validate_presence_of(:file) }
it { is_expected.to validate_presence_of(:file_name) }
it { is_expected.to validate_presence_of(:digest) }
end
describe 'file is being stored' do
subject { create(:dependency_proxy_manifest) }
context 'when existing object has local store' do
it_behaves_like 'mounted file in local store'
end
context 'when direct upload is enabled' do
before do
stub_dependency_proxy_object_storage(direct_upload: true)
end
it_behaves_like 'mounted file in object store'
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment