mirror of https://github.com/status-im/consul.git
memberlist: vendor v0.1.6 to pull in new state: stateLeft (#7184)
This commit is contained in:
parent
4ab3af0ede
commit
649ffcb66f
2
go.mod
2
go.mod
|
@ -47,7 +47,7 @@ require (
|
||||||
github.com/hashicorp/hcl v1.0.0
|
github.com/hashicorp/hcl v1.0.0
|
||||||
github.com/hashicorp/hil v0.0.0-20160711231837-1e86c6b523c5
|
github.com/hashicorp/hil v0.0.0-20160711231837-1e86c6b523c5
|
||||||
github.com/hashicorp/mdns v1.0.1 // indirect
|
github.com/hashicorp/mdns v1.0.1 // indirect
|
||||||
github.com/hashicorp/memberlist v0.1.5
|
github.com/hashicorp/memberlist v0.1.6
|
||||||
github.com/hashicorp/net-rpc-msgpackrpc v0.0.0-20151116020338-a14192a58a69
|
github.com/hashicorp/net-rpc-msgpackrpc v0.0.0-20151116020338-a14192a58a69
|
||||||
github.com/hashicorp/raft v1.1.2
|
github.com/hashicorp/raft v1.1.2
|
||||||
github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea
|
github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea
|
||||||
|
|
5
go.sum
5
go.sum
|
@ -185,8 +185,8 @@ github.com/hashicorp/mdns v1.0.1 h1:XFSOubp8KWB+Jd2PDyaX5xUd5bhSP/+pTDZVDMzZJM8=
|
||||||
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
|
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
|
||||||
github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M=
|
github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M=
|
||||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||||
github.com/hashicorp/memberlist v0.1.5 h1:AYBsgJOW9gab/toO5tEB8lWetVgDKZycqkebJ8xxpqM=
|
github.com/hashicorp/memberlist v0.1.6 h1:ouPxvwKYaNZe+eTcHxYP0EblPduVLvIPycul+vv8his=
|
||||||
github.com/hashicorp/memberlist v0.1.5/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
github.com/hashicorp/memberlist v0.1.6/go.mod h1:5VDNHjqFMgEcclnwmkCnC99IPwxBmIsxwY8qn+Nl0H4=
|
||||||
github.com/hashicorp/net-rpc-msgpackrpc v0.0.0-20151116020338-a14192a58a69 h1:lc3c72qGlIMDqQpQH82Y4vaglRMMFdJbziYWriR4UcE=
|
github.com/hashicorp/net-rpc-msgpackrpc v0.0.0-20151116020338-a14192a58a69 h1:lc3c72qGlIMDqQpQH82Y4vaglRMMFdJbziYWriR4UcE=
|
||||||
github.com/hashicorp/net-rpc-msgpackrpc v0.0.0-20151116020338-a14192a58a69/go.mod h1:/z+jUGRBlwVpUZfjute9jWaF6/HuhjuFQuL1YXzVD1Q=
|
github.com/hashicorp/net-rpc-msgpackrpc v0.0.0-20151116020338-a14192a58a69/go.mod h1:/z+jUGRBlwVpUZfjute9jWaF6/HuhjuFQuL1YXzVD1Q=
|
||||||
github.com/hashicorp/raft v1.1.1 h1:HJr7UE1x/JrJSc9Oy6aDBHtNHUUBHjcQjTgvUVihoZs=
|
github.com/hashicorp/raft v1.1.1 h1:HJr7UE1x/JrJSc9Oy6aDBHtNHUUBHjcQjTgvUVihoZs=
|
||||||
|
@ -486,7 +486,6 @@ gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
istio.io/gogo-genproto v0.0.0-20190124151557-6d926a6e6feb/go.mod h1:eIDJ6jNk/IeJz6ODSksHl5Aiczy5JUq6vFhJWI5OtiI=
|
|
||||||
k8s.io/api v0.0.0-20180806132203-61b11ee65332/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
|
k8s.io/api v0.0.0-20180806132203-61b11ee65332/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
|
||||||
k8s.io/api v0.0.0-20190325185214-7544f9db76f6 h1:9MWtbqhwTyDvF4cS1qAhxDb9Mi8taXiAu+5nEacl7gY=
|
k8s.io/api v0.0.0-20190325185214-7544f9db76f6 h1:9MWtbqhwTyDvF4cS1qAhxDb9Mi8taXiAu+5nEacl7gY=
|
||||||
k8s.io/api v0.0.0-20190325185214-7544f9db76f6/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
|
k8s.io/api v0.0.0-20190325185214-7544f9db76f6/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
language: go
|
|
||||||
|
|
||||||
sudo: true
|
|
||||||
|
|
||||||
go:
|
|
||||||
- "1.x"
|
|
||||||
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
|
|
||||||
install: true
|
|
||||||
|
|
||||||
env:
|
|
||||||
- GO111MODULE=on # Enable Go modules in 1.11
|
|
|
@ -1,6 +1,10 @@
|
||||||
DEPS := $(shell go list -f '{{range .Imports}}{{.}} {{end}}' ./...)
|
SHELL := bash
|
||||||
|
|
||||||
test: subnet
|
GOFILES ?= $(shell go list ./... | grep -v /vendor/)
|
||||||
|
|
||||||
|
default: test
|
||||||
|
|
||||||
|
test: vet subnet
|
||||||
go test ./...
|
go test ./...
|
||||||
|
|
||||||
integ: subnet
|
integ: subnet
|
||||||
|
@ -10,11 +14,20 @@ subnet:
|
||||||
./test/setup_subnet.sh
|
./test/setup_subnet.sh
|
||||||
|
|
||||||
cov:
|
cov:
|
||||||
gocov test github.com/hashicorp/memberlist | gocov-html > /tmp/coverage.html
|
go test ./... -coverprofile=coverage.out
|
||||||
open /tmp/coverage.html
|
go tool cover -html=coverage.out
|
||||||
|
|
||||||
deps:
|
format:
|
||||||
go get -t -d -v ./...
|
@echo "--> Running go fmt"
|
||||||
echo $(DEPS) | xargs -n1 go get -d
|
@go fmt $(GOFILES)
|
||||||
|
|
||||||
.PHONY: test cov integ
|
vet:
|
||||||
|
@echo "--> Running go vet"
|
||||||
|
@go vet -tags '$(GOTAGS)' $(GOFILES); if [ $$? -eq 1 ]; then \
|
||||||
|
echo ""; \
|
||||||
|
echo "Vet found suspicious constructs. Please check the reported constructs"; \
|
||||||
|
echo "and fix them if necessary before submitting the code for review."; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
.PHONY: default test integ subnet cov format vet
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# memberlist [![GoDoc](https://godoc.org/github.com/hashicorp/memberlist?status.png)](https://godoc.org/github.com/hashicorp/memberlist) [![Build Status](https://travis-ci.org/hashicorp/memberlist.svg?branch=master)](https://travis-ci.org/hashicorp/memberlist)
|
# memberlist [![GoDoc](https://godoc.org/github.com/hashicorp/memberlist?status.png)](https://godoc.org/github.com/hashicorp/memberlist) [![CircleCI](https://circleci.com/gh/hashicorp/memberlist.svg?style=svg)](https://circleci.com/gh/hashicorp/memberlist)
|
||||||
|
|
||||||
memberlist is a [Go](http://www.golang.org) library that manages cluster
|
memberlist is a [Go](http://www.golang.org) library that manages cluster
|
||||||
membership and member failure detection using a gossip based protocol.
|
membership and member failure detection using a gossip based protocol.
|
||||||
|
@ -23,8 +23,6 @@ Please check your installation with:
|
||||||
go version
|
go version
|
||||||
```
|
```
|
||||||
|
|
||||||
Run `make deps` to fetch dependencies before building
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Memberlist is surprisingly simple to use. An example is shown below:
|
Memberlist is surprisingly simple to use. An example is shown below:
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
module github.com/hashicorp/memberlist
|
module github.com/hashicorp/memberlist
|
||||||
|
|
||||||
|
go 1.12
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
|
|
@ -55,8 +55,8 @@ type Memberlist struct {
|
||||||
|
|
||||||
nodeLock sync.RWMutex
|
nodeLock sync.RWMutex
|
||||||
nodes []*nodeState // Known nodes
|
nodes []*nodeState // Known nodes
|
||||||
nodeMap map[string]*nodeState // Maps Addr.String() -> NodeState
|
nodeMap map[string]*nodeState // Maps Node.Name -> NodeState
|
||||||
nodeTimers map[string]*suspicion // Maps Addr.String() -> suspicion timer
|
nodeTimers map[string]*suspicion // Maps Node.Name -> suspicion timer
|
||||||
awareness *awareness
|
awareness *awareness
|
||||||
|
|
||||||
tickerLock sync.Mutex
|
tickerLock sync.Mutex
|
||||||
|
@ -472,7 +472,7 @@ func (m *Memberlist) UpdateNode(timeout time.Duration) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendTo is deprecated in favor of SendBestEffort, which requires a node to
|
// Deprecated: SendTo is deprecated in favor of SendBestEffort, which requires a node to
|
||||||
// target.
|
// target.
|
||||||
func (m *Memberlist) SendTo(to net.Addr, msg []byte) error {
|
func (m *Memberlist) SendTo(to net.Addr, msg []byte) error {
|
||||||
// Encode as a user message
|
// Encode as a user message
|
||||||
|
@ -484,12 +484,12 @@ func (m *Memberlist) SendTo(to net.Addr, msg []byte) error {
|
||||||
return m.rawSendMsgPacket(to.String(), nil, buf)
|
return m.rawSendMsgPacket(to.String(), nil, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendToUDP is deprecated in favor of SendBestEffort.
|
// Deprecated: SendToUDP is deprecated in favor of SendBestEffort.
|
||||||
func (m *Memberlist) SendToUDP(to *Node, msg []byte) error {
|
func (m *Memberlist) SendToUDP(to *Node, msg []byte) error {
|
||||||
return m.SendBestEffort(to, msg)
|
return m.SendBestEffort(to, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendToTCP is deprecated in favor of SendReliable.
|
// Deprecated: SendToTCP is deprecated in favor of SendReliable.
|
||||||
func (m *Memberlist) SendToTCP(to *Node, msg []byte) error {
|
func (m *Memberlist) SendToTCP(to *Node, msg []byte) error {
|
||||||
return m.SendReliable(to, msg)
|
return m.SendReliable(to, msg)
|
||||||
}
|
}
|
||||||
|
@ -525,7 +525,7 @@ func (m *Memberlist) Members() []*Node {
|
||||||
|
|
||||||
nodes := make([]*Node, 0, len(m.nodes))
|
nodes := make([]*Node, 0, len(m.nodes))
|
||||||
for _, n := range m.nodes {
|
for _, n := range m.nodes {
|
||||||
if n.State != stateDead {
|
if !n.DeadOrLeft() {
|
||||||
nodes = append(nodes, &n.Node)
|
nodes = append(nodes, &n.Node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -542,7 +542,7 @@ func (m *Memberlist) NumMembers() (alive int) {
|
||||||
defer m.nodeLock.RUnlock()
|
defer m.nodeLock.RUnlock()
|
||||||
|
|
||||||
for _, n := range m.nodes {
|
for _, n := range m.nodes {
|
||||||
if n.State != stateDead {
|
if !n.DeadOrLeft() {
|
||||||
alive++
|
alive++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -579,9 +579,14 @@ func (m *Memberlist) Leave(timeout time.Duration) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This dead message is special, because Node and From are the
|
||||||
|
// same. This helps other nodes figure out that a node left
|
||||||
|
// intentionally. When Node equals From, other nodes know for
|
||||||
|
// sure this node is gone.
|
||||||
d := dead{
|
d := dead{
|
||||||
Incarnation: state.Incarnation,
|
Incarnation: state.Incarnation,
|
||||||
Node: state.Name,
|
Node: state.Name,
|
||||||
|
From: state.Name,
|
||||||
}
|
}
|
||||||
m.deadNode(&d)
|
m.deadNode(&d)
|
||||||
|
|
||||||
|
@ -607,7 +612,7 @@ func (m *Memberlist) anyAlive() bool {
|
||||||
m.nodeLock.RLock()
|
m.nodeLock.RLock()
|
||||||
defer m.nodeLock.RUnlock()
|
defer m.nodeLock.RUnlock()
|
||||||
for _, n := range m.nodes {
|
for _, n := range m.nodes {
|
||||||
if n.State != stateDead && n.Name != m.config.Name {
|
if !n.DeadOrLeft() && n.Name != m.config.Name {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -630,7 +635,7 @@ func (m *Memberlist) ProtocolVersion() uint8 {
|
||||||
return m.config.ProtocolVersion
|
return m.config.ProtocolVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shutdown will stop any background maintanence of network activity
|
// Shutdown will stop any background maintenance of network activity
|
||||||
// for this memberlist, causing it to appear "dead". A leave message
|
// for this memberlist, causing it to appear "dead". A leave message
|
||||||
// will not be broadcasted prior, so the cluster being left will have
|
// will not be broadcasted prior, so the cluster being left will have
|
||||||
// to detect this node's shutdown using probing. If you wish to more
|
// to detect this node's shutdown using probing. If you wish to more
|
||||||
|
|
|
@ -669,7 +669,8 @@ func (m *Memberlist) rawSendMsgPacket(addr string, node *Node, msg []byte) error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to look up the destination node
|
// Try to look up the destination node. Note this will only work if the
|
||||||
|
// bare ip address is used as the node name, which is not guaranteed.
|
||||||
if node == nil {
|
if node == nil {
|
||||||
toAddr, _, err := net.SplitHostPort(addr)
|
toAddr, _, err := net.SplitHostPort(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -19,6 +19,7 @@ const (
|
||||||
stateAlive nodeStateType = iota
|
stateAlive nodeStateType = iota
|
||||||
stateSuspect
|
stateSuspect
|
||||||
stateDead
|
stateDead
|
||||||
|
stateLeft
|
||||||
)
|
)
|
||||||
|
|
||||||
// Node represents a node in the cluster.
|
// Node represents a node in the cluster.
|
||||||
|
@ -60,6 +61,10 @@ func (n *nodeState) Address() string {
|
||||||
return n.Node.Address()
|
return n.Node.Address()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *nodeState) DeadOrLeft() bool {
|
||||||
|
return n.State == stateDead || n.State == stateLeft
|
||||||
|
}
|
||||||
|
|
||||||
// ackHandler is used to register handlers for incoming acks and nacks.
|
// ackHandler is used to register handlers for incoming acks and nacks.
|
||||||
type ackHandler struct {
|
type ackHandler struct {
|
||||||
ackFn func([]byte, time.Time)
|
ackFn func([]byte, time.Time)
|
||||||
|
@ -218,7 +223,7 @@ START:
|
||||||
node = *m.nodes[m.probeIndex]
|
node = *m.nodes[m.probeIndex]
|
||||||
if node.Name == m.config.Name {
|
if node.Name == m.config.Name {
|
||||||
skip = true
|
skip = true
|
||||||
} else if node.State == stateDead {
|
} else if node.DeadOrLeft() {
|
||||||
skip = true
|
skip = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -963,8 +968,8 @@ func (m *Memberlist) aliveNode(a *alive, notify chan struct{}, bootstrap bool) {
|
||||||
time.Since(state.StateChange) > m.config.DeadNodeReclaimTime)
|
time.Since(state.StateChange) > m.config.DeadNodeReclaimTime)
|
||||||
|
|
||||||
// Allow the address to be updated if a dead node is being replaced.
|
// Allow the address to be updated if a dead node is being replaced.
|
||||||
if state.State == stateDead && canReclaim {
|
if state.State == stateLeft || (state.State == stateDead && canReclaim) {
|
||||||
m.logger.Printf("[INFO] memberlist: Updating address for failed node %s from %v:%d to %v:%d",
|
m.logger.Printf("[INFO] memberlist: Updating address for left or failed node %s from %v:%d to %v:%d",
|
||||||
state.Name, state.Addr, state.Port, net.IP(a.Addr), a.Port)
|
state.Name, state.Addr, state.Port, net.IP(a.Addr), a.Port)
|
||||||
updatesNode = true
|
updatesNode = true
|
||||||
} else {
|
} else {
|
||||||
|
@ -1059,8 +1064,8 @@ func (m *Memberlist) aliveNode(a *alive, notify chan struct{}, bootstrap bool) {
|
||||||
|
|
||||||
// Notify the delegate of any relevant updates
|
// Notify the delegate of any relevant updates
|
||||||
if m.config.Events != nil {
|
if m.config.Events != nil {
|
||||||
if oldState == stateDead {
|
if oldState == stateDead || oldState == stateLeft {
|
||||||
// if Dead -> Alive, notify of join
|
// if Dead/Left -> Alive, notify of join
|
||||||
m.config.Events.NotifyJoin(&state.Node)
|
m.config.Events.NotifyJoin(&state.Node)
|
||||||
|
|
||||||
} else if !bytes.Equal(oldMeta, state.Meta) {
|
} else if !bytes.Equal(oldMeta, state.Meta) {
|
||||||
|
@ -1179,7 +1184,7 @@ func (m *Memberlist) deadNode(d *dead) {
|
||||||
delete(m.nodeTimers, d.Node)
|
delete(m.nodeTimers, d.Node)
|
||||||
|
|
||||||
// Ignore if node is already dead
|
// Ignore if node is already dead
|
||||||
if state.State == stateDead {
|
if state.DeadOrLeft() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1203,7 +1208,14 @@ func (m *Memberlist) deadNode(d *dead) {
|
||||||
|
|
||||||
// Update the state
|
// Update the state
|
||||||
state.Incarnation = d.Incarnation
|
state.Incarnation = d.Incarnation
|
||||||
state.State = stateDead
|
|
||||||
|
// If the dead message was send by the node itself, mark it is left
|
||||||
|
// instead of dead.
|
||||||
|
if d.Node == d.From {
|
||||||
|
state.State = stateLeft
|
||||||
|
} else {
|
||||||
|
state.State = stateDead
|
||||||
|
}
|
||||||
state.StateChange = time.Now()
|
state.StateChange = time.Now()
|
||||||
|
|
||||||
// Notify of death
|
// Notify of death
|
||||||
|
@ -1228,6 +1240,9 @@ func (m *Memberlist) mergeState(remote []pushNodeState) {
|
||||||
}
|
}
|
||||||
m.aliveNode(&a, nil, false)
|
m.aliveNode(&a, nil, false)
|
||||||
|
|
||||||
|
case stateLeft:
|
||||||
|
d := dead{Incarnation: r.Incarnation, Node: r.Name, From: r.Name}
|
||||||
|
m.deadNode(&d)
|
||||||
case stateDead:
|
case stateDead:
|
||||||
// If the remote node believes a node is dead, we prefer to
|
// If the remote node believes a node is dead, we prefer to
|
||||||
// suspect that node instead of declaring it dead instantly
|
// suspect that node instead of declaring it dead instantly
|
||||||
|
|
|
@ -241,7 +241,7 @@ github.com/hashicorp/hil
|
||||||
github.com/hashicorp/hil/ast
|
github.com/hashicorp/hil/ast
|
||||||
# github.com/hashicorp/mdns v1.0.1
|
# github.com/hashicorp/mdns v1.0.1
|
||||||
github.com/hashicorp/mdns
|
github.com/hashicorp/mdns
|
||||||
# github.com/hashicorp/memberlist v0.1.5
|
# github.com/hashicorp/memberlist v0.1.6
|
||||||
github.com/hashicorp/memberlist
|
github.com/hashicorp/memberlist
|
||||||
# github.com/hashicorp/net-rpc-msgpackrpc v0.0.0-20151116020338-a14192a58a69
|
# github.com/hashicorp/net-rpc-msgpackrpc v0.0.0-20151116020338-a14192a58a69
|
||||||
github.com/hashicorp/net-rpc-msgpackrpc
|
github.com/hashicorp/net-rpc-msgpackrpc
|
||||||
|
|
Loading…
Reference in New Issue