Commit 3c4b5ff9 authored by Mayra Cabrera's avatar Mayra Cabrera

Merge branch 'revert-5fa85f3e' into 'master'

Revert '202207-move-enum-modules-to-concerns' RUN AS-IF-FOSS

Closes #233025

See merge request gitlab-org/gitlab!38429
parents ab0ca7c7 bc73ee78
......@@ -103,15 +103,15 @@ module Ci
after_create :keep_around_commits, unless: :importing?
# We use `Enums::Ci::Pipeline.sources` here so that EE can more easily extend
# We use `Ci::PipelineEnums.sources` here so that EE can more easily extend
# this `Hash` with new values.
enum_with_nil source: Enums::Ci::Pipeline.sources
enum_with_nil source: ::Ci::PipelineEnums.sources
enum_with_nil config_source: Enums::Ci::Pipeline.config_sources
enum_with_nil config_source: ::Ci::PipelineEnums.config_sources
# We use `Enums::Ci::Pipeline.failure_reasons` here so that EE can more easily
# We use `Ci::PipelineEnums.failure_reasons` here so that EE can more easily
# extend this `Hash` with new values.
enum failure_reason: Enums::Ci::Pipeline.failure_reasons
enum failure_reason: ::Ci::PipelineEnums.failure_reasons
enum locked: { unlocked: 0, artifacts_locked: 1 }
......@@ -253,7 +253,7 @@ module Ci
scope :internal, -> { where(source: internal_sources) }
scope :no_child, -> { where.not(source: :parent_pipeline) }
scope :ci_sources, -> { where(config_source: Enums::Ci::Pipeline.ci_config_sources_values) }
scope :ci_sources, -> { where(config_source: ::Ci::PipelineEnums.ci_config_sources_values) }
scope :for_user, -> (user) { where(user: user) }
scope :for_sha, -> (sha) { where(sha: sha) }
scope :for_source_sha, -> (source_sha) { where(source_sha: source_sha) }
......@@ -1058,7 +1058,7 @@ module Ci
end
def cacheable?
Enums::Ci::Pipeline.ci_config_sources.key?(config_source.to_sym)
Ci::PipelineEnums.ci_config_sources.key?(config_source.to_sym)
end
def source_ref_path
......
# frozen_string_literal: true
module Ci
module PipelineEnums
# Returns the `Hash` to use for creating the `failure_reason` enum for
# `Ci::Pipeline`.
def self.failure_reasons
{
unknown_failure: 0,
config_error: 1,
external_validation_failure: 2
}
end
# Returns the `Hash` to use for creating the `sources` enum for
# `Ci::Pipeline`.
def self.sources
{
unknown: nil,
push: 1,
web: 2,
trigger: 3,
schedule: 4,
api: 5,
external: 6,
# TODO: Rename `pipeline` to `cross_project_pipeline` in 13.0
# https://gitlab.com/gitlab-org/gitlab/issues/195991
pipeline: 7,
chat: 8,
webide: 9,
merge_request_event: 10,
external_pull_request_event: 11,
parent_pipeline: 12,
ondemand_dast_scan: 13
}
end
# Returns the `Hash` to use for creating the `config_sources` enum for
# `Ci::Pipeline`.
def self.config_sources
{
unknown_source: nil,
repository_source: 1,
auto_devops_source: 2,
webide_source: 3,
remote_source: 4,
external_project_source: 5,
bridge_source: 6,
parameter_source: 7
}
end
def self.ci_config_sources
config_sources.slice(
:unknown_source,
:repository_source,
:auto_devops_source,
:remote_source,
:external_project_source
)
end
def self.ci_config_sources_values
ci_config_sources.values
end
def self.non_ci_config_source_values
config_sources.values - ci_config_sources.values
end
end
end
Ci::PipelineEnums.prepend_if_ee('EE::Ci::PipelineEnums')
......@@ -77,9 +77,9 @@ class CommitStatus < ApplicationRecord
merge(or_conditions)
end
# We use `Enums::CommitStatus.failure_reasons` here so that EE can more easily
# We use `CommitStatusEnums.failure_reasons` here so that EE can more easily
# extend this `Hash` with new values.
enum_with_nil failure_reason: Enums::CommitStatus.failure_reasons
enum_with_nil failure_reason: ::CommitStatusEnums.failure_reasons
##
# We still create some CommitStatuses outside of CreatePipelineService.
......
# frozen_string_literal: true
module Enums
module Ci
module Pipeline
# Returns the `Hash` to use for creating the `failure_reason` enum for
# `Ci::Pipeline`.
def self.failure_reasons
{
unknown_failure: 0,
config_error: 1,
external_validation_failure: 2
}
end
# Returns the `Hash` to use for creating the `sources` enum for
# `Ci::Pipeline`.
def self.sources
{
unknown: nil,
push: 1,
web: 2,
trigger: 3,
schedule: 4,
api: 5,
external: 6,
# TODO: Rename `pipeline` to `cross_project_pipeline` in 13.0
# https://gitlab.com/gitlab-org/gitlab/issues/195991
pipeline: 7,
chat: 8,
webide: 9,
merge_request_event: 10,
external_pull_request_event: 11,
parent_pipeline: 12,
ondemand_dast_scan: 13
}
end
# Returns the `Hash` to use for creating the `config_sources` enum for
# `Ci::Pipeline`.
def self.config_sources
{
unknown_source: nil,
repository_source: 1,
auto_devops_source: 2,
webide_source: 3,
remote_source: 4,
external_project_source: 5,
bridge_source: 6,
parameter_source: 7
}
end
def self.ci_config_sources
config_sources.slice(
:unknown_source,
:repository_source,
:auto_devops_source,
:remote_source,
:external_project_source
)
end
def self.ci_config_sources_values
ci_config_sources.values
end
def self.non_ci_config_source_values
config_sources.values - ci_config_sources.values
end
end
end
end
Enums::Ci::Pipeline.prepend_if_ee('EE::Enums::Ci::Pipeline')
# frozen_string_literal: true
module Enums
module CommitStatus
# Returns the Hash to use for creating the `failure_reason` enum for
# `CommitStatus`.
def self.failure_reasons
{
unknown_failure: nil,
script_failure: 1,
api_failure: 2,
stuck_or_timeout_failure: 3,
runner_system_failure: 4,
missing_dependency_failure: 5,
runner_unsupported: 6,
stale_schedule: 7,
job_execution_timeout: 8,
archived_failure: 9,
unmet_prerequisites: 10,
scheduler_failure: 11,
data_integrity_failure: 12,
forward_deployment_failure: 13,
protected_environment_failure: 1_000,
insufficient_bridge_permissions: 1_001,
downstream_bridge_project_not_found: 1_002,
invalid_bridge_trigger: 1_003,
upstream_bridge_project_not_found: 1_004,
insufficient_upstream_permissions: 1_005,
bridge_pipeline_is_child_pipeline: 1_006,
downstream_pipeline_creation_failed: 1_007
}
end
end
end
# frozen_string_literal: true
module Enums
module PrometheusMetric
def self.groups
{
# built-in groups
nginx_ingress_vts: -1,
ha_proxy: -2,
aws_elb: -3,
nginx: -4,
kubernetes: -5,
nginx_ingress: -6,
cluster_health: -100
}.merge(custom_groups).freeze
end
# custom/user groups
def self.custom_groups
{
business: 0,
response: 1,
system: 2
}.freeze
end
def self.group_details
{
# built-in groups
nginx_ingress_vts: {
group_title: _('Response metrics (NGINX Ingress VTS)'),
required_metrics: %w(nginx_upstream_responses_total nginx_upstream_response_msecs_avg),
priority: 10
}.freeze,
nginx_ingress: {
group_title: _('Response metrics (NGINX Ingress)'),
required_metrics: %w(nginx_ingress_controller_requests nginx_ingress_controller_ingress_upstream_latency_seconds_sum),
priority: 10
}.freeze,
ha_proxy: {
group_title: _('Response metrics (HA Proxy)'),
required_metrics: %w(haproxy_frontend_http_requests_total haproxy_frontend_http_responses_total),
priority: 10
}.freeze,
aws_elb: {
group_title: _('Response metrics (AWS ELB)'),
required_metrics: %w(aws_elb_request_count_sum aws_elb_latency_average aws_elb_httpcode_backend_5_xx_sum),
priority: 10
}.freeze,
nginx: {
group_title: _('Response metrics (NGINX)'),
required_metrics: %w(nginx_server_requests nginx_server_requestMsec),
priority: 10
}.freeze,
kubernetes: {
group_title: _('System metrics (Kubernetes)'),
required_metrics: %w(container_memory_usage_bytes container_cpu_usage_seconds_total),
priority: 5
}.freeze,
cluster_health: {
group_title: _('Cluster Health'),
required_metrics: %w(container_memory_usage_bytes container_cpu_usage_seconds_total),
priority: 10
}.freeze
}.merge(custom_group_details).freeze
end
# custom/user groups
def self.custom_group_details
{
business: {
group_title: _('Business metrics (Custom)'),
priority: 0
}.freeze,
response: {
group_title: _('Response metrics (Custom)'),
priority: -5
}.freeze,
system: {
group_title: _('System metrics (Custom)'),
priority: -10
}.freeze
}.freeze
end
end
end
# frozen_string_literal: true
module Enums
module UserCallout
# Returns the `Hash` to use for the `feature_name` enum in the `UserCallout`
# model.
#
# This method is separate from the `UserCallout` model so that it can be
# extended by EE.
#
# If you are going to add new items to this hash, check that you're not going
# to conflict with EE-only values: https://gitlab.com/gitlab-org/gitlab/blob/master/ee/app/models/concerns/ee/enums/user_callout.rb
def self.feature_names
{
gke_cluster_integration: 1,
gcp_signup_offer: 2,
cluster_security_warning: 3,
suggest_popover_dismissed: 9,
tabs_position_highlight: 10,
webhooks_moved: 13,
admin_integrations_moved: 15,
personal_access_token_expiry: 21 # EE-only
}
end
end
end
Enums::UserCallout.prepend_if_ee('EE::Enums::UserCallout')
......@@ -21,7 +21,7 @@ class InternalId < ApplicationRecord
belongs_to :project
belongs_to :namespace
enum usage: Enums::InternalId.usage_resources
enum usage: ::InternalIdEnums.usage_resources
validates :usage, presence: true
......
# frozen_string_literal: true
module Enums
module InternalId
def self.usage_resources
# when adding new resource, make sure it doesn't conflict with EE usage_resources
{
module InternalIdEnums
def self.usage_resources
# when adding new resource, make sure it doesn't conflict with EE usage_resources
{
issues: 0,
merge_requests: 1,
deployments: 2,
......@@ -15,9 +14,8 @@ module Enums
operations_user_lists: 7,
alert_management_alerts: 8,
sprints: 9 # iterations
}
end
}
end
end
Enums::InternalId.prepend_if_ee('EE::Enums::InternalId')
InternalIdEnums.prepend_if_ee('EE::InternalIdEnums')
......@@ -4,7 +4,7 @@ class PrometheusMetric < ApplicationRecord
belongs_to :project, validate: true, inverse_of: :prometheus_metrics
has_many :prometheus_alerts, inverse_of: :prometheus_metric
enum group: Enums::PrometheusMetric.groups
enum group: PrometheusMetricEnums.groups
validates :title, presence: true
validates :query, presence: true
......@@ -72,6 +72,6 @@ class PrometheusMetric < ApplicationRecord
private
def group_details(group)
Enums::PrometheusMetric.group_details.fetch(group.to_sym)
PrometheusMetricEnums.group_details.fetch(group.to_sym)
end
end
# frozen_string_literal: true
module PrometheusMetricEnums
def self.groups
{
# built-in groups
nginx_ingress_vts: -1,
ha_proxy: -2,
aws_elb: -3,
nginx: -4,
kubernetes: -5,
nginx_ingress: -6,
cluster_health: -100
}.merge(custom_groups).freeze
end
# custom/user groups
def self.custom_groups
{
business: 0,
response: 1,
system: 2
}.freeze
end
def self.group_details
{
# built-in groups
nginx_ingress_vts: {
group_title: _('Response metrics (NGINX Ingress VTS)'),
required_metrics: %w(nginx_upstream_responses_total nginx_upstream_response_msecs_avg),
priority: 10
}.freeze,
nginx_ingress: {
group_title: _('Response metrics (NGINX Ingress)'),
required_metrics: %w(nginx_ingress_controller_requests nginx_ingress_controller_ingress_upstream_latency_seconds_sum),
priority: 10
}.freeze,
ha_proxy: {
group_title: _('Response metrics (HA Proxy)'),
required_metrics: %w(haproxy_frontend_http_requests_total haproxy_frontend_http_responses_total),
priority: 10
}.freeze,
aws_elb: {
group_title: _('Response metrics (AWS ELB)'),
required_metrics: %w(aws_elb_request_count_sum aws_elb_latency_average aws_elb_httpcode_backend_5_xx_sum),
priority: 10
}.freeze,
nginx: {
group_title: _('Response metrics (NGINX)'),
required_metrics: %w(nginx_server_requests nginx_server_requestMsec),
priority: 10
}.freeze,
kubernetes: {
group_title: _('System metrics (Kubernetes)'),
required_metrics: %w(container_memory_usage_bytes container_cpu_usage_seconds_total),
priority: 5
}.freeze,
cluster_health: {
group_title: _('Cluster Health'),
required_metrics: %w(container_memory_usage_bytes container_cpu_usage_seconds_total),
priority: 10
}.freeze
}.merge(custom_group_details).freeze
end
# custom/user groups
def self.custom_group_details
{
business: {
group_title: _('Business metrics (Custom)'),
priority: 0
}.freeze,
response: {
group_title: _('Response metrics (Custom)'),
priority: -5
}.freeze,
system: {
group_title: _('System metrics (Custom)'),
priority: -10
}.freeze
}.freeze
end
end
......@@ -3,9 +3,9 @@
class UserCallout < ApplicationRecord
belongs_to :user
# We use `Enums::UserCallout.feature_names` here so that EE can more easily
# We use `UserCalloutEnums.feature_names` here so that EE can more easily
# extend this `Hash` with new values.
enum feature_name: Enums::UserCallout.feature_names
enum feature_name: ::UserCalloutEnums.feature_names
validates :user, presence: true
validates :feature_name,
......
# frozen_string_literal: true
module UserCalloutEnums
# Returns the `Hash` to use for the `feature_name` enum in the `UserCallout`
# model.
#
# This method is separate from the `UserCallout` model so that it can be
# extended by EE.
#
# If you are going to add new items to this hash, check that you're not going
# to conflict with EE-only values: https://gitlab.com/gitlab-org/gitlab/blob/master/ee/app/models/ee/user_callout_enums.rb
def self.feature_names
{
gke_cluster_integration: 1,
gcp_signup_offer: 2,
cluster_security_warning: 3,
suggest_popover_dismissed: 9,
tabs_position_highlight: 10,
webhooks_moved: 13,
admin_integrations_moved: 15,
personal_access_token_expiry: 21 # EE-only
}
end
end
UserCalloutEnums.prepend_if_ee('EE::UserCalloutEnums')
......@@ -31,7 +31,7 @@ module Metrics
# A group title is valid if it is one of the limited
# options the user can select in the UI.
def valid_group_title?(group)
Enums::PrometheusMetric
PrometheusMetricEnums
.custom_group_details
.map { |_, details| details[:group_title] }
.include?(group)
......@@ -100,12 +100,12 @@ module Metrics
# Returns a symbol representing the group that
# the dashboard's group title belongs to.
# It will be one of the keys found under
# Enums::PrometheusMetric.custom_groups.
# PrometheusMetricEnums.custom_groups.
#
# @return [String]
def group_key
strong_memoize(:group_key) do
Enums::PrometheusMetric
PrometheusMetricEnums
.group_details
.find { |_, details| details[:group_title] == group }
.first
......
......@@ -13,7 +13,7 @@ class DeleteUserCalloutAlertsMoved < ActiveRecord::Migration[6.0]
BATCH_SIZE = 1_000
# Inlined from Enums::UserCallout.feature_names
# Inlined from UserCalloutEnums.feature_names
FEATURE_NAME_ALERTS_MOVED = 20
def up
......
......@@ -33,32 +33,28 @@ tempted to organize the `enum` as the following:
```ruby
# Define `failure_reason` enum in `Pipeline` model:
class Pipeline < ApplicationRecord
enum failure_reason: Enums::Pipeline.failure_reasons
enum failure_reason: ::PipelineEnums.failure_reasons
end
```
```ruby
# Define key/value pairs that used in FOSS and EE:
module Enums
module Pipeline
def self.failure_reasons
{ unknown_failure: 0, config_error: 1 }
end
module PipelineEnums
def self.failure_reasons
{ unknown_failure: 0, config_error: 1 }
end
end
Enums::Pipeline.prepend_if_ee('EE::Enums::Pipeline')
PipelineEnums.prepend_if_ee('EE::PipelineEnums')
```
```ruby
# Define key/value pairs that used in EE only:
module EE
module Enums
module Pipeline
override :failure_reasons
def failure_reasons
super.merge(activity_limit_exceeded: 2)
end
module PipelineEnums
override :failure_reasons
def failure_reasons
super.merge(activity_limit_exceeded: 2)
end
end
end
......@@ -67,7 +63,7 @@ end
This works as-is, however, it has a couple of downside that:
- Someone could define a key/value pair in EE that is **conflicted** with a value defined in FOSS.
e.g. Define `activity_limit_exceeded: 1` in `EE::Enums::Pipeline`.
e.g. Define `activity_limit_exceeded: 1` in `EE::PipelineEnums`.
- When it happens, the feature works totally different.
e.g. We cannot figure out `failure_reason` is either `config_error` or `activity_limit_exceeded`.
- When it happens, we have to ship a database migration to fix the data integrity,
......@@ -78,12 +74,10 @@ For example, this example sets `1000` as the offset:
```ruby
module EE
module Enums
module Pipeline
override :failure_reasons
def failure_reasons
super.merge(activity_limit_exceeded: 1_000, size_limit_exceeded: 1_001)
end
module PipelineEnums
override :failure_reasons
def failure_reasons
super.merge(activity_limit_exceeded: 1_000, size_limit_exceeded: 1_001)
end
end
end
......
# frozen_string_literal: true
module EE
module Enums
module Ci
module Pipeline
extend ActiveSupport::Concern
class_methods do
extend ::Gitlab::Utils::Override
override :failure_reasons
def failure_reasons
super.merge(
activity_limit_exceeded: 20,
size_limit_exceeded: 21,
job_activity_limit_exceeded: 22
)
end
end
end
end
end
end
# frozen_string_literal: true
module EE
module Enums
module UserCallout
extend ActiveSupport::Concern
class_methods do
extend ::Gitlab::Utils::Override
# If you are going to add new items to this hash, check that you're not going
# to conflict with FOSS-only values: https://gitlab.com/gitlab-org/gitlab/blob/master/app/models/concerns/enums/user_callout.rb
override :feature_names
def feature_names
super.merge(
gold_trial: 4,
geo_enable_hashed_storage: 5,
geo_migrate_hashed_storage: 6,
canary_deployment: 7,
gold_trial_billings: 8,
threat_monitoring_info: 11,
account_recovery_regular_check: 12,
users_over_license_banner: 16,
standalone_vulnerabilities_introduction_banner: 17,
active_user_count_threshold: 18,
buy_pipeline_minutes_notification_dot: 19
)
end
end
end
end
end
# frozen_string_literal: true
module EE
module Enums
module InternalId
module Ci
module PipelineEnums
extend ActiveSupport::Concern
class_methods do
extend ::Gitlab::Utils::Override
override :usage_resources
def usage_resources
super.merge(requirements: 1000)
override :failure_reasons
def failure_reasons
super.merge(
activity_limit_exceeded: 20,
size_limit_exceeded: 21,
job_activity_limit_exceeded: 22
)
end
end
end
......
# frozen_string_literal: true
module EE
module InternalIdEnums
extend ActiveSupport::Concern
class_methods do
extend ::Gitlab::Utils::Override
override :usage_resources
def usage_resources
super.merge(requirements: 1000)
end
end
end
end
# frozen_string_literal: true
module EE
module UserCalloutEnums
extend ActiveSupport::Concern
class_methods do
extend ::Gitlab::Utils::Override
# If you are going to add new items to this hash, check that you're not going
# to conflict with FOSS-only values: https://gitlab.com/gitlab-org/gitlab/blob/master/app/models/user_callout_enums.rb
override :feature_names
def feature_names
super.merge(
gold_trial: 4,
geo_enable_hashed_storage: 5,
geo_migrate_hashed_storage: 6,
canary_deployment: 7,
gold_trial_billings: 8,
threat_monitoring_info: 11,
account_recovery_regular_check: 12,
users_over_license_banner: 16,
standalone_vulnerabilities_introduction_banner: 17,
active_user_count_threshold: 18,
buy_pipeline_minutes_notification_dot: 19
)
end
end
end
end
......@@ -27,7 +27,7 @@ module Gitlab
private
def custom_group_titles
@custom_group_titles ||= Enums::PrometheusMetric.custom_group_details.values.map { |group_details| group_details[:group_title] }
@custom_group_titles ||= PrometheusMetricEnums.custom_group_details.values.map { |group_details| group_details[:group_title] }
end
def edit_path(metric)
......
......@@ -8,7 +8,7 @@ RSpec.describe Gitlab::DatabaseImporters::CommonMetrics::PrometheusMetric do
end
it '.group_titles equals ::PrometheusMetric' do
existing_group_titles = Enums::PrometheusMetric.group_details.transform_values do |value|
existing_group_titles = ::PrometheusMetricEnums.group_details.transform_values do |value|
value[:group_title]
end
expect(Gitlab::DatabaseImporters::CommonMetrics::PrometheusMetricEnums.group_titles).to eq(existing_group_titles)
......
......@@ -2020,7 +2020,7 @@ RSpec.describe Ci::Pipeline, :mailer do
let(:project) { create(:project, :repository) }
let(:branch) { project.default_branch }
let(:ref) { project.ci_refs.take }
let(:config_source) { Enums::Ci::Pipeline.config_sources[:parameter_source] }
let(:config_source) { Ci::PipelineEnums.config_sources[:parameter_source] }
let!(:pipeline1) { create(:ci_pipeline, :success, project: project, ref: branch) }
let!(:pipeline2) { create(:ci_pipeline, :success, project: project, ref: branch) }
let!(:pipeline3) { create(:ci_pipeline, :failed, project: project, ref: branch) }
......
......@@ -125,7 +125,7 @@ RSpec.describe Ci::Ref do
describe '#last_finished_pipeline_id' do
let(:pipeline_status) { :running }
let(:config_source) { Enums::Ci::Pipeline.config_sources[:repository_source] }
let(:config_source) { Ci::PipelineEnums.config_sources[:repository_source] }
let(:pipeline) { create(:ci_pipeline, pipeline_status, config_source: config_source) }
let(:ci_ref) { pipeline.ci_ref }
......@@ -143,7 +143,7 @@ RSpec.describe Ci::Ref do
end
context 'when the pipeline is not a ci_source' do
let(:config_source) { Enums::Ci::Pipeline.config_sources[:parameter_source] }
let(:config_source) { Ci::PipelineEnums.config_sources[:parameter_source] }
it 'returns nil' do
expect(ci_ref.last_finished_pipeline_id).to be_nil
......
......@@ -65,7 +65,7 @@ RSpec.describe Ci::PipelinePresenter do
describe '#failure_reason' do
context 'when pipeline has a failure reason' do
Enums::Ci::Pipeline.failure_reasons.keys.each do |failure_reason|
::Ci::PipelineEnums.failure_reasons.keys.each do |failure_reason|
context "when failure reason is #{failure_reason}" do
before do
pipeline.failure_reason = failure_reason
......
......@@ -477,7 +477,7 @@ RSpec.describe API::Ci::Pipelines do
end
context 'when config source is not ci' do
let(:non_ci_config_source) { Enums::Ci::Pipeline.non_ci_config_source_values.first }
let(:non_ci_config_source) { ::Ci::PipelineEnums.non_ci_config_source_values.first }
let(:pipeline_not_ci) do
create(:ci_pipeline, config_source: non_ci_config_source, project: project)
end
......
......@@ -240,7 +240,7 @@ RSpec.describe API::Jobs do
end
context 'when config source not ci' do
let(:non_ci_config_source) { Enums::Ci::Pipeline.non_ci_config_source_values.first }
let(:non_ci_config_source) { ::Ci::PipelineEnums.non_ci_config_source_values.first }
let(:pipeline) do
create(:ci_pipeline, config_source: non_ci_config_source, project: project)
end
......
......@@ -41,7 +41,7 @@ module MetricsDashboardHelpers
end
def business_metric_title
Enums::PrometheusMetric.group_details[:business][:group_title]
PrometheusMetricEnums.group_details[:business][:group_title]
end
def self_monitoring_dashboard_path
......
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