Skip to content

Commit bd2890e

Browse files
committed
Fixes #297
1 parent 8a4b0fd commit bd2890e

File tree

10 files changed

+75
-47
lines changed

10 files changed

+75
-47
lines changed

actions/reconfigure_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ backend %s-be%s
489489
}
490490

491491
func (s ReconfigureTestSuite) Test_Execute_WritesBeTemplateWithRedirectToHttps_WhenHttpsOnlyIsTrue() {
492-
s.reconfigure.HttpsOnly = true
492+
s.reconfigure.ServiceDest[0].HttpsOnly = true
493493
var actualFilename, actualData string
494494
expectedFilename := fmt.Sprintf("%s/%s-be.cfg", s.TemplatesPath, s.ServiceName)
495495
expectedData := fmt.Sprintf(

docker-compose-test.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,13 @@ services:
1010
environment:
1111
- HOST_IP=${HOST_IP}
1212
- DOCKER_HUB_USER=${DOCKER_HUB_USER}
13+
14+
staging-swarm-local:
15+
image: vfarcic/docker-flow-proxy-test
16+
volumes:
17+
- /var/run/docker.sock:/var/run/docker.sock
18+
- .:/src
19+
environment:
20+
- HOST_IP=${HOST_IP}
21+
- DOCKER_HUB_USER=${DOCKER_HUB_USER}
22+
command: ["sh", "-c", "chmod +x /src/run-tests.sh && /src/run-tests.sh"]

docs/usage.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ The following query parameters can be used to send a *reconfigure* request to *D
3737
|timeoutTunnel |The tunnel timeout in seconds.<br>**Example:** `3600` |3600 |
3838
|xForwardedProto|Whether to add "X-Forwarded-Proto https" header.<br>**Example:** `true` |false |
3939

40-
Multiple destinations for a single service can be specified by adding index as a suffix to `servicePath`, `srcPort`, `port`, `userAgent`, `ignoreAuthorization`, `serviceDomain``allowedMethods`, `deniedMethods`, `denyHttp` or `ReqMode` parameters. In that case, `srcPort` is required.
40+
Multiple destinations for a single service can be specified by adding index as a suffix to `servicePath`, `srcPort`, `port`, `userAgent`, `ignoreAuthorization`, `serviceDomain``allowedMethods`, `deniedMethods`, `denyHttp`, `httpsOnly`, or `ReqMode` parameters. In that case, `srcPort` is required.
4141

4242
### HTTP Mode Query Parameters
4343

@@ -48,7 +48,7 @@ The following query parameters can be used only when `reqMode` is set to `http`
4848
|allowedMethods|The list of allowed methods. If specified, a request with a method that is not on the list will be denied. Multiple methods can be separated with comma (`,`). The parameter can be prefixed with an index thus allowing definition of multiple destinations for a single service (e.g. `allowedMethods.1`, `allowedMethods.2`, and so on).<br>**Example:** `GET,DELETE`|
4949
|deniedMethods|The list of denied methods. If specified, a request with a method that is on the list will be denied. Multiple methods can be separated with comma (`,`). The parameter can be prefixed with an index thus allowing definition of multiple destinations for a single service (e.g. `deniedMethods.1`, `deniedMethods.2`, and so on).<br>**Example:** `PUT,POST`|
5050
|denyHttp |Whether to deny HTTP requests thus allowing only HTTPS. The parameter can be prefixed with an index thus allowing definition of multiple destinations for a single service (e.g. `denyHttp.1`, `denyHttp.2`, and so on).<br>**Example:** `true`<br>**Default Value:** `false`|
51-
|httpsOnly |If set to true, HTTP requests to the service will be redirected to HTTPS.<br>**Example:** `true`<br>**Default Value:** `false`|
51+
|httpsOnly |If set to true, HTTP requests to the service will be redirected to HTTPS. The parameter can be prefixed with an index thus allowing definition of multiple destinations for a single service (e.g. `httpsOnly.1`, `httpsOnly.2`, and so on).<br>**Example:** `true`<br>**Default Value:** `false`|
5252
|outboundHostname|The hostname where the service is running, for instance on a separate swarm. If specified, the proxy will dispatch requests to that domain.<br>**Example:** `ecme.com`|
5353
|pathType |The ACL derivative. Defaults to *path_beg*. See [HAProxy path](https://cbonte.github.io/haproxy-dconv/configuration-1.5.html#7.3.6-path) for more info.<br>**Example:** `path_beg`|
5454
|redirectWhenHttpProto|Whether to redirect to https when X-Forwarded-Proto is set and the request is made over an HTTP port.<br>**Example:** `true`<br>**Default Value:** `false`|
@@ -66,7 +66,7 @@ The following query parameters can be used only when `reqMode` is set to `http`
6666
|usersPassEncrypted|Indicates whether passwords provided by `users` or `usersSecret` contain encrypted data. Passwords can be encrypted with the command `mkpasswd -m sha-512 password1`.<br>**Example:** `true`<br>**Default Value:** `false`|
6767
|verifyClientSsl|Whether to verify client SSL and, if it is not valid, deny request and return 403 Forbidden status code. SSL is validated against the `ca-file` specified through the environment variable `CA_FILE`.<br>**Example:** true<br>**Default Value:** `false`|
6868

69-
Multiple destinations for a single service can be specified by adding index as a suffix to `servicePath`, `srcPort`, `port`, `userAgent`, `ignoreAuthorization`, `serviceDomain`, `allowedMethods`, `deniedMethods`, `denyHttp` or `ReqMode` parameters. In that case, `srcPort` is required.
69+
Multiple destinations for a single service can be specified by adding index as a suffix to `servicePath`, `srcPort`, `port`, `userAgent`, `ignoreAuthorization`, `serviceDomain`, `allowedMethods`, `deniedMethods`, `denyHttp`, `httpsOnly`, or `ReqMode` parameters. In that case, `srcPort` is required.
7070

7171
### TCP Mode HTTP Query Parameters
7272

integration_tests/integration_swarm_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ func (s IntegrationSwarmTestSuite) Test_Domain() {
9494
client := new(http.Client)
9595
url := fmt.Sprintf("http://%s/demo/hello", s.hostIP)
9696
req, err := http.NewRequest("GET", url, nil)
97+
s.NoError(err)
9798
req.Host = "my-domain.com"
9899
resp, err := client.Do(req)
99100

proxy/ha_proxy_test.go

Lines changed: 1 addition & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,37 +1432,7 @@ func (s HaProxyTestSuite) Test_CreateConfigFromTemplates_AddsContentFrontEndWith
14321432
s.Equal(expectedData, actualData)
14331433
}
14341434

1435-
func (s HaProxyTestSuite) Test_CreateConfigFromTemplates_ForwardsToHttps_WhenHttpsOnlyIsTrue() {
1436-
var actualData string
1437-
tmpl := s.TemplateContent
1438-
expectedData := fmt.Sprintf(
1439-
`%s
1440-
acl url_my-service1111 path_beg /path
1441-
use_backend my-service-be1111 if url_my-service1111%s`,
1442-
tmpl,
1443-
s.ServicesContent,
1444-
)
1445-
writeFile = func(filename string, data []byte, perm os.FileMode) error {
1446-
actualData = string(data)
1447-
return nil
1448-
}
1449-
p := NewHaProxy(s.TemplatesPath, s.ConfigsPath)
1450-
dataInstance.Services["my-service"] = Service{
1451-
ServiceName: "my-service",
1452-
PathType: "path_beg",
1453-
HttpsOnly: true,
1454-
AclName: "my-service",
1455-
ServiceDest: []ServiceDest{
1456-
{Port: "1111", ServicePath: []string{"/path"}},
1457-
},
1458-
}
1459-
1460-
p.CreateConfigFromTemplates()
1461-
1462-
s.Equal(expectedData, actualData)
1463-
}
1464-
1465-
func (s HaProxyTestSuite) Test_CreateConfigFromTemplates_ForwardsToHttpsWhenRedirectWhenHttpProtoIsTrue() {
1435+
func (s HaProxyTestSuite) Test_CreateConfigFromTemplates_ForwardsToHttps_WhenRedirectWhenHttpProtoIsTrue() {
14661436
var actualData string
14671437
tmpl := s.TemplateContent
14681438
expectedData := fmt.Sprintf(

proxy/template.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ backend {{$.ServiceName}}-be{{.Port}}
146146
{{- if .DenyHttp}}
147147
http-request deny if !{ ssl_fc }
148148
{{- end}}
149-
{{- if $.HttpsOnly}}
149+
{{- if .HttpsOnly}}
150150
redirect scheme https if !{ ssl_fc }
151151
{{- end}}
152152
server {{$.ServiceName}} {{$.Host}}:{{.Port}}{{if eq $.CheckResolvers true}} check resolvers docker{{end}}{{if eq $.SslVerifyNone true}} ssl verify none{{end}}

proxy/types.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ type ServiceDest struct {
1616
DeniedMethods []string
1717
// Whether to deny HTTP requests thus allowing only HTTPS.
1818
DenyHttp bool
19+
// Whether to redirect all http requests to https
20+
HttpsOnly bool
1921
// Whether to ignore authorization for this service destination.
2022
IgnoreAuthorization bool
2123
// The internal port of a service that should be reconfigured.
@@ -87,8 +89,6 @@ type Service struct {
8789
// Whether to distribute a request to all the instances of the proxy.
8890
// Used only in the swarm mode.
8991
Distribute bool `split_words:"true"`
90-
// Whether to redirect all http requests to https
91-
HttpsOnly bool `split_words:"true"`
9292
// The internal HTTPS port of a service that should be reconfigured.
9393
// The port is used only in the swarm mode.
9494
// If not specified, the `port` parameter will be used instead.
@@ -311,6 +311,7 @@ func getServiceDestList(sr *Service, provider ServiceParameterProvider) []Servic
311311
} else {
312312
serviceDomain = sd.ServiceDomain
313313
}
314+
httpsOnly := sd.HttpsOnly
314315
for i := 1; i <= 10; i++ {
315316
sd := getServiceDest(sr, provider, i)
316317
if isServiceDestValid(&sd) {
@@ -332,6 +333,9 @@ func getServiceDestList(sr *Service, provider ServiceParameterProvider) []Servic
332333
} else if len(sd.ServiceDomain) == 0 && len(serviceDomain) > 0 {
333334
sdList[i].ServiceDomain = serviceDomain
334335
}
336+
if httpsOnly && !sd.HttpsOnly {
337+
sdList[i].HttpsOnly = true
338+
}
335339
}
336340
return sdList
337341
}
@@ -365,6 +369,7 @@ func getServiceDest(sr *Service, provider ServiceParameterProvider, index int) S
365369
AllowedMethods: getSliceFromString(provider, fmt.Sprintf("allowedMethods%s", suffix)),
366370
DeniedMethods: getSliceFromString(provider, fmt.Sprintf("deniedMethods%s", suffix)),
367371
DenyHttp: getBoolParam(provider, fmt.Sprintf("denyHttp%s", suffix)),
372+
HttpsOnly: getBoolParam(provider, fmt.Sprintf("httpsOnly%s", suffix)),
368373
IgnoreAuthorization: getBoolParam(provider, fmt.Sprintf("ignoreAuthorization%s", suffix)),
369374
Port: provider.GetString(fmt.Sprintf("port%s", suffix)),
370375
ReqMode: reqMode,

proxy/types_test.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,33 @@ func (s *TypesTestSuite) Test_GetServiceFromProvider_MovesServiceDomainToIndexed
207207
s.Equal(expected, *actual)
208208
}
209209

210+
func (s *TypesTestSuite) Test_GetServiceFromProvider_MovesHttpsOnlyToIndexedEntries_WhenEmpty() {
211+
expected := Service{
212+
ServiceDest: []ServiceDest{{
213+
AllowedMethods: []string{},
214+
DeniedMethods: []string{},
215+
HttpsOnly: true,
216+
ServiceDomain: []string{},
217+
ServiceHeader: map[string]string{},
218+
ServicePath: []string{"/"},
219+
Port: "1234",
220+
ReqMode: "reqMode",
221+
}},
222+
ServiceName: "serviceName",
223+
}
224+
serviceMap := map[string]string{
225+
// "serviceDomain": strings.Join(expected.ServiceDest[0].ServiceDomain, ","),
226+
"httpsOnly": strconv.FormatBool(expected.ServiceDest[0].HttpsOnly),
227+
"serviceName": expected.ServiceName,
228+
"port.1": expected.ServiceDest[0].Port,
229+
"reqMode.1": expected.ServiceDest[0].ReqMode,
230+
"servicePath.1": strings.Join(expected.ServiceDest[0].ServicePath, ","),
231+
}
232+
provider := mapParameterProvider{&serviceMap}
233+
actual := GetServiceFromProvider(&provider)
234+
s.Equal(expected, *actual)
235+
}
236+
210237
// Suite
211238

212239
func TestRunUnitTestSuite(t *testing.T) {
@@ -229,7 +256,6 @@ func (s *TypesTestSuite) getServiceMap(expected Service, indexSuffix string) map
229256
"delReqHeader": strings.Join(expected.DelReqHeader, ","),
230257
"delResHeader": strings.Join(expected.DelResHeader, ","),
231258
"distribute": strconv.FormatBool(expected.Distribute),
232-
"httpsOnly": strconv.FormatBool(expected.HttpsOnly),
233259
"httpsPort": strconv.Itoa(expected.HttpsPort),
234260
"isDefaultBackend": strconv.FormatBool(expected.IsDefaultBackend),
235261
"outboundHostname": expected.OutboundHostname,
@@ -254,6 +280,7 @@ func (s *TypesTestSuite) getServiceMap(expected Service, indexSuffix string) map
254280
"allowedMethods" + indexSuffix: strings.Join(expected.ServiceDest[0].AllowedMethods, ","),
255281
"deniedMethods" + indexSuffix: strings.Join(expected.ServiceDest[0].DeniedMethods, ","),
256282
"denyHttp" + indexSuffix: strconv.FormatBool(expected.ServiceDest[0].DenyHttp),
283+
"httpsOnly" + indexSuffix: strconv.FormatBool(expected.ServiceDest[0].HttpsOnly),
257284
"ignoreAuthorization" + indexSuffix: strconv.FormatBool(expected.ServiceDest[0].IgnoreAuthorization),
258285
"port" + indexSuffix: expected.ServiceDest[0].Port,
259286
"reqMode" + indexSuffix: expected.ServiceDest[0].ReqMode,
@@ -274,7 +301,6 @@ func (s *TypesTestSuite) getExpectedService() Service {
274301
DelReqHeader: []string{"del-header-1", "del-header-2"},
275302
DelResHeader: []string{"del-header-1", "del-header-2"},
276303
Distribute: true,
277-
HttpsOnly: true,
278304
HttpsPort: 1234,
279305
IsDefaultBackend: true,
280306
OutboundHostname: "outboundHostname",
@@ -288,6 +314,7 @@ func (s *TypesTestSuite) getExpectedService() Service {
288314
AllowedMethods: []string{"GET", "DELETE"},
289315
DeniedMethods: []string{"PUT", "POST"},
290316
DenyHttp: true,
317+
HttpsOnly: true,
291318
IgnoreAuthorization: true,
292319
ServiceDomain: []string{"domain1", "domain2"},
293320
ServiceHeader: map[string]string{"X-Version": "3", "name": "Viktor"},

server/server.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,10 +278,12 @@ func (m *serve) getServiceFromEnvVars(prefix string) (proxy.Service, error) {
278278
if len(reqMode) == 0 {
279279
reqMode = "http"
280280
}
281+
httpsOnly, _ := strconv.ParseBool(os.Getenv(prefix+"_HTTPS_ONLY"))
281282
if len(path) > 0 || len(port) > 0 {
282283
sd = append(
283284
sd,
284285
proxy.ServiceDest{
286+
HttpsOnly: httpsOnly,
285287
Port: port,
286288
ReqMode: reqMode,
287289
ServiceDomain: domain,
@@ -294,14 +296,21 @@ func (m *serve) getServiceFromEnvVars(prefix string) (proxy.Service, error) {
294296
port := os.Getenv(fmt.Sprintf("%s_PORT_%d", prefix, i))
295297
path := os.Getenv(fmt.Sprintf("%s_SERVICE_PATH_%d", prefix, i))
296298
reqMode := os.Getenv(fmt.Sprintf("%s_REQ_MODE_%d", prefix, i))
299+
httpsOnly, _ := strconv.ParseBool(os.Getenv(fmt.Sprintf("%s_HTTPS_ONLY_%d", prefix, i)))
297300
if len(reqMode) == 0 {
298301
reqMode = "http"
299302
}
300303
srcPort, _ := strconv.Atoi(os.Getenv(fmt.Sprintf("%s_SRC_PORT_%d", prefix, i)))
301304
if len(path) > 0 && len(port) > 0 {
302305
sd = append(
303306
sd,
304-
proxy.ServiceDest{Port: port, SrcPort: srcPort, ServicePath: strings.Split(path, ","), ReqMode: reqMode},
307+
proxy.ServiceDest{
308+
HttpsOnly: httpsOnly,
309+
Port: port,
310+
SrcPort: srcPort,
311+
ServicePath: strings.Split(path, ","),
312+
ReqMode: reqMode,
313+
},
305314
)
306315
} else {
307316
break

server/server_test.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,6 @@ func (s *ServerTestSuite) Test_GetServiceFromUrl_ReturnsProxyService() {
523523
DelReqHeader: []string{"add-header-1", "add-header-2"},
524524
DelResHeader: []string{"add-header-1", "add-header-2"},
525525
Distribute: true,
526-
HttpsOnly: true,
527526
HttpsPort: 1234,
528527
OutboundHostname: "outboundHostname",
529528
PathType: "pathType",
@@ -534,6 +533,7 @@ func (s *ServerTestSuite) Test_GetServiceFromUrl_ReturnsProxyService() {
534533
ServiceDest: []proxy.ServiceDest{{
535534
AllowedMethods: []string{"GET", "DELETE"},
536535
DeniedMethods: []string{"PUT", "POST"},
536+
HttpsOnly: true,
537537
Port: "1234",
538538
ReqMode: "reqMode",
539539
ServiceDomain: []string{"domain1", "domain2"},
@@ -570,7 +570,7 @@ func (s *ServerTestSuite) Test_GetServiceFromUrl_ReturnsProxyService() {
570570
expected.TimeoutServer,
571571
expected.TimeoutTunnel,
572572
expected.ServiceDest[0].ReqMode,
573-
expected.HttpsOnly,
573+
expected.ServiceDest[0].HttpsOnly,
574574
expected.IsDefaultBackend,
575575
expected.XForwardedProto,
576576
expected.RedirectWhenHttpProto,
@@ -654,7 +654,6 @@ func (s *ServerTestSuite) Test_GetServicesFromEnvVars_ReturnsServices() {
654654
DelReqHeader: []string{"del-header-1", "del-header-2"},
655655
DelResHeader: []string{"del-header-1", "del-header-2"},
656656
Distribute: true,
657-
HttpsOnly: true,
658657
HttpsPort: 1234,
659658
IsDefaultBackend: true,
660659
OutboundHostname: "my-OutboundHostname",
@@ -675,6 +674,7 @@ func (s *ServerTestSuite) Test_GetServicesFromEnvVars_ReturnsServices() {
675674
XForwardedProto: true,
676675
ServiceDest: []proxy.ServiceDest{
677676
{
677+
HttpsOnly: true,
678678
Port: "1111",
679679
ServiceDomain: []string{"my-domain-1.com", "my-domain-2.com"},
680680
ServicePath: []string{"my-path-11", "my-path-12"},
@@ -690,7 +690,7 @@ func (s *ServerTestSuite) Test_GetServicesFromEnvVars_ReturnsServices() {
690690
os.Setenv("DFP_SERVICE_DEL_REQ_HEADER", strings.Join(service.DelReqHeader, ","))
691691
os.Setenv("DFP_SERVICE_DEL_RES_HEADER", strings.Join(service.DelResHeader, ","))
692692
os.Setenv("DFP_SERVICE_DISTRIBUTE", strconv.FormatBool(service.Distribute))
693-
os.Setenv("DFP_SERVICE_HTTPS_ONLY", strconv.FormatBool(service.HttpsOnly))
693+
os.Setenv("DFP_SERVICE_HTTPS_ONLY", strconv.FormatBool(service.ServiceDest[0].HttpsOnly))
694694
os.Setenv("DFP_SERVICE_HTTPS_PORT", strconv.Itoa(service.HttpsPort))
695695
os.Setenv("DFP_SERVICE_IS_DEFAULT_BACKEND", strconv.FormatBool(service.IsDefaultBackend))
696696
os.Setenv("DFP_SERVICE_OUTBOUND_HOSTNAME", service.OutboundHostname)
@@ -789,34 +789,40 @@ func (s *ServerTestSuite) Test_GetServicesFromEnvVars_ReturnsServicesWithIndexed
789789
service := proxy.Service{
790790
ServiceName: "my-ServiceName",
791791
ServiceDest: []proxy.ServiceDest{
792-
{Port: "1111", ServicePath: []string{"my-path-11", "my-path-12"}, SrcPort: 1112},
793-
{Port: "2221", ServicePath: []string{"my-path-21", "my-path-22"}, SrcPort: 2222},
792+
{Port: "1111", ServicePath: []string{"my-path-11", "my-path-12"}, SrcPort: 1112, HttpsOnly: true},
793+
{Port: "2221", ServicePath: []string{"my-path-21", "my-path-22"}, SrcPort: 2222, HttpsOnly: false},
794794
},
795795
}
796796
os.Setenv("DFP_SERVICE_SERVICE_NAME", service.ServiceName)
797+
os.Setenv("DFP_SERVICE_HTTPS_ONLY_1", "true")
797798
os.Setenv("DFP_SERVICE_PORT_1", service.ServiceDest[0].Port)
798799
os.Setenv("DFP_SERVICE_SERVICE_PATH_1", strings.Join(service.ServiceDest[0].ServicePath, ","))
799800
os.Setenv("DFP_SERVICE_SRC_PORT_1", strconv.Itoa(service.ServiceDest[0].SrcPort))
801+
os.Setenv("DFP_SERVICE_HTTPS_ONLY_2", "false")
800802
os.Setenv("DFP_SERVICE_PORT_2", service.ServiceDest[1].Port)
801803
os.Setenv("DFP_SERVICE_SERVICE_PATH_2", strings.Join(service.ServiceDest[1].ServicePath, ","))
802804
os.Setenv("DFP_SERVICE_SRC_PORT_2", strconv.Itoa(service.ServiceDest[1].SrcPort))
803805

804806
defer func() {
805807
os.Unsetenv("DFP_SERVICE_SERVICE_NAME")
808+
os.Unsetenv("DFP_SERVICE_HTTPS_ONLY_1")
806809
os.Unsetenv("DFP_SERVICE_PORT_1")
807810
os.Unsetenv("DFP_SERVICE_SERVICE_PATH_1")
808811
os.Unsetenv("DFP_SERVICE_SRC_PORT_1")
812+
os.Unsetenv("DFP_SERVICE_HTTPS_ONLY_2")
809813
os.Unsetenv("DFP_SERVICE_PORT_2")
810814
os.Unsetenv("DFP_SERVICE_SERVICE_PATH_2")
811815
os.Unsetenv("DFP_SERVICE_SRC_PORT_2")
812816
}()
813817
srv := serve{}
818+
println("000")
814819
actual := srv.GetServicesFromEnvVars()
815820

816821
service.ServiceDest[0].ReqMode = "http"
817822
service.ServiceDest[1].ReqMode = "http"
818823
s.Len(*actual, 1)
819824
s.Contains(*actual, service)
825+
println("999")
820826
}
821827

822828
func (s *ServerTestSuite) Test_GetServicesFromEnvVars_ReturnsEmptyIfServiceNameIsNotSet() {

0 commit comments

Comments
 (0)