Repository: dragonflyoss/Dragonfly2
Pull request #822 (2021-11-23 14:31:55)

feat: add package reachable
Signed-off-by: Gaius <gaius.qi@gmail.com>

<!--- Provide a general summary of your changes in the Title above -->

## Description
- Add package `reachable`
- Dfdaemon filters unavailable scheduler addresses.
<!--- Describe your changes in detail -->

## Related Issue

<!--- This project only accepts pull requests related to open issues -->
<!--- If suggesting a new feature or change, please discuss it in an issue first -->
<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
<!--- Please link to the issue here: -->

## Motivation and Context

<!--- Why is this change required? What problem does it solve? -->

## Screenshots (if appropriate):

## Types of changes

<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Documentation Update (if none of the other choices apply)

## Checklist:

<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->

- [x] Code compiles correctly.
- [x] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [x] I have read the **CONTRIBUTING** document.
- [x] I have added tests to cover my changes.
- [x] All new and existing tests passed.


codecov-commenter: # [Codecov](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/822?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) Report
> Merging [#822](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/822?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) (34d4cca) into [main](https://codecov.io/gh/dragonflyoss/Dragonfly2/commit/28587a196bae140a4a6cf441ca6b5f2e340920cb?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) (28587a1) will **increase** coverage by `0.41%`.
> The diff coverage is `82.05%`.

[![Impacted file tree graph](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/822/graphs/tree.svg?width=650&height=150&src=pr&token=PUICJYSAT7&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss)](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/822?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss)

```diff
@@            Coverage Diff             @@
##             main     #822      +/-   ##
==========================================
+ Coverage   40.76%   41.17%   +0.41%     
==========================================
  Files         101      102       +1     
  Lines        8032     8066      +34     
==========================================
+ Hits         3274     3321      +47     
+ Misses       4348     4335      -13     
  Partials      410      410              
```

| Flag | Coverage Δ | |
|---|---|---|
| Object-compatibility-e2etests | `?` | |
| e2etests | `?` | |
| unittests | `41.17% <82.05%> (+0.41%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/822?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) | Coverage Δ | |
|---|---|---|
| [client/config/dynconfig.go](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/822/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#diff-Y2xpZW50L2NvbmZpZy9keW5jb25maWcuZ28=) | `56.66% <ø> (ø)` | |
| [client/daemon/daemon.go](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/822/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#diff-Y2xpZW50L2RhZW1vbi9kYWVtb24uZ28=) | `5.66% <68.18%> (+5.66%)` | :arrow_up: |
| [pkg/reachable/reachable.go](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/822/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#diff-cGtnL3JlYWNoYWJsZS9yZWFjaGFibGUuZ28=) | `100.00% <100.00%> (ø)` | |
| [client/daemon/peer/peertask\_base.go](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/822/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#diff-Y2xpZW50L2RhZW1vbi9wZWVyL3BlZXJ0YXNrX2Jhc2UuZ28=) | `46.66% <0.00%> (+0.60%)` | :arrow_up: |
| [client/daemon/peer/peertask\_stream.go](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/822/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#diff-Y2xpZW50L2RhZW1vbi9wZWVyL3BlZXJ0YXNrX3N0cmVhbS5nbw==) | `55.35% <0.00%> (+1.52%)` | :arrow_up: |
| [pkg/util/net/iputils/ip\_utils.go](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/822/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#diff-cGtnL3V0aWwvbmV0L2lwdXRpbHMvaXBfdXRpbHMuZ28=) | `62.29% <0.00%> (+3.36%)` | :arrow_up: |


Patch:
diff --git a/client/config/dynconfig.go b/client/config/dynconfig.go
index 5f458681..7fa724d1 100644
--- a/client/config/dynconfig.go
+++ b/client/config/dynconfig.go
@@ -34,7 +34,7 @@ var (
 	cachePath = filepath.Join(dfpath.DefaultCacheDir, "daemon_dynconfig")
 
 	// Watch dynconfig interval
-	watchInterval = 5 * time.Second
+	watchInterval = 10 * time.Second
 )
 
 type DynconfigData struct {
diff --git a/client/daemon/daemon.go b/client/daemon/daemon.go
index 104a9a44..af27d24d 100644
--- a/client/daemon/daemon.go
+++ b/client/daemon/daemon.go
@@ -49,6 +49,7 @@ import (
 	"d7y.io/dragonfly/v2/internal/dfpath"
 	"d7y.io/dragonfly/v2/internal/idgen"
 	"d7y.io/dragonfly/v2/pkg/basic/dfnet"
+	"d7y.io/dragonfly/v2/pkg/reachable"
 	"d7y.io/dragonfly/v2/pkg/rpc"
 	"d7y.io/dragonfly/v2/pkg/rpc/manager"
 	managerclient "d7y.io/dragonfly/v2/pkg/rpc/manager/client"
@@ -84,7 +85,7 @@ type clientDaemon struct {
 	PieceManager    peer.PieceManager
 
 	dynconfig       config.Dynconfig
-	schedulerAddrs  []dfnet.NetAddr
+	schedulers      []*manager.Scheduler
 	schedulerClient schedulerclient.SchedulerClient
 }
 
@@ -102,6 +103,7 @@ func New(opt *config.DaemonOption) (Daemon, error) {
 	}
 
 	var addrs []dfnet.NetAddr
+	var schedulers []*manager.Scheduler
 	var dynconfig config.Dynconfig
 	if opt.Scheduler.Manager.Enable == true {
 		// New manager client
@@ -119,12 +121,11 @@ func New(opt *config.DaemonOption) (Daemon, error) {
 		}
 
 		// Get schedulers from manager
-		schedulers, err := dynconfig.GetSchedulers()
-		if err != nil {
+		if schedulers, err = dynconfig.GetSchedulers(); err != nil {
 			return nil, err
 		}
 
-		addrs = schedulersToNetAddrs(schedulers)
+		addrs = schedulersToAvailableNetAddrs(schedulers)
 	} else {
 		addrs = opt.Scheduler.NetAddrs
 	}
@@ -219,7 +220,7 @@ func New(opt *config.DaemonOption) (Daemon, error) {
 		StorageManager:  storageManager,
 		GCManager:       gc.NewManager(opt.GCInterval.Duration),
 		dynconfig:       dynconfig,
-		schedulerAddrs:  addrs,
+		schedulers:      schedulers,
 		schedulerClient: sched,
 	}, nil
 }
@@ -517,24 +518,45 @@ func (cd *clientDaemon) Stop() {
 }
 
 func (cd *clientDaemon) OnNotify(data *config.DynconfigData) {
-	addrs := schedulersToNetAddrs(data.Schedulers)
-	if reflect.DeepEqual(cd.schedulerAddrs, addrs) {
+	if reflect.DeepEqual(cd.schedulers, data.Schedulers) {
 		return
 	}
 
+	// Get the available scheduler addresses and use ip first
+	addrs := schedulersToAvailableNetAddrs(data.Schedulers)
+
 	// Update scheduler client addresses
 	cd.schedulerClient.UpdateState(addrs)
-	cd.schedulerAddrs = addrs
+	cd.schedulers = data.Schedulers
 }
 
-// schedulersToNetAddrs coverts []*manager.Scheduler to []dfnet.NetAddr.
-func schedulersToNetAddrs(schedulers []*manager.Scheduler) []dfnet.NetAddr {
+// schedulersToAvailableNetAddrs coverts []*manager.Scheduler to available []dfnet.NetAddr.
+func schedulersToAvailableNetAddrs(schedulers []*manager.Scheduler) []dfnet.NetAddr {
 	netAddrs := make([]dfnet.NetAddr, 0, len(schedulers))
 	for _, scheduler := range schedulers {
-		netAddrs = append(netAddrs, dfnet.NetAddr{
-			Type: dfnet.TCP,
-			Addr: fmt.Sprintf("%s:%d", scheduler.HostName, scheduler.Port),
-		})
+		// Check whether the ip can be reached
+		ipReachable := reachable.New(&reachable.Config{Address: fmt.Sprintf("%s:%d", scheduler.Ip, scheduler.Port)})
+		if err := ipReachable.Check(); err != nil {
+			logger.Warnf("scheduler address %s:%d is unreachable", scheduler.Ip, scheduler.Port)
+		} else {
+			netAddrs = append(netAddrs, dfnet.NetAddr{
+				Type: dfnet.TCP,
+				Addr: fmt.Sprintf("%s:%d", scheduler.Ip, scheduler.Port),
+			})
+
+			continue
+		}
+
+		// Check whether the host can be reached
+		hostReachable := reachable.New(&reachable.Config{Address: fmt.Sprintf("%s:%d", scheduler.HostName, scheduler.Port)})
+		if err := hostReachable.Check(); err != nil {
+			logger.Warnf("scheduler address %s:%d is unreachable", scheduler.HostName, scheduler.Port)
+		} else {
+			netAddrs = append(netAddrs, dfnet.NetAddr{
+				Type: dfnet.TCP,
+				Addr: fmt.Sprintf("%s:%d", scheduler.HostName, scheduler.Port),
+			})
+		}
 	}
 
 	return netAddrs
diff --git a/docs/en/deployment/configuration/cdn.yaml b/docs/en/deployment/configuration/cdn.yaml
index 114a39bd..4cd2f8aa 100644
--- a/docs/en/deployment/configuration/cdn.yaml
+++ b/docs/en/deployment/configuration/cdn.yaml
@@ -55,6 +55,11 @@ base:
     location:
     idc:
 
+  # enable prometheus metrics
+  # metrics:
+  #  # metrics service address
+  #  addr: ":8000"
+
 plugins:
   storagedriver:
     - name: disk
diff --git a/docs/en/deployment/configuration/manager.yaml b/docs/en/deployment/configuration/manager.yaml
index 136fac28..7faf208a 100644
--- a/docs/en/deployment/configuration/manager.yaml
+++ b/docs/en/deployment/configuration/manager.yaml
@@ -34,6 +34,7 @@ database:
     host: dragonfly
     port: 6379
     db: 0
+
 # manager server cache
 # cache:
 #   # redis cache configure
@@ -46,3 +47,8 @@ database:
 #     size: 10000
 #     # cache ttl configure
 #     ttl: 30s
+
+# enable prometheus metrics
+# metrics:
+#  # metrics service address
+#  addr: ":8000"
diff --git a/docs/en/deployment/configuration/scheduler.yaml b/docs/en/deployment/configuration/scheduler.yaml
index e20b0917..6657c208 100644
--- a/docs/en/deployment/configuration/scheduler.yaml
+++ b/docs/en/deployment/configuration/scheduler.yaml
@@ -97,11 +97,11 @@ job:
     # backendDB
     backendDB: ""
 
-# Whether to disable CDN
+# whether to disable CDN
 # default: false
 disableCDN: false
 
-# Console shows log on console
+# console shows log on console
 # default: false
 console: false
 
@@ -120,3 +120,8 @@ jaeger: ""
 # service name used in tracer
 # default: dragonfly-scheduler
 service-name: dragonfly-scheduler
+
+# enable prometheus metrics
+# metrics:
+#  # metrics service address
+#  addr: ":8000"
diff --git a/docs/zh-CN/deployment/configuration/cdn.yaml b/docs/zh-CN/deployment/configuration/cdn.yaml
index 859b5aa1..91827d89 100644
--- a/docs/zh-CN/deployment/configuration/cdn.yaml
+++ b/docs/zh-CN/deployment/configuration/cdn.yaml
@@ -62,6 +62,11 @@ base:
     # IDC(Internet Data Center)，互联网数据中心
     idc: ""
 
+  # 开启数据收集服务
+  # metrics:
+  #  # 数据服务地址
+  #  addr: ":8000"
+
 plugins:
   storagedriver:
     - name: disk
diff --git a/docs/zh-CN/deployment/configuration/manager.yaml b/docs/zh-CN/deployment/configuration/manager.yaml
index 96afa9ca..60930e48 100644
--- a/docs/zh-CN/deployment/configuration/manager.yaml
+++ b/docs/zh-CN/deployment/configuration/manager.yaml
@@ -32,6 +32,7 @@ database:
     host: dragonfly
     port: 6379
     db: 0
+
 # 缓存配置
 # cache:
 #   # redis 缓存配置
@@ -44,3 +45,8 @@ database:
 #     size: 10000
 #     # ttl 配置
 #     ttl: 30s
+
+# 开启数据收集服务
+# metrics:
+#  # 数据服务地址
+#  addr: ":8000"
diff --git a/docs/zh-CN/deployment/configuration/scheduler.yaml b/docs/zh-CN/deployment/configuration/scheduler.yaml
index 19bfe631..1d6108ad 100644
--- a/docs/zh-CN/deployment/configuration/scheduler.yaml
+++ b/docs/zh-CN/deployment/configuration/scheduler.yaml
@@ -123,3 +123,8 @@ jaeger: ""
 # tracer 中使用的 service-name
 # 默认值：dragonfly-cdn
 service-name: dragonfly-scheduler
+
+# 开启数据收集服务
+# metrics:
+#  # 数据服务地址
+#  addr: ":8000"
diff --git a/pkg/reachable/reachable.go b/pkg/reachable/reachable.go
new file mode 100644
index 00000000..394f2275
--- /dev/null
+++ b/pkg/reachable/reachable.go
@@ -0,0 +1,83 @@
+/*
+ *     Copyright 2020 The Dragonfly Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package reachable
+
+import (
+	"fmt"
+	"net"
+	"strings"
+	"time"
+)
+
+const (
+	// DefaultPort is the default tcp port
+	DefaultPort = "80"
+	// DefaultNetwork is the default network type
+	DefaultNetwork = "tcp"
+	// DefaultTimeout is the default dial timeout
+	DefaultTimeout = 1 * time.Second
+)
+
+type Reachable interface {
+	// Check that the address can be accessed
+	Check() error
+}
+
+type reachable struct {
+	address string
+	network string
+	timeout time.Duration
+}
+
+type Config struct {
+	Address string
+	Network string
+	Timeout time.Duration
+}
+
+// New returns a new ReachableInterface interface
+func New(r *Config) Reachable {
+	network := DefaultNetwork
+	if r.Network != "" {
+		network = r.Network
+	}
+
+	timeout := DefaultTimeout
+	if r.Timeout != 0 {
+		timeout = r.Timeout
+	}
+
+	return &reachable{
+		address: r.Address,
+		network: network,
+		timeout: timeout,
+	}
+}
+
+func (r *reachable) Check() error {
+	if !strings.Contains(r.address, ":") {
+		r.address = fmt.Sprintf("%s:%s", r.address, DefaultPort)
+	}
+
+	conn, err := net.DialTimeout(r.network, r.address, r.timeout)
+	if err != nil {
+		return err
+	}
+	conn.Close()
+
+	return nil
+}


Test patch:
diff --git a/client/daemon/daemon_test.go b/client/daemon/daemon_test.go
index b8818837..10fa279f 100644
--- a/client/daemon/daemon_test.go
+++ b/client/daemon/daemon_test.go
@@ -17,8 +17,132 @@
 package daemon
 
 import (
+	"net"
 	"testing"
+
+	"github.com/stretchr/testify/assert"
+
+	"d7y.io/dragonfly/v2/pkg/basic/dfnet"
+	"d7y.io/dragonfly/v2/pkg/rpc/manager"
 )
 
-func TestPeerHost_Serve(t *testing.T) {
+func TestDaemonSchedulersToAvailableNetAddrs(t *testing.T) {
+	l, err := net.Listen("tcp", ":3000")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer l.Close()
+
+	tests := []struct {
+		name       string
+		schedulers []*manager.Scheduler
+		expect     func(t *testing.T, addrs []dfnet.NetAddr)
+	}{
+		{
+			name: "available ip",
+			schedulers: []*manager.Scheduler{
+				{
+					Ip:   "127.0.0.1",
+					Port: int32(3000),
+				},
+				{
+					Ip:   "127.0.0.1",
+					Port: int32(3001),
+				},
+			},
+			expect: func(t *testing.T, addrs []dfnet.NetAddr) {
+				assert := assert.New(t)
+				assert.EqualValues(addrs, []dfnet.NetAddr{{Type: dfnet.TCP, Addr: "127.0.0.1:3000"}})
+			},
+		},
+		{
+			name: "available host",
+			schedulers: []*manager.Scheduler{
+				{
+					Ip:       "foo",
+					HostName: "localhost",
+					Port:     int32(3000),
+				},
+				{
+					Ip:       "foo",
+					HostName: "localhost",
+					Port:     int32(3001),
+				},
+			},
+			expect: func(t *testing.T, addrs []dfnet.NetAddr) {
+				assert := assert.New(t)
+				assert.EqualValues(addrs, []dfnet.NetAddr{{Type: dfnet.TCP, Addr: "localhost:3000"}})
+			},
+		},
+		{
+			name: "available ip and host",
+			schedulers: []*manager.Scheduler{
+				{
+					Ip:       "foo",
+					HostName: "localhost",
+					Port:     int32(3000),
+				},
+				{
+					Ip:       "foo",
+					HostName: "localhost",
+					Port:     int32(3001),
+				},
+				{
+					Ip:       "127.0.0.1",
+					HostName: "foo",
+					Port:     int32(3001),
+				},
+				{
+					Ip:       "127.0.0.1",
+					HostName: "foo",
+					Port:     int32(3000),
+				},
+				{
+					Ip:       "127.0.0.1",
+					HostName: "foo",
+					Port:     int32(3001),
+				},
+			},
+			expect: func(t *testing.T, addrs []dfnet.NetAddr) {
+				assert := assert.New(t)
+				assert.EqualValues(addrs, []dfnet.NetAddr{
+					{Type: dfnet.TCP, Addr: "localhost:3000"},
+					{Type: dfnet.TCP, Addr: "127.0.0.1:3000"},
+				})
+			},
+		},
+		{
+			name: "unreachable",
+			schedulers: []*manager.Scheduler{
+				{
+					Ip:       "foo",
+					HostName: "localhost",
+					Port:     int32(3001),
+				},
+				{
+					Ip:       "127.0.0.1",
+					HostName: "foo",
+					Port:     int32(3001),
+				},
+			},
+			expect: func(t *testing.T, addrs []dfnet.NetAddr) {
+				assert := assert.New(t)
+				assert.EqualValues(addrs, []dfnet.NetAddr{})
+			},
+		},
+		{
+			name:       "empty schedulers",
+			schedulers: []*manager.Scheduler{},
+			expect: func(t *testing.T, addrs []dfnet.NetAddr) {
+				assert := assert.New(t)
+				assert.EqualValues(addrs, []dfnet.NetAddr{})
+			},
+		},
+	}
+
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+			tc.expect(t, schedulersToAvailableNetAddrs(tc.schedulers))
+		})
+	}
 }
diff --git a/pkg/reachable/reachable_test.go b/pkg/reachable/reachable_test.go
new file mode 100644
index 00000000..7c161f96
--- /dev/null
+++ b/pkg/reachable/reachable_test.go
@@ -0,0 +1,93 @@
+/*
+ *     Copyright 2020 The Dragonfly Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package reachable
+
+import (
+	"net"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestReachableCheck(t *testing.T) {
+	l, err := net.Listen("tcp", ":3000")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer l.Close()
+
+	tests := []struct {
+		name    string
+		address string
+		network string
+		timeout time.Duration
+		expect  func(t *testing.T, err error)
+	}{
+		{
+			name:    "check address succeeded",
+			address: ":3000",
+			network: "tcp",
+			timeout: 1 * time.Second,
+			expect: func(t *testing.T, err error) {
+				assert := assert.New(t)
+				assert.NoError(err)
+			},
+		},
+		{
+			name:    "check address without network",
+			address: ":3000",
+			network: "",
+			timeout: 1 * time.Second,
+			expect: func(t *testing.T, err error) {
+				assert := assert.New(t)
+				assert.NoError(err)
+			},
+		},
+		{
+			name:    "check address without address",
+			address: "",
+			network: "tcp",
+			timeout: 1 * time.Second,
+			expect: func(t *testing.T, err error) {
+				assert := assert.New(t)
+				assert.Error(err)
+			},
+		},
+		{
+			name:    "check invalid address",
+			address: "example",
+			network: "tcp",
+			timeout: 1 * time.Second,
+			expect: func(t *testing.T, err error) {
+				assert := assert.New(t)
+				assert.Error(err)
+			},
+		},
+	}
+
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+			r := New(&Config{
+				Address: tc.address,
+				Network: tc.network,
+				Timeout: tc.timeout,
+			})
+			tc.expect(t, r.Check())
+		})
+	}
+}