Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
b00c3f71
Commit
b00c3f71
authored
Sep 23, 2019
by
Matija Čupić
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Move Needs entry to core
Moves the Needs entry to core, because it's required for use in DAG needs.
parent
977ea357
Changes
13
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
374 additions
and
60 deletions
+374
-60
ee/lib/ee/gitlab/ci/config/entry/bridge.rb
ee/lib/ee/gitlab/ci/config/entry/bridge.rb
+1
-1
ee/lib/ee/gitlab/ci/config/entry/need.rb
ee/lib/ee/gitlab/ci/config/entry/need.rb
+50
-0
ee/spec/lib/ee/gitlab/ci/config/entry/bridge_spec.rb
ee/spec/lib/ee/gitlab/ci/config/entry/bridge_spec.rb
+1
-1
ee/spec/lib/ee/gitlab/ci/config/entry/need_spec.rb
ee/spec/lib/ee/gitlab/ci/config/entry/need_spec.rb
+51
-0
ee/spec/lib/ee/gitlab/ci/config/entry/needs_spec.rb
ee/spec/lib/ee/gitlab/ci/config/entry/needs_spec.rb
+40
-32
lib/gitlab/ci/config/entry/job.rb
lib/gitlab/ci/config/entry/job.rb
+3
-1
lib/gitlab/ci/config/entry/need.rb
lib/gitlab/ci/config/entry/need.rb
+42
-0
lib/gitlab/ci/config/entry/needs.rb
lib/gitlab/ci/config/entry/needs.rb
+52
-0
lib/gitlab/ci/yaml_processor.rb
lib/gitlab/ci/yaml_processor.rb
+3
-3
spec/lib/gitlab/ci/config/entry/job_spec.rb
spec/lib/gitlab/ci/config/entry/job_spec.rb
+1
-16
spec/lib/gitlab/ci/config/entry/need_spec.rb
spec/lib/gitlab/ci/config/entry/need_spec.rb
+67
-0
spec/lib/gitlab/ci/config/entry/needs_spec.rb
spec/lib/gitlab/ci/config/entry/needs_spec.rb
+62
-0
spec/lib/gitlab/ci/yaml_processor_spec.rb
spec/lib/gitlab/ci/yaml_processor_spec.rb
+1
-6
No files found.
ee/lib/ee/gitlab/ci/config/entry/bridge.rb
View file @
b00c3f71
...
@@ -41,7 +41,7 @@ module EE
...
@@ -41,7 +41,7 @@ module EE
description:
'CI/CD Bridge downstream trigger definition.'
,
description:
'CI/CD Bridge downstream trigger definition.'
,
inherit:
false
inherit:
false
entry
:needs
,
::
EE
::
Gitlab
::
Ci
::
Config
::
Entry
::
Needs
,
entry
:needs
,
::
Gitlab
::
Ci
::
Config
::
Entry
::
Needs
,
description:
'CI/CD Bridge needs dependency definition.'
,
description:
'CI/CD Bridge needs dependency definition.'
,
inherit:
false
inherit:
false
...
...
ee/lib/ee/gitlab/ci/config/entry/need
s
.rb
→
ee/lib/ee/gitlab/ci/config/entry/need.rb
View file @
b00c3f71
...
@@ -5,14 +5,14 @@ module EE
...
@@ -5,14 +5,14 @@ module EE
module
Ci
module
Ci
module
Config
module
Config
module
Entry
module
Entry
##
module
Need
# Entry that represents a cross-project needs dependency.
extend
ActiveSupport
::
Concern
#
class
Needs
<
::
Gitlab
::
Config
::
Entry
::
Simplifiable
strategy
:BridgeNeeds
,
if:
->
(
config
)
{
config
.
is_a?
(
Hash
)
}
strategy
:ComplexNeeds
,
if:
->
(
config
)
{
config
.
is_a?
(
Array
)
}
class
BridgeNeeds
<
::
Gitlab
::
Config
::
Entry
::
Node
prepended
do
strategy
:Bridge
,
if:
->
(
config
)
{
config
.
is_a?
(
Hash
)
}
end
class
Bridge
<
::
Gitlab
::
Config
::
Entry
::
Node
include
::
Gitlab
::
Config
::
Entry
::
Validatable
include
::
Gitlab
::
Config
::
Entry
::
Validatable
include
::
Gitlab
::
Config
::
Entry
::
Attributable
include
::
Gitlab
::
Config
::
Entry
::
Attributable
...
@@ -24,43 +24,22 @@ module EE
...
@@ -24,43 +24,22 @@ module EE
validates
:config
,
allowed_keys:
ALLOWED_KEYS
validates
:config
,
allowed_keys:
ALLOWED_KEYS
validates
:pipeline
,
type:
String
,
presence:
true
validates
:pipeline
,
type:
String
,
presence:
true
end
end
end
class
ComplexNeeds
<
::
Gitlab
::
Config
::
Entry
::
Node
include
::
Gitlab
::
Config
::
Entry
::
Validatable
validations
do
validates
:config
,
presence:
true
validates
:config
,
type:
Array
validate
:one_needs_pipeline
validate
:needs_array_elements
end
def
one_needs_pipeline
def
bridge?
if
config
.
count
{
|
element
|
element
.
is_a?
(
Hash
)
}
>
1
true
errors
.
add
(
:needs
,
'needs hash element needs to have a pipeline key'
)
end
end
end
def
needs_array_elements
config
.
each
do
|
element
|
next
if
element
.
is_a?
(
String
)
unless
element
.
is_a?
(
Hash
)
&&
element
[
:pipeline
]
def
pipeline?
errors
.
add
(
:needs
,
'needs hash element needs to have a pipeline key'
)
false
end
end
end
end
end
def
value
module
UnknownStrategy
bridge
,
pipeline
=
config
.
partition
{
|
element
|
element
.
is_a?
(
Hash
)
}
extend
::
Gitlab
::
Utils
::
Override
{
bridge:
bridge
.
first
,
pipeline:
pipeline
}
end
end
class
UnknownStrategy
<
::
Gitlab
::
Config
::
Entry
::
Node
override
:errors
def
errors
def
errors
[
"
#{
location
}
has to be
either an array of conditions or a
hash"
]
[
"
#{
location
}
has to be
a string, symbol or
hash"
]
end
end
end
end
end
end
...
...
ee/spec/lib/ee/gitlab/ci/config/entry/bridge_spec.rb
View file @
b00c3f71
...
@@ -88,7 +88,7 @@ describe EE::Gitlab::Ci::Config::Entry::Bridge do
...
@@ -88,7 +88,7 @@ describe EE::Gitlab::Ci::Config::Entry::Bridge do
describe
'#value'
do
describe
'#value'
do
it
'is returns a bridge job configuration'
do
it
'is returns a bridge job configuration'
do
expect
(
subject
.
value
).
to
eq
(
name: :my_bridge
,
expect
(
subject
.
value
).
to
eq
(
name: :my_bridge
,
needs:
{
pipeline:
'some/project'
},
needs:
{
bridge:
{
pipeline:
'some/project'
}
},
ignore:
false
,
ignore:
false
,
stage:
'test'
,
stage:
'test'
,
only:
{
refs:
%w[branches tags]
})
only:
{
refs:
%w[branches tags]
})
...
...
ee/spec/lib/ee/gitlab/ci/config/entry/need_spec.rb
0 → 100644
View file @
b00c3f71
# frozen_string_literal: true
require
'spec_helper'
describe
::
Gitlab
::
Ci
::
Config
::
Entry
::
Need
do
subject
{
described_class
.
new
(
config
)
}
context
'when upstream is specified'
do
let
(
:config
)
{
{
pipeline:
'some/project'
}
}
describe
'#valid?'
do
it
{
is_expected
.
to
be_valid
}
end
describe
'#value'
do
it
'returns job needs configuration'
do
expect
(
subject
.
value
).
to
eq
(
pipeline:
'some/project'
)
end
end
end
context
'when need is empty'
do
let
(
:config
)
{
{}
}
describe
'#valid?'
do
it
{
is_expected
.
not_to
be_valid
}
end
describe
'#errors'
do
it
'is returns an error about an empty config'
do
expect
(
subject
.
errors
.
first
)
.
to
end_with
(
"bridge config can't be blank"
)
end
end
end
context
'when need is the wrong type'
do
let
(
:config
)
{
123
}
describe
'#valid?'
do
it
{
is_expected
.
not_to
be_valid
}
end
describe
'#errors'
do
it
'is returns an error about an empty config'
do
expect
(
subject
.
errors
.
first
)
.
to
end_with
(
'has to be a string, symbol or hash'
)
end
end
end
end
ee/spec/lib/ee/gitlab/ci/config/entry/needs_spec.rb
View file @
b00c3f71
# frozen_string_literal: true
# frozen_string_literal: true
require
'fast_spec_helper'
require
'spec_helper'
require_dependency
'active_model'
describe
EE
::
Gitlab
::
Ci
::
Config
::
Entry
::
Needs
do
describe
::
Gitlab
::
Ci
::
Config
::
Entry
::
Needs
do
subject
{
described_class
.
new
(
config
)
}
subject
(
:needs
)
{
described_class
.
new
(
config
)
}
context
'when needs is a bridge needs'
do
describe
'validations'
do
context
'when upstream config is a non-empty string'
do
before
do
let
(
:config
)
{
{
pipeline:
'some/project'
}
}
needs
.
compose!
end
context
'when entry config value is correct'
do
let
(
:config
)
{
[
'job_name'
,
pipeline:
'some/project'
]
}
describe
'#valid?'
do
describe
'#valid?'
do
it
{
is_expected
.
to
be_valid
}
it
{
is_expected
.
to
be_valid
}
end
end
describe
'#value'
do
it
'is returns a upstream configuration hash'
do
expect
(
subject
.
value
).
to
eq
(
pipeline:
'some/project'
)
end
end
end
end
context
'when
upstream config is not a string
'
do
context
'when
wrong needs type is used
'
do
let
(
:config
)
{
{
pipeline:
123
}
}
let
(
:config
)
{
[
'job_name'
,
{
pipeline:
'some/project'
},
123
]
}
describe
'#valid?'
do
describe
'#valid?'
do
it
{
is_expected
.
not_to
be_valid
}
it
{
is_expected
.
not_to
be_valid
}
end
end
describe
'#errors'
do
describe
'#errors'
do
it
'returns an error message'
do
it
'returns error about incorrect type'
do
expect
(
subject
.
errors
.
first
)
error_message
=
Gitlab
.
ee?
?
'need has to be a string, symbol or hash'
:
'need has to be a string or symbol'
.
to
eq
(
'bridge needs pipeline should be a string'
)
end
expect
(
needs
.
errors
)
.
to
include
error_message
end
end
end
end
end
end
context
'when needs is a complex need
s'
do
context
'when bridge needs has wrong attribute
s'
do
let
(
:config
)
{
[
'test'
,
{
pipeline:
'test'
}
]
}
let
(
:config
)
{
[
'job_name'
,
project:
'some/project'
]
}
it
'test'
do
describe
'#valid?'
do
subject
it
{
is_expected
.
not_to
be_valid
}
end
end
end
end
end
context
'when needs is empty'
do
describe
'.compose!'
do
let
(
:config
)
{
''
}
context
'when valid job entries composed'
do
let
(
:config
)
{
[
'first_job_name'
,
pipeline:
'some/project'
]
}
describe
'#valid?'
do
before
do
it
{
is_expected
.
not_to
be_valid
}
needs
.
compose!
end
end
describe
'#errors'
do
describe
'#value'
do
it
'is returns an error about an empty config'
do
it
'returns key value'
do
expect
(
subject
.
errors
.
first
)
expect
(
needs
.
value
).
to
eq
(
pipeline:
[
'first_job_name'
],
bridge:
{
pipeline:
'some/project'
})
.
to
end_with
(
'has to be either an array of conditions or a hash'
)
end
end
describe
'#descendants'
do
it
'creates valid descendant nodes'
do
expect
(
needs
.
descendants
.
count
).
to
eq
2
expect
(
needs
.
descendants
)
.
to
all
(
be_an_instance_of
(
::
Gitlab
::
Ci
::
Config
::
Entry
::
Need
))
end
end
end
end
end
end
end
...
...
lib/gitlab/ci/config/entry/job.rb
View file @
b00c3f71
...
@@ -50,7 +50,6 @@ module Gitlab
...
@@ -50,7 +50,6 @@ module Gitlab
validates
:timeout
,
duration:
{
limit:
ChronicDuration
.
output
(
Project
::
MAX_BUILD_TIMEOUT
)
}
validates
:timeout
,
duration:
{
limit:
ChronicDuration
.
output
(
Project
::
MAX_BUILD_TIMEOUT
)
}
validates
:dependencies
,
array_of_strings:
true
validates
:dependencies
,
array_of_strings:
true
validates
:needs
,
array_of_strings:
true
validates
:extends
,
array_of_strings_or_string:
true
validates
:extends
,
array_of_strings_or_string:
true
validates
:rules
,
array_of_hashes:
true
validates
:rules
,
array_of_hashes:
true
end
end
...
@@ -114,6 +113,9 @@ module Gitlab
...
@@ -114,6 +113,9 @@ module Gitlab
description:
'List of evaluable Rules to determine job inclusion.'
,
description:
'List of evaluable Rules to determine job inclusion.'
,
inherit:
false
inherit:
false
entry
:needs
,
Entry
::
Needs
,
description:
'Needs configuration for this job.'
entry
:variables
,
Entry
::
Variables
,
entry
:variables
,
Entry
::
Variables
,
description:
'Environment variables available for this job.'
,
description:
'Environment variables available for this job.'
,
inherit:
false
inherit:
false
...
...
lib/gitlab/ci/config/entry/need.rb
0 → 100644
View file @
b00c3f71
# frozen_string_literal: true
module
Gitlab
module
Ci
class
Config
module
Entry
class
Need
<
::
Gitlab
::
Config
::
Entry
::
Simplifiable
strategy
:Pipeline
,
if:
->
(
config
)
{
config
.
is_a?
(
String
)
||
config
.
is_a?
(
Symbol
)
}
class
Pipeline
<
::
Gitlab
::
Config
::
Entry
::
Node
include
::
Gitlab
::
Config
::
Entry
::
Validatable
validations
do
validates
:config
,
presence:
true
end
def
bridge?
false
end
def
pipeline?
true
end
def
value
@config
.
to_s
end
end
class
UnknownStrategy
<
::
Gitlab
::
Config
::
Entry
::
Node
def
errors
[
"
#{
location
}
has to be a string or symbol"
]
end
end
end
end
end
end
end
::
Gitlab
::
Ci
::
Config
::
Entry
::
Need
.
prepend_if_ee
(
'::EE::Gitlab::Ci::Config::Entry::Need'
)
# rubocop: disable Cop/InjectEnterpriseEditionModule
::
Gitlab
::
Ci
::
Config
::
Entry
::
Need
::
UnknownStrategy
.
prepend_if_ee
(
'::EE::Gitlab::Ci::Config::Entry::Need::UnknownStrategy'
)
lib/gitlab/ci/config/entry/needs.rb
0 → 100644
View file @
b00c3f71
# frozen_string_literal: true
module
Gitlab
module
Ci
class
Config
module
Entry
##
# Entry that represents a set of needs dependencies.
#
class
Needs
<
::
Gitlab
::
Config
::
Entry
::
Node
include
::
Gitlab
::
Config
::
Entry
::
Validatable
validations
do
validates
:config
,
presence:
true
end
def
compose!
(
deps
=
nil
)
super
(
deps
)
do
[].
tap
{
|
array
|
array
.
push
(
@config
)
}.
flatten
.
each_with_index
do
|
need
,
index
|
@entries
[
index
]
=
::
Gitlab
::
Config
::
Entry
::
Factory
.
new
(
Entry
::
Need
)
.
value
(
need
)
.
with
(
key:
"need"
,
parent:
self
,
description:
"need definition."
)
# rubocop:disable CodeReuse/ActiveRecord
.
create!
end
@entries
.
each_value
do
|
entry
|
entry
.
compose!
(
deps
)
end
end
end
def
value
{}.
tap
do
|
result_hash
|
result_hash
[
:bridge
]
=
bridge_values
.
first
if
bridge_values
.
any?
result_hash
[
:pipeline
]
=
pipeline_values
if
pipeline_values
.
any?
end
end
private
def
bridge_values
@entries
.
values
.
select
(
&
:bridge?
).
map
(
&
:value
)
end
def
pipeline_values
@entries
.
values
.
select
(
&
:pipeline?
).
map
(
&
:value
)
end
end
end
end
end
end
lib/gitlab/ci/yaml_processor.rb
View file @
b00c3f71
...
@@ -40,7 +40,7 @@ module Gitlab
...
@@ -40,7 +40,7 @@ module Gitlab
environment:
job
[
:environment_name
],
environment:
job
[
:environment_name
],
coverage_regex:
job
[
:coverage
],
coverage_regex:
job
[
:coverage
],
yaml_variables:
yaml_variables
(
name
),
yaml_variables:
yaml_variables
(
name
),
needs_attributes:
job
[
:needs
][
:pipeline
]
&
.
map
{
|
need
|
{
name:
need
}
},
needs_attributes:
job
.
dig
(
:needs
,
:pipeline
)
&
.
map
{
|
need
|
{
name:
need
}
},
interruptible:
job
[
:interruptible
],
interruptible:
job
[
:interruptible
],
rules:
job
[
:rules
],
rules:
job
[
:rules
],
options:
{
options:
{
...
@@ -59,7 +59,7 @@ module Gitlab
...
@@ -59,7 +59,7 @@ module Gitlab
instance:
job
[
:instance
],
instance:
job
[
:instance
],
start_in:
job
[
:start_in
],
start_in:
job
[
:start_in
],
trigger:
job
[
:trigger
],
trigger:
job
[
:trigger
],
bridge_needs:
job
[
:needs
][
:bridge
]
bridge_needs:
job
.
dig
(
:needs
,
:bridge
)
}.
compact
}.
compact
}.
compact
}.
compact
end
end
...
@@ -159,7 +159,7 @@ module Gitlab
...
@@ -159,7 +159,7 @@ module Gitlab
end
end
def
validate_job_needs!
(
name
,
job
)
def
validate_job_needs!
(
name
,
job
)
return
unless
job
[
:needs
][
:pipeline
]
return
unless
job
.
dig
(
:needs
,
:pipeline
)
stage_index
=
@stages
.
index
(
job
[
:stage
])
stage_index
=
@stages
.
index
(
job
[
:stage
])
...
...
spec/lib/gitlab/ci/config/entry/job_spec.rb
View file @
b00c3f71
...
@@ -23,7 +23,7 @@ describe Gitlab::Ci::Config::Entry::Job do
...
@@ -23,7 +23,7 @@ describe Gitlab::Ci::Config::Entry::Job do
let
(
:result
)
do
let
(
:result
)
do
%i[before_script script stage type after_script cache
%i[before_script script stage type after_script cache
image services only except rules variables artifacts
image services only except rules
needs
variables artifacts
environment coverage retry]
environment coverage retry]
end
end
...
@@ -384,21 +384,6 @@ describe Gitlab::Ci::Config::Entry::Job do
...
@@ -384,21 +384,6 @@ describe Gitlab::Ci::Config::Entry::Job do
end
end
context
'when has needs'
do
context
'when has needs'
do
context
'that are not a array of strings'
do
let
(
:config
)
do
{
stage:
'test'
,
script:
'echo'
,
needs:
'build-job'
}
end
it
'returns error about invalid type'
do
expect
(
entry
).
not_to
be_valid
expect
(
entry
.
errors
).
to
include
'job needs should be an array of strings'
end
end
context
'when have dependencies that are not subset of needs'
do
context
'when have dependencies that are not subset of needs'
do
let
(
:config
)
do
let
(
:config
)
do
{
{
...
...
spec/lib/gitlab/ci/config/entry/need_spec.rb
0 → 100644
View file @
b00c3f71
# frozen_string_literal: true
require
'spec_helper'
describe
::
Gitlab
::
Ci
::
Config
::
Entry
::
Need
do
subject
(
:need
)
{
described_class
.
new
(
config
)
}
context
'when job is specified'
do
let
(
:config
)
{
'job_name'
}
describe
'#valid?'
do
it
{
is_expected
.
to
be_valid
}
end
describe
'#value'
do
it
'returns job needs configuration'
do
expect
(
need
.
value
).
to
eq
(
'job_name'
)
end
end
end
context
'when job is specified as symbol'
do
let
(
:config
)
{
:job_name
}
describe
'#valid?'
do
it
{
is_expected
.
to
be_valid
}
end
describe
'#value'
do
it
'returns job needs configuration'
do
expect
(
need
.
value
).
to
eq
(
'job_name'
)
end
end
end
context
'when need is empty'
do
let
(
:config
)
{
''
}
describe
'#valid?'
do
it
{
is_expected
.
not_to
be_valid
}
end
describe
'#errors'
do
it
'is returns an error about an empty config'
do
expect
(
need
.
errors
.
first
)
.
to
end_with
(
"pipeline config can't be blank"
)
end
end
end
context
'when need is not a string'
do
let
(
:config
)
{
123
}
describe
'#valid?'
do
it
{
is_expected
.
not_to
be_valid
}
end
describe
'#errors'
do
it
'is returns an error about an empty config'
do
error_message
=
Gitlab
.
ee?
?
'has to be a string, symbol or hash'
:
'has to be a string or symbol'
expect
(
need
.
errors
.
first
)
.
to
end_with
(
error_message
)
end
end
end
end
spec/lib/gitlab/ci/config/entry/needs_spec.rb
0 → 100644
View file @
b00c3f71
# frozen_string_literal: true
require
'spec_helper'
describe
::
Gitlab
::
Ci
::
Config
::
Entry
::
Needs
do
subject
(
:needs
)
{
described_class
.
new
(
config
)
}
describe
'validations'
do
before
do
needs
.
compose!
end
context
'when entry config value is correct'
do
let
(
:config
)
{
[
'job_name'
]
}
describe
'#valid?'
do
it
{
is_expected
.
to
be_valid
}
end
end
context
'when wrong needs type is used'
do
let
(
:config
)
{
[
123
]
}
describe
'#valid?'
do
it
{
is_expected
.
not_to
be_valid
}
end
describe
'#errors'
do
it
'returns error about incorrect type'
do
error_message
=
Gitlab
.
ee?
?
'need has to be a string, symbol or hash'
:
'need has to be a string or symbol'
expect
(
needs
.
errors
)
.
to
include
error_message
end
end
end
end
describe
'.compose!'
do
context
'when valid job entries composed'
do
let
(
:config
)
{
%w[first_job_name second_job_name]
}
before
do
needs
.
compose!
end
describe
'#value'
do
it
'returns key value'
do
expect
(
needs
.
value
).
to
eq
(
pipeline:
%w[first_job_name second_job_name]
)
end
end
describe
'#descendants'
do
it
'creates valid descendant nodes'
do
expect
(
needs
.
descendants
.
count
).
to
eq
2
expect
(
needs
.
descendants
)
.
to
all
(
be_an_instance_of
(
::
Gitlab
::
Ci
::
Config
::
Entry
::
Need
))
end
end
end
end
end
spec/lib/gitlab/ci/yaml_processor_spec.rb
View file @
b00c3f71
...
@@ -1293,12 +1293,7 @@ module Gitlab
...
@@ -1293,12 +1293,7 @@ module Gitlab
stage:
"test"
,
stage:
"test"
,
stage_idx:
2
,
stage_idx:
2
,
name:
"test1"
,
name:
"test1"
,
options:
{
options:
{
script:
[
"test"
]
},
script:
[
"test"
],
# This does not make sense, there is a follow-up:
# https://gitlab.com/gitlab-org/gitlab-foss/issues/65569
bridge_needs:
%w[build1 build2]
},
needs_attributes:
[
needs_attributes:
[
{
name:
"build1"
},
{
name:
"build1"
},
{
name:
"build2"
}
{
name:
"build2"
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment