Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
0028385e
Commit
0028385e
authored
Aug 24, 2017
by
Nick Sardo
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Consume new config value for network project id
parent
b248f777
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
150 additions
and
76 deletions
+150
-76
gce.go
pkg/cloudprovider/providers/gce/gce.go
+109
-47
gce_firewall.go
pkg/cloudprovider/providers/gce/gce_firewall.go
+7
-7
gce_loadbalancer_external.go
pkg/cloudprovider/providers/gce/gce_loadbalancer_external.go
+2
-2
gce_op.go
pkg/cloudprovider/providers/gce/gce_op.go
+23
-11
gce_routes.go
pkg/cloudprovider/providers/gce/gce_routes.go
+7
-7
gce_test.go
pkg/cloudprovider/providers/gce/gce_test.go
+0
-0
e2e.go
test/e2e/e2e.go
+2
-2
No files found.
pkg/cloudprovider/providers/gce/gce.go
View file @
0028385e
This diff is collapsed.
Click to expand it.
pkg/cloudprovider/providers/gce/gce_firewall.go
View file @
0028385e
...
@@ -27,38 +27,38 @@ func newFirewallMetricContext(request string) *metricContext {
...
@@ -27,38 +27,38 @@ func newFirewallMetricContext(request string) *metricContext {
// GetFirewall returns the Firewall by name.
// GetFirewall returns the Firewall by name.
func
(
gce
*
GCECloud
)
GetFirewall
(
name
string
)
(
*
compute
.
Firewall
,
error
)
{
func
(
gce
*
GCECloud
)
GetFirewall
(
name
string
)
(
*
compute
.
Firewall
,
error
)
{
mc
:=
newFirewallMetricContext
(
"get"
)
mc
:=
newFirewallMetricContext
(
"get"
)
v
,
err
:=
gce
.
service
.
Firewalls
.
Get
(
gce
.
projectID
,
name
)
.
Do
()
v
,
err
:=
gce
.
service
.
Firewalls
.
Get
(
gce
.
NetworkProjectID
()
,
name
)
.
Do
()
return
v
,
mc
.
Observe
(
err
)
return
v
,
mc
.
Observe
(
err
)
}
}
// CreateFirewall creates the passed firewall
// CreateFirewall creates the passed firewall
func
(
gce
*
GCECloud
)
CreateFirewall
(
f
*
compute
.
Firewall
)
error
{
func
(
gce
*
GCECloud
)
CreateFirewall
(
f
*
compute
.
Firewall
)
error
{
mc
:=
newFirewallMetricContext
(
"create"
)
mc
:=
newFirewallMetricContext
(
"create"
)
op
,
err
:=
gce
.
service
.
Firewalls
.
Insert
(
gce
.
projectID
,
f
)
.
Do
()
op
,
err
:=
gce
.
service
.
Firewalls
.
Insert
(
gce
.
NetworkProjectID
()
,
f
)
.
Do
()
if
err
!=
nil
{
if
err
!=
nil
{
return
mc
.
Observe
(
err
)
return
mc
.
Observe
(
err
)
}
}
return
gce
.
waitForGlobalOp
(
op
,
mc
)
return
gce
.
waitForGlobalOp
InProject
(
op
,
gce
.
NetworkProjectID
()
,
mc
)
}
}
// DeleteFirewall deletes the given firewall rule.
// DeleteFirewall deletes the given firewall rule.
func
(
gce
*
GCECloud
)
DeleteFirewall
(
name
string
)
error
{
func
(
gce
*
GCECloud
)
DeleteFirewall
(
name
string
)
error
{
mc
:=
newFirewallMetricContext
(
"delete"
)
mc
:=
newFirewallMetricContext
(
"delete"
)
op
,
err
:=
gce
.
service
.
Firewalls
.
Delete
(
gce
.
projectID
,
name
)
.
Do
()
op
,
err
:=
gce
.
service
.
Firewalls
.
Delete
(
gce
.
NetworkProjectID
()
,
name
)
.
Do
()
if
err
!=
nil
{
if
err
!=
nil
{
return
mc
.
Observe
(
err
)
return
mc
.
Observe
(
err
)
}
}
return
gce
.
waitForGlobalOp
(
op
,
mc
)
return
gce
.
waitForGlobalOp
InProject
(
op
,
gce
.
NetworkProjectID
()
,
mc
)
}
}
// UpdateFirewall applies the given firewall as an update to an existing service.
// UpdateFirewall applies the given firewall as an update to an existing service.
func
(
gce
*
GCECloud
)
UpdateFirewall
(
f
*
compute
.
Firewall
)
error
{
func
(
gce
*
GCECloud
)
UpdateFirewall
(
f
*
compute
.
Firewall
)
error
{
mc
:=
newFirewallMetricContext
(
"update"
)
mc
:=
newFirewallMetricContext
(
"update"
)
op
,
err
:=
gce
.
service
.
Firewalls
.
Update
(
gce
.
projectID
,
f
.
Name
,
f
)
.
Do
()
op
,
err
:=
gce
.
service
.
Firewalls
.
Update
(
gce
.
NetworkProjectID
()
,
f
.
Name
,
f
)
.
Do
()
if
err
!=
nil
{
if
err
!=
nil
{
return
mc
.
Observe
(
err
)
return
mc
.
Observe
(
err
)
}
}
return
gce
.
waitForGlobalOp
(
op
,
mc
)
return
gce
.
waitForGlobalOp
InProject
(
op
,
gce
.
NetworkProjectID
()
,
mc
)
}
}
pkg/cloudprovider/providers/gce/gce_loadbalancer_external.go
View file @
0028385e
...
@@ -729,7 +729,7 @@ func (gce *GCECloud) firewallNeedsUpdate(name, serviceName, region, ipAddress st
...
@@ -729,7 +729,7 @@ func (gce *GCECloud) firewallNeedsUpdate(name, serviceName, region, ipAddress st
return
false
,
false
,
nil
return
false
,
false
,
nil
}
}
fw
,
err
:=
gce
.
service
.
Firewalls
.
Get
(
gce
.
projectID
,
makeFirewallName
(
name
))
.
Do
()
fw
,
err
:=
gce
.
service
.
Firewalls
.
Get
(
gce
.
NetworkProjectID
()
,
makeFirewallName
(
name
))
.
Do
()
if
err
!=
nil
{
if
err
!=
nil
{
if
isHTTPErrorCode
(
err
,
http
.
StatusNotFound
)
{
if
isHTTPErrorCode
(
err
,
http
.
StatusNotFound
)
{
return
false
,
true
,
nil
return
false
,
true
,
nil
...
@@ -776,7 +776,7 @@ func (gce *GCECloud) ensureHttpHealthCheckFirewall(serviceName, ipAddress, regio
...
@@ -776,7 +776,7 @@ func (gce *GCECloud) ensureHttpHealthCheckFirewall(serviceName, ipAddress, regio
ports
:=
[]
v1
.
ServicePort
{{
Protocol
:
"tcp"
,
Port
:
hcPort
}}
ports
:=
[]
v1
.
ServicePort
{{
Protocol
:
"tcp"
,
Port
:
hcPort
}}
fwName
:=
MakeHealthCheckFirewallName
(
clusterID
,
hcName
,
isNodesHealthCheck
)
fwName
:=
MakeHealthCheckFirewallName
(
clusterID
,
hcName
,
isNodesHealthCheck
)
fw
,
err
:=
gce
.
service
.
Firewalls
.
Get
(
gce
.
projectID
,
fwName
)
.
Do
()
fw
,
err
:=
gce
.
service
.
Firewalls
.
Get
(
gce
.
NetworkProjectID
()
,
fwName
)
.
Do
()
if
err
!=
nil
{
if
err
!=
nil
{
if
!
isHTTPErrorCode
(
err
,
http
.
StatusNotFound
)
{
if
!
isHTTPErrorCode
(
err
,
http
.
StatusNotFound
)
{
return
fmt
.
Errorf
(
"error getting firewall for health checks: %v"
,
err
)
return
fmt
.
Errorf
(
"error getting firewall for health checks: %v"
,
err
)
...
...
pkg/cloudprovider/providers/gce/gce_op.go
View file @
0028385e
...
@@ -93,62 +93,74 @@ func getErrorFromOp(op *computev1.Operation) error {
...
@@ -93,62 +93,74 @@ func getErrorFromOp(op *computev1.Operation) error {
}
}
func
(
gce
*
GCECloud
)
waitForGlobalOp
(
op
gceObject
,
mc
*
metricContext
)
error
{
func
(
gce
*
GCECloud
)
waitForGlobalOp
(
op
gceObject
,
mc
*
metricContext
)
error
{
return
gce
.
waitForGlobalOpInProject
(
op
,
gce
.
ProjectID
(),
mc
)
}
func
(
gce
*
GCECloud
)
waitForRegionOp
(
op
gceObject
,
region
string
,
mc
*
metricContext
)
error
{
return
gce
.
waitForRegionOpInProject
(
op
,
gce
.
ProjectID
(),
region
,
mc
)
}
func
(
gce
*
GCECloud
)
waitForZoneOp
(
op
gceObject
,
zone
string
,
mc
*
metricContext
)
error
{
return
gce
.
waitForZoneOpInProject
(
op
,
gce
.
ProjectID
(),
zone
,
mc
)
}
func
(
gce
*
GCECloud
)
waitForGlobalOpInProject
(
op
gceObject
,
projectID
string
,
mc
*
metricContext
)
error
{
switch
v
:=
op
.
(
type
)
{
switch
v
:=
op
.
(
type
)
{
case
*
computealpha
.
Operation
:
case
*
computealpha
.
Operation
:
return
gce
.
waitForOp
(
convertToV1Operation
(
op
),
func
(
operationName
string
)
(
*
computev1
.
Operation
,
error
)
{
return
gce
.
waitForOp
(
convertToV1Operation
(
op
),
func
(
operationName
string
)
(
*
computev1
.
Operation
,
error
)
{
op
,
err
:=
gce
.
serviceAlpha
.
GlobalOperations
.
Get
(
gce
.
projectID
,
operationName
)
.
Do
()
op
,
err
:=
gce
.
serviceAlpha
.
GlobalOperations
.
Get
(
projectID
,
operationName
)
.
Do
()
return
convertToV1Operation
(
op
),
err
return
convertToV1Operation
(
op
),
err
},
mc
)
},
mc
)
case
*
computebeta
.
Operation
:
case
*
computebeta
.
Operation
:
return
gce
.
waitForOp
(
convertToV1Operation
(
op
),
func
(
operationName
string
)
(
*
computev1
.
Operation
,
error
)
{
return
gce
.
waitForOp
(
convertToV1Operation
(
op
),
func
(
operationName
string
)
(
*
computev1
.
Operation
,
error
)
{
op
,
err
:=
gce
.
serviceBeta
.
GlobalOperations
.
Get
(
gce
.
projectID
,
operationName
)
.
Do
()
op
,
err
:=
gce
.
serviceBeta
.
GlobalOperations
.
Get
(
projectID
,
operationName
)
.
Do
()
return
convertToV1Operation
(
op
),
err
return
convertToV1Operation
(
op
),
err
},
mc
)
},
mc
)
case
*
computev1
.
Operation
:
case
*
computev1
.
Operation
:
return
gce
.
waitForOp
(
op
.
(
*
computev1
.
Operation
),
func
(
operationName
string
)
(
*
computev1
.
Operation
,
error
)
{
return
gce
.
waitForOp
(
op
.
(
*
computev1
.
Operation
),
func
(
operationName
string
)
(
*
computev1
.
Operation
,
error
)
{
return
gce
.
service
.
GlobalOperations
.
Get
(
gce
.
projectID
,
operationName
)
.
Do
()
return
gce
.
service
.
GlobalOperations
.
Get
(
projectID
,
operationName
)
.
Do
()
},
mc
)
},
mc
)
default
:
default
:
return
fmt
.
Errorf
(
"unexpected type: %T"
,
v
)
return
fmt
.
Errorf
(
"unexpected type: %T"
,
v
)
}
}
}
}
func
(
gce
*
GCECloud
)
waitForRegionOp
(
op
gceObject
,
region
string
,
mc
*
metricContext
)
error
{
func
(
gce
*
GCECloud
)
waitForRegionOp
InProject
(
op
gceObject
,
projectID
,
region
string
,
mc
*
metricContext
)
error
{
switch
v
:=
op
.
(
type
)
{
switch
v
:=
op
.
(
type
)
{
case
*
computealpha
.
Operation
:
case
*
computealpha
.
Operation
:
return
gce
.
waitForOp
(
convertToV1Operation
(
op
),
func
(
operationName
string
)
(
*
computev1
.
Operation
,
error
)
{
return
gce
.
waitForOp
(
convertToV1Operation
(
op
),
func
(
operationName
string
)
(
*
computev1
.
Operation
,
error
)
{
op
,
err
:=
gce
.
serviceAlpha
.
RegionOperations
.
Get
(
gce
.
projectID
,
region
,
operationName
)
.
Do
()
op
,
err
:=
gce
.
serviceAlpha
.
RegionOperations
.
Get
(
projectID
,
region
,
operationName
)
.
Do
()
return
convertToV1Operation
(
op
),
err
return
convertToV1Operation
(
op
),
err
},
mc
)
},
mc
)
case
*
computebeta
.
Operation
:
case
*
computebeta
.
Operation
:
return
gce
.
waitForOp
(
convertToV1Operation
(
op
),
func
(
operationName
string
)
(
*
computev1
.
Operation
,
error
)
{
return
gce
.
waitForOp
(
convertToV1Operation
(
op
),
func
(
operationName
string
)
(
*
computev1
.
Operation
,
error
)
{
op
,
err
:=
gce
.
serviceBeta
.
RegionOperations
.
Get
(
gce
.
projectID
,
region
,
operationName
)
.
Do
()
op
,
err
:=
gce
.
serviceBeta
.
RegionOperations
.
Get
(
projectID
,
region
,
operationName
)
.
Do
()
return
convertToV1Operation
(
op
),
err
return
convertToV1Operation
(
op
),
err
},
mc
)
},
mc
)
case
*
computev1
.
Operation
:
case
*
computev1
.
Operation
:
return
gce
.
waitForOp
(
op
.
(
*
computev1
.
Operation
),
func
(
operationName
string
)
(
*
computev1
.
Operation
,
error
)
{
return
gce
.
waitForOp
(
op
.
(
*
computev1
.
Operation
),
func
(
operationName
string
)
(
*
computev1
.
Operation
,
error
)
{
return
gce
.
service
.
RegionOperations
.
Get
(
gce
.
projectID
,
region
,
operationName
)
.
Do
()
return
gce
.
service
.
RegionOperations
.
Get
(
projectID
,
region
,
operationName
)
.
Do
()
},
mc
)
},
mc
)
default
:
default
:
return
fmt
.
Errorf
(
"unexpected type: %T"
,
v
)
return
fmt
.
Errorf
(
"unexpected type: %T"
,
v
)
}
}
}
}
func
(
gce
*
GCECloud
)
waitForZoneOp
(
op
gceObject
,
zone
string
,
mc
*
metricContext
)
error
{
func
(
gce
*
GCECloud
)
waitForZoneOp
InProject
(
op
gceObject
,
projectID
,
zone
string
,
mc
*
metricContext
)
error
{
switch
v
:=
op
.
(
type
)
{
switch
v
:=
op
.
(
type
)
{
case
*
computealpha
.
Operation
:
case
*
computealpha
.
Operation
:
return
gce
.
waitForOp
(
convertToV1Operation
(
op
),
func
(
operationName
string
)
(
*
computev1
.
Operation
,
error
)
{
return
gce
.
waitForOp
(
convertToV1Operation
(
op
),
func
(
operationName
string
)
(
*
computev1
.
Operation
,
error
)
{
op
,
err
:=
gce
.
serviceAlpha
.
ZoneOperations
.
Get
(
gce
.
projectID
,
zone
,
operationName
)
.
Do
()
op
,
err
:=
gce
.
serviceAlpha
.
ZoneOperations
.
Get
(
projectID
,
zone
,
operationName
)
.
Do
()
return
convertToV1Operation
(
op
),
err
return
convertToV1Operation
(
op
),
err
},
mc
)
},
mc
)
case
*
computebeta
.
Operation
:
case
*
computebeta
.
Operation
:
return
gce
.
waitForOp
(
convertToV1Operation
(
op
),
func
(
operationName
string
)
(
*
computev1
.
Operation
,
error
)
{
return
gce
.
waitForOp
(
convertToV1Operation
(
op
),
func
(
operationName
string
)
(
*
computev1
.
Operation
,
error
)
{
op
,
err
:=
gce
.
serviceBeta
.
ZoneOperations
.
Get
(
gce
.
projectID
,
zone
,
operationName
)
.
Do
()
op
,
err
:=
gce
.
serviceBeta
.
ZoneOperations
.
Get
(
projectID
,
zone
,
operationName
)
.
Do
()
return
convertToV1Operation
(
op
),
err
return
convertToV1Operation
(
op
),
err
},
mc
)
},
mc
)
case
*
computev1
.
Operation
:
case
*
computev1
.
Operation
:
return
gce
.
waitForOp
(
op
.
(
*
computev1
.
Operation
),
func
(
operationName
string
)
(
*
computev1
.
Operation
,
error
)
{
return
gce
.
waitForOp
(
op
.
(
*
computev1
.
Operation
),
func
(
operationName
string
)
(
*
computev1
.
Operation
,
error
)
{
return
gce
.
service
.
ZoneOperations
.
Get
(
gce
.
projectID
,
zone
,
operationName
)
.
Do
()
return
gce
.
service
.
ZoneOperations
.
Get
(
projectID
,
zone
,
operationName
)
.
Do
()
},
mc
)
},
mc
)
default
:
default
:
return
fmt
.
Errorf
(
"unexpected type: %T"
,
v
)
return
fmt
.
Errorf
(
"unexpected type: %T"
,
v
)
...
...
pkg/cloudprovider/providers/gce/gce_routes.go
View file @
0028385e
...
@@ -38,13 +38,13 @@ func (gce *GCECloud) ListRoutes(clusterName string) ([]*cloudprovider.Route, err
...
@@ -38,13 +38,13 @@ func (gce *GCECloud) ListRoutes(clusterName string) ([]*cloudprovider.Route, err
page
:=
0
page
:=
0
for
;
page
==
0
||
(
pageToken
!=
""
&&
page
<
maxPages
);
page
++
{
for
;
page
==
0
||
(
pageToken
!=
""
&&
page
<
maxPages
);
page
++
{
mc
:=
newRoutesMetricContext
(
"list_page"
)
mc
:=
newRoutesMetricContext
(
"list_page"
)
listCall
:=
gce
.
service
.
Routes
.
List
(
gce
.
projectID
)
listCall
:=
gce
.
service
.
Routes
.
List
(
gce
.
NetworkProjectID
()
)
prefix
:=
truncateClusterName
(
clusterName
)
prefix
:=
truncateClusterName
(
clusterName
)
// Filter for routes starting with clustername AND belonging to the
// Filter for routes starting with clustername AND belonging to the
// relevant gcp network AND having description = "k8s-node-route".
// relevant gcp network AND having description = "k8s-node-route".
filter
:=
"(name eq "
+
prefix
+
"-.*) "
filter
:=
"(name eq "
+
prefix
+
"-.*) "
filter
=
filter
+
"(network eq "
+
gce
.
networkURL
+
") "
filter
=
filter
+
"(network eq "
+
gce
.
NetworkURL
()
+
") "
filter
=
filter
+
"(description eq "
+
k8sNodeRouteTag
+
")"
filter
=
filter
+
"(description eq "
+
k8sNodeRouteTag
+
")"
listCall
=
listCall
.
Filter
(
filter
)
listCall
=
listCall
.
Filter
(
filter
)
if
pageToken
!=
""
{
if
pageToken
!=
""
{
...
@@ -80,11 +80,11 @@ func (gce *GCECloud) CreateRoute(clusterName string, nameHint string, route *clo
...
@@ -80,11 +80,11 @@ func (gce *GCECloud) CreateRoute(clusterName string, nameHint string, route *clo
}
}
mc
:=
newRoutesMetricContext
(
"create"
)
mc
:=
newRoutesMetricContext
(
"create"
)
insertOp
,
err
:=
gce
.
service
.
Routes
.
Insert
(
gce
.
projectID
,
&
compute
.
Route
{
insertOp
,
err
:=
gce
.
service
.
Routes
.
Insert
(
gce
.
NetworkProjectID
()
,
&
compute
.
Route
{
Name
:
routeName
,
Name
:
routeName
,
DestRange
:
route
.
DestinationCIDR
,
DestRange
:
route
.
DestinationCIDR
,
NextHopInstance
:
fmt
.
Sprintf
(
"zones/%s/instances/%s"
,
targetInstance
.
Zone
,
targetInstance
.
Name
),
NextHopInstance
:
fmt
.
Sprintf
(
"zones/%s/instances/%s"
,
targetInstance
.
Zone
,
targetInstance
.
Name
),
Network
:
gce
.
networkURL
,
Network
:
gce
.
NetworkURL
()
,
Priority
:
1000
,
Priority
:
1000
,
Description
:
k8sNodeRouteTag
,
Description
:
k8sNodeRouteTag
,
})
.
Do
()
})
.
Do
()
...
@@ -96,16 +96,16 @@ func (gce *GCECloud) CreateRoute(clusterName string, nameHint string, route *clo
...
@@ -96,16 +96,16 @@ func (gce *GCECloud) CreateRoute(clusterName string, nameHint string, route *clo
return
mc
.
Observe
(
err
)
return
mc
.
Observe
(
err
)
}
}
}
}
return
gce
.
waitForGlobalOp
(
insertOp
,
mc
)
return
gce
.
waitForGlobalOp
InProject
(
insertOp
,
gce
.
NetworkProjectID
()
,
mc
)
}
}
func
(
gce
*
GCECloud
)
DeleteRoute
(
clusterName
string
,
route
*
cloudprovider
.
Route
)
error
{
func
(
gce
*
GCECloud
)
DeleteRoute
(
clusterName
string
,
route
*
cloudprovider
.
Route
)
error
{
mc
:=
newRoutesMetricContext
(
"delete"
)
mc
:=
newRoutesMetricContext
(
"delete"
)
deleteOp
,
err
:=
gce
.
service
.
Routes
.
Delete
(
gce
.
projectID
,
route
.
Name
)
.
Do
()
deleteOp
,
err
:=
gce
.
service
.
Routes
.
Delete
(
gce
.
NetworkProjectID
()
,
route
.
Name
)
.
Do
()
if
err
!=
nil
{
if
err
!=
nil
{
return
mc
.
Observe
(
err
)
return
mc
.
Observe
(
err
)
}
}
return
gce
.
waitForGlobalOp
(
deleteOp
,
mc
)
return
gce
.
waitForGlobalOp
InProject
(
deleteOp
,
gce
.
NetworkProjectID
()
,
mc
)
}
}
func
truncateClusterName
(
clusterName
string
)
string
{
func
truncateClusterName
(
clusterName
string
)
string
{
...
...
pkg/cloudprovider/providers/gce/gce_test.go
View file @
0028385e
This diff is collapsed.
Click to expand it.
test/e2e/e2e.go
View file @
0028385e
...
@@ -84,8 +84,8 @@ func setupProviderConfig() error {
...
@@ -84,8 +84,8 @@ func setupProviderConfig() error {
Region
:
region
,
Region
:
region
,
Zone
:
zone
,
Zone
:
zone
,
ManagedZones
:
managedZones
,
ManagedZones
:
managedZones
,
Network
URL
:
""
,
Network
Name
:
""
,
// TODO: Change this to use framework.TestContext.CloudConfig.Network?
Subnetwork
URL
:
""
,
Subnetwork
Name
:
""
,
NodeTags
:
nil
,
NodeTags
:
nil
,
NodeInstancePrefix
:
""
,
NodeInstancePrefix
:
""
,
TokenSource
:
nil
,
TokenSource
:
nil
,
...
...
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