Refactor node ID handling, use check digits (fixes #269)
New node ID:s contain four Luhn check digits and are grouped differently. Code uses NodeID type instead of string, so it's formatted homogenously everywhere.
This commit is contained in:
@@ -49,8 +49,8 @@ type Model struct {
|
||||
|
||||
repoCfgs map[string]config.RepositoryConfiguration // repo -> cfg
|
||||
repoFiles map[string]*files.Set // repo -> files
|
||||
repoNodes map[string][]string // repo -> nodeIDs
|
||||
nodeRepos map[string][]string // nodeID -> repos
|
||||
repoNodes map[string][]protocol.NodeID // repo -> nodeIDs
|
||||
nodeRepos map[protocol.NodeID][]string // nodeID -> repos
|
||||
suppressor map[string]*suppressor // repo -> suppressor
|
||||
rmut sync.RWMutex // protects the above
|
||||
|
||||
@@ -59,9 +59,9 @@ type Model struct {
|
||||
|
||||
cm *cid.Map
|
||||
|
||||
protoConn map[string]protocol.Connection
|
||||
rawConn map[string]io.Closer
|
||||
nodeVer map[string]string
|
||||
protoConn map[protocol.NodeID]protocol.Connection
|
||||
rawConn map[protocol.NodeID]io.Closer
|
||||
nodeVer map[protocol.NodeID]string
|
||||
pmut sync.RWMutex // protects protoConn and rawConn
|
||||
|
||||
sup suppressor
|
||||
@@ -86,14 +86,14 @@ func NewModel(indexDir string, cfg *config.Configuration, clientName, clientVers
|
||||
clientVersion: clientVersion,
|
||||
repoCfgs: make(map[string]config.RepositoryConfiguration),
|
||||
repoFiles: make(map[string]*files.Set),
|
||||
repoNodes: make(map[string][]string),
|
||||
nodeRepos: make(map[string][]string),
|
||||
repoNodes: make(map[string][]protocol.NodeID),
|
||||
nodeRepos: make(map[protocol.NodeID][]string),
|
||||
repoState: make(map[string]repoState),
|
||||
suppressor: make(map[string]*suppressor),
|
||||
cm: cid.NewMap(),
|
||||
protoConn: make(map[string]protocol.Connection),
|
||||
rawConn: make(map[string]io.Closer),
|
||||
nodeVer: make(map[string]string),
|
||||
protoConn: make(map[protocol.NodeID]protocol.Connection),
|
||||
rawConn: make(map[protocol.NodeID]io.Closer),
|
||||
nodeVer: make(map[protocol.NodeID]string),
|
||||
sup: suppressor{threshold: int64(cfg.Options.MaxChangeKbps)},
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ func (m *Model) ConnectionStats() map[string]ConnectionInfo {
|
||||
ci.Completion = int(100 * have / tot)
|
||||
}
|
||||
|
||||
res[node] = ci
|
||||
res[node.String()] = ci
|
||||
}
|
||||
|
||||
m.rmut.RUnlock()
|
||||
@@ -261,7 +261,7 @@ func (m *Model) NeedFilesRepo(repo string) []scanner.File {
|
||||
|
||||
// Index is called when a new node is connected and we receive their full index.
|
||||
// Implements the protocol.Model interface.
|
||||
func (m *Model) Index(nodeID string, repo string, fs []protocol.FileInfo) {
|
||||
func (m *Model) Index(nodeID protocol.NodeID, repo string, fs []protocol.FileInfo) {
|
||||
if debug {
|
||||
l.Debugf("IDX(in): %s %q: %d files", nodeID, repo, len(fs))
|
||||
}
|
||||
@@ -297,7 +297,7 @@ func (m *Model) Index(nodeID string, repo string, fs []protocol.FileInfo) {
|
||||
|
||||
// IndexUpdate is called for incremental updates to connected nodes' indexes.
|
||||
// Implements the protocol.Model interface.
|
||||
func (m *Model) IndexUpdate(nodeID string, repo string, fs []protocol.FileInfo) {
|
||||
func (m *Model) IndexUpdate(nodeID protocol.NodeID, repo string, fs []protocol.FileInfo) {
|
||||
if debug {
|
||||
l.Debugf("IDXUP(in): %s / %q: %d files", nodeID, repo, len(fs))
|
||||
}
|
||||
@@ -331,7 +331,7 @@ func (m *Model) IndexUpdate(nodeID string, repo string, fs []protocol.FileInfo)
|
||||
m.rmut.RUnlock()
|
||||
}
|
||||
|
||||
func (m *Model) repoSharedWith(repo, nodeID string) bool {
|
||||
func (m *Model) repoSharedWith(repo string, nodeID protocol.NodeID) bool {
|
||||
m.rmut.RLock()
|
||||
defer m.rmut.RUnlock()
|
||||
for _, nrepo := range m.nodeRepos[nodeID] {
|
||||
@@ -342,7 +342,7 @@ func (m *Model) repoSharedWith(repo, nodeID string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Model) ClusterConfig(nodeID string, config protocol.ClusterConfigMessage) {
|
||||
func (m *Model) ClusterConfig(nodeID protocol.NodeID, config protocol.ClusterConfigMessage) {
|
||||
compErr := compareClusterConfig(m.clusterConfig(nodeID), config)
|
||||
if debug {
|
||||
l.Debugf("ClusterConfig: %s: %#v", nodeID, config)
|
||||
@@ -365,7 +365,7 @@ func (m *Model) ClusterConfig(nodeID string, config protocol.ClusterConfigMessag
|
||||
|
||||
// Close removes the peer from the model and closes the underlying connection if possible.
|
||||
// Implements the protocol.Model interface.
|
||||
func (m *Model) Close(node string, err error) {
|
||||
func (m *Model) Close(node protocol.NodeID, err error) {
|
||||
if debug {
|
||||
l.Debugf("%s: %v", node, err)
|
||||
}
|
||||
@@ -397,7 +397,7 @@ func (m *Model) Close(node string, err error) {
|
||||
|
||||
// Request returns the specified data segment by reading it from local disk.
|
||||
// Implements the protocol.Model interface.
|
||||
func (m *Model) Request(nodeID, repo, name string, offset int64, size int) ([]byte, error) {
|
||||
func (m *Model) Request(nodeID protocol.NodeID, repo, name string, offset int64, size int) ([]byte, error) {
|
||||
// Verify that the requested file exists in the local model.
|
||||
m.rmut.RLock()
|
||||
r, ok := m.repoFiles[repo]
|
||||
@@ -423,7 +423,7 @@ func (m *Model) Request(nodeID, repo, name string, offset int64, size int) ([]by
|
||||
return nil, ErrNoSuchFile
|
||||
}
|
||||
|
||||
if debug && nodeID != "<local>" {
|
||||
if debug && nodeID != cid.LocalNodeID {
|
||||
l.Debugf("REQ(in): %s: %q / %q o=%d s=%d", nodeID, repo, name, offset, size)
|
||||
}
|
||||
m.rmut.RLock()
|
||||
@@ -489,7 +489,7 @@ func (cf cFiler) CurrentFile(file string) scanner.File {
|
||||
}
|
||||
|
||||
// ConnectedTo returns true if we are connected to the named node.
|
||||
func (m *Model) ConnectedTo(nodeID string) bool {
|
||||
func (m *Model) ConnectedTo(nodeID protocol.NodeID) bool {
|
||||
m.pmut.RLock()
|
||||
_, ok := m.protoConn[nodeID]
|
||||
m.pmut.RUnlock()
|
||||
@@ -560,7 +560,7 @@ func (m *Model) updateLocal(repo string, f scanner.File) {
|
||||
m.rmut.RUnlock()
|
||||
}
|
||||
|
||||
func (m *Model) requestGlobal(nodeID, repo, name string, offset int64, size int, hash []byte) ([]byte, error) {
|
||||
func (m *Model) requestGlobal(nodeID protocol.NodeID, repo, name string, offset int64, size int, hash []byte) ([]byte, error) {
|
||||
m.pmut.RLock()
|
||||
nc, ok := m.protoConn[nodeID]
|
||||
m.pmut.RUnlock()
|
||||
@@ -639,7 +639,7 @@ func (m *Model) AddRepo(cfg config.RepositoryConfiguration) {
|
||||
m.repoFiles[cfg.ID] = files.NewSet()
|
||||
m.suppressor[cfg.ID] = &suppressor{threshold: int64(m.cfg.Options.MaxChangeKbps)}
|
||||
|
||||
m.repoNodes[cfg.ID] = make([]string, len(cfg.Nodes))
|
||||
m.repoNodes[cfg.ID] = make([]protocol.NodeID, len(cfg.Nodes))
|
||||
for i, node := range cfg.Nodes {
|
||||
m.repoNodes[cfg.ID][i] = node.NodeID
|
||||
m.nodeRepos[node.NodeID] = append(m.nodeRepos[node.NodeID], cfg.ID)
|
||||
@@ -805,7 +805,7 @@ func (m *Model) loadIndex(repo string, dir string) []protocol.FileInfo {
|
||||
}
|
||||
|
||||
// clusterConfig returns a ClusterConfigMessage that is correct for the given peer node
|
||||
func (m *Model) clusterConfig(node string) protocol.ClusterConfigMessage {
|
||||
func (m *Model) clusterConfig(node protocol.NodeID) protocol.ClusterConfigMessage {
|
||||
cm := protocol.ClusterConfigMessage{
|
||||
ClientName: m.clientName,
|
||||
ClientVersion: m.clientVersion,
|
||||
@@ -819,7 +819,7 @@ func (m *Model) clusterConfig(node string) protocol.ClusterConfigMessage {
|
||||
for _, node := range m.repoNodes[repo] {
|
||||
// TODO: Set read only bit when relevant
|
||||
cr.Nodes = append(cr.Nodes, protocol.Node{
|
||||
ID: node,
|
||||
ID: node[:],
|
||||
Flags: protocol.FlagShareTrusted,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -17,6 +17,13 @@ import (
|
||||
"github.com/calmh/syncthing/scanner"
|
||||
)
|
||||
|
||||
var node1, node2 protocol.NodeID
|
||||
|
||||
func init() {
|
||||
node1, _ = protocol.NodeIDFromString("AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR")
|
||||
node2, _ = protocol.NodeIDFromString("GYRZZQB-IRNPV4Z-T7TC52W-EQYJ3TT-FDQW6MW-DFLMU42-SSSU6EM-FBK2VAY")
|
||||
}
|
||||
|
||||
var testDataExpected = map[string]scanner.File{
|
||||
"foo": scanner.File{
|
||||
Name: "foo",
|
||||
@@ -56,7 +63,7 @@ func TestRequest(t *testing.T) {
|
||||
m.AddRepo(config.RepositoryConfiguration{ID: "default", Directory: "testdata"})
|
||||
m.ScanRepo("default")
|
||||
|
||||
bs, err := m.Request("some node", "default", "foo", 0, 6)
|
||||
bs, err := m.Request(node1, "default", "foo", 0, 6)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -64,7 +71,7 @@ func TestRequest(t *testing.T) {
|
||||
t.Errorf("Incorrect data from request: %q", string(bs))
|
||||
}
|
||||
|
||||
bs, err = m.Request("some node", "default", "../walk.go", 0, 6)
|
||||
bs, err = m.Request(node1, "default", "../walk.go", 0, 6)
|
||||
if err == nil {
|
||||
t.Error("Unexpected nil error on insecure file read")
|
||||
}
|
||||
@@ -95,7 +102,7 @@ func BenchmarkIndex10000(b *testing.B) {
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.Index("42", "default", files)
|
||||
m.Index(node1, "default", files)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +114,7 @@ func BenchmarkIndex00100(b *testing.B) {
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.Index("42", "default", files)
|
||||
m.Index(node1, "default", files)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,11 +123,11 @@ func BenchmarkIndexUpdate10000f10000(b *testing.B) {
|
||||
m.AddRepo(config.RepositoryConfiguration{ID: "default", Directory: "testdata"})
|
||||
m.ScanRepo("default")
|
||||
files := genFiles(10000)
|
||||
m.Index("42", "default", files)
|
||||
m.Index(node1, "default", files)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.IndexUpdate("42", "default", files)
|
||||
m.IndexUpdate(node1, "default", files)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,12 +136,12 @@ func BenchmarkIndexUpdate10000f00100(b *testing.B) {
|
||||
m.AddRepo(config.RepositoryConfiguration{ID: "default", Directory: "testdata"})
|
||||
m.ScanRepo("default")
|
||||
files := genFiles(10000)
|
||||
m.Index("42", "default", files)
|
||||
m.Index(node1, "default", files)
|
||||
|
||||
ufiles := genFiles(100)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.IndexUpdate("42", "default", ufiles)
|
||||
m.IndexUpdate(node1, "default", ufiles)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,17 +150,17 @@ func BenchmarkIndexUpdate10000f00001(b *testing.B) {
|
||||
m.AddRepo(config.RepositoryConfiguration{ID: "default", Directory: "testdata"})
|
||||
m.ScanRepo("default")
|
||||
files := genFiles(10000)
|
||||
m.Index("42", "default", files)
|
||||
m.Index(node1, "default", files)
|
||||
|
||||
ufiles := genFiles(1)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.IndexUpdate("42", "default", ufiles)
|
||||
m.IndexUpdate(node1, "default", ufiles)
|
||||
}
|
||||
}
|
||||
|
||||
type FakeConnection struct {
|
||||
id string
|
||||
id protocol.NodeID
|
||||
requestData []byte
|
||||
}
|
||||
|
||||
@@ -161,8 +168,8 @@ func (FakeConnection) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f FakeConnection) ID() string {
|
||||
return string(f.id)
|
||||
func (f FakeConnection) ID() protocol.NodeID {
|
||||
return f.id
|
||||
}
|
||||
|
||||
func (f FakeConnection) Option(string) string {
|
||||
@@ -202,15 +209,15 @@ func BenchmarkRequest(b *testing.B) {
|
||||
}
|
||||
|
||||
fc := FakeConnection{
|
||||
id: "42",
|
||||
id: node1,
|
||||
requestData: []byte("some data to return"),
|
||||
}
|
||||
m.AddConnection(fc, fc)
|
||||
m.Index("42", "default", files)
|
||||
m.Index(node1, "default", files)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
data, err := m.requestGlobal("42", "default", files[i%n].Name, 0, 32, nil)
|
||||
data, err := m.requestGlobal(node1, "default", files[i%n].Name, 0, 32, nil)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
@@ -222,26 +229,26 @@ func BenchmarkRequest(b *testing.B) {
|
||||
|
||||
func TestActivityMap(t *testing.T) {
|
||||
cm := cid.NewMap()
|
||||
fooID := cm.Get("foo")
|
||||
fooID := cm.Get(node1)
|
||||
if fooID == 0 {
|
||||
t.Fatal("ID cannot be zero")
|
||||
}
|
||||
barID := cm.Get("bar")
|
||||
barID := cm.Get(node2)
|
||||
if barID == 0 {
|
||||
t.Fatal("ID cannot be zero")
|
||||
}
|
||||
|
||||
m := make(activityMap)
|
||||
if node := m.leastBusyNode(1<<fooID, cm); node != "foo" {
|
||||
if node := m.leastBusyNode(1<<fooID, cm); node != node1 {
|
||||
t.Errorf("Incorrect least busy node %q", node)
|
||||
}
|
||||
if node := m.leastBusyNode(1<<barID, cm); node != "bar" {
|
||||
if node := m.leastBusyNode(1<<barID, cm); node != node2 {
|
||||
t.Errorf("Incorrect least busy node %q", node)
|
||||
}
|
||||
if node := m.leastBusyNode(1<<fooID|1<<barID, cm); node != "foo" {
|
||||
if node := m.leastBusyNode(1<<fooID|1<<barID, cm); node != node1 {
|
||||
t.Errorf("Incorrect least busy node %q", node)
|
||||
}
|
||||
if node := m.leastBusyNode(1<<fooID|1<<barID, cm); node != "bar" {
|
||||
if node := m.leastBusyNode(1<<fooID|1<<barID, cm); node != node2 {
|
||||
t.Errorf("Incorrect least busy node %q", node)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
)
|
||||
|
||||
type requestResult struct {
|
||||
node string
|
||||
node protocol.NodeID
|
||||
file scanner.File
|
||||
filepath string // full filepath name
|
||||
offset int64
|
||||
@@ -39,11 +39,11 @@ type openFile struct {
|
||||
done bool // we have sent all requests for this file
|
||||
}
|
||||
|
||||
type activityMap map[string]int
|
||||
type activityMap map[protocol.NodeID]int
|
||||
|
||||
func (m activityMap) leastBusyNode(availability uint64, cm *cid.Map) string {
|
||||
func (m activityMap) leastBusyNode(availability uint64, cm *cid.Map) protocol.NodeID {
|
||||
var low int = 2<<30 - 1
|
||||
var selected string
|
||||
var selected protocol.NodeID
|
||||
for _, node := range cm.Names() {
|
||||
id := cm.Get(node)
|
||||
if id == cid.LocalID {
|
||||
@@ -61,7 +61,7 @@ func (m activityMap) leastBusyNode(availability uint64, cm *cid.Map) string {
|
||||
return selected
|
||||
}
|
||||
|
||||
func (m activityMap) decrease(node string) {
|
||||
func (m activityMap) decrease(node protocol.NodeID) {
|
||||
m[node]--
|
||||
}
|
||||
|
||||
@@ -540,7 +540,7 @@ func (p *puller) handleRequestBlock(b bqBlock) bool {
|
||||
of.outstanding++
|
||||
p.openFiles[f.Name] = of
|
||||
|
||||
go func(node string, b bqBlock) {
|
||||
go func(node protocol.NodeID, b bqBlock) {
|
||||
if debug {
|
||||
l.Debugf("pull: requesting %q / %q offset %d size %d from %q outstanding %d", p.repoCfg.ID, f.Name, b.block.Offset, b.block.Size, node, of.outstanding)
|
||||
}
|
||||
|
||||
@@ -58,12 +58,14 @@ func fileInfoFromFile(f scanner.File) protocol.FileInfo {
|
||||
return pf
|
||||
}
|
||||
|
||||
func cmMap(cm protocol.ClusterConfigMessage) map[string]map[string]uint32 {
|
||||
m := make(map[string]map[string]uint32)
|
||||
func cmMap(cm protocol.ClusterConfigMessage) map[string]map[protocol.NodeID]uint32 {
|
||||
m := make(map[string]map[protocol.NodeID]uint32)
|
||||
for _, repo := range cm.Repositories {
|
||||
m[repo.ID] = make(map[string]uint32)
|
||||
m[repo.ID] = make(map[protocol.NodeID]uint32)
|
||||
for _, node := range repo.Nodes {
|
||||
m[repo.ID][node.ID] = node.Flags
|
||||
var id protocol.NodeID
|
||||
copy(id[:], node.ID)
|
||||
m[repo.ID][id] = node.Flags
|
||||
}
|
||||
}
|
||||
return m
|
||||
@@ -72,7 +74,7 @@ func cmMap(cm protocol.ClusterConfigMessage) map[string]map[string]uint32 {
|
||||
type ClusterConfigMismatch error
|
||||
|
||||
// compareClusterConfig returns nil for two equivalent configurations,
|
||||
// otherwise a decriptive error
|
||||
// otherwise a descriptive error
|
||||
func compareClusterConfig(local, remote protocol.ClusterConfigMessage) error {
|
||||
lm := cmMap(local)
|
||||
rm := cmMap(remote)
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/calmh/syncthing/protocol"
|
||||
)
|
||||
|
||||
var testcases = []struct {
|
||||
local, remote protocol.ClusterConfigMessage
|
||||
err string
|
||||
}{
|
||||
{
|
||||
local: protocol.ClusterConfigMessage{},
|
||||
remote: protocol.ClusterConfigMessage{},
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
local: protocol.ClusterConfigMessage{ClientName: "a", ClientVersion: "b"},
|
||||
remote: protocol.ClusterConfigMessage{ClientName: "c", ClientVersion: "d"},
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
local: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
{ID: "foo"},
|
||||
{ID: "bar"},
|
||||
},
|
||||
},
|
||||
remote: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
{ID: "foo"},
|
||||
{ID: "bar"},
|
||||
},
|
||||
},
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
local: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
{
|
||||
ID: "foo",
|
||||
Nodes: []protocol.Node{
|
||||
{ID: "a"},
|
||||
},
|
||||
},
|
||||
{ID: "bar"},
|
||||
},
|
||||
},
|
||||
remote: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
{ID: "foo"},
|
||||
{ID: "bar"},
|
||||
},
|
||||
},
|
||||
err: "",
|
||||
},
|
||||
|
||||
{
|
||||
local: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
{
|
||||
ID: "foo",
|
||||
Nodes: []protocol.Node{
|
||||
{ID: "a"},
|
||||
},
|
||||
},
|
||||
{ID: "bar"},
|
||||
},
|
||||
},
|
||||
remote: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
{
|
||||
ID: "foo",
|
||||
Nodes: []protocol.Node{
|
||||
{ID: "a"},
|
||||
{ID: "b"},
|
||||
},
|
||||
},
|
||||
{ID: "bar"},
|
||||
},
|
||||
},
|
||||
err: "",
|
||||
},
|
||||
|
||||
{
|
||||
local: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
{
|
||||
ID: "foo",
|
||||
Nodes: []protocol.Node{
|
||||
{
|
||||
ID: "a",
|
||||
Flags: protocol.FlagShareReadOnly,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ID: "bar"},
|
||||
},
|
||||
},
|
||||
remote: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
{
|
||||
ID: "foo",
|
||||
Nodes: []protocol.Node{
|
||||
{
|
||||
ID: "a",
|
||||
Flags: protocol.FlagShareTrusted,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ID: "bar"},
|
||||
},
|
||||
},
|
||||
err: `remote has different sharing flags for node "a" in repository "foo"`,
|
||||
},
|
||||
}
|
||||
|
||||
func TestCompareClusterConfig(t *testing.T) {
|
||||
for i, tc := range testcases {
|
||||
err := compareClusterConfig(tc.local, tc.remote)
|
||||
switch {
|
||||
case tc.err == "" && err != nil:
|
||||
t.Errorf("#%d: unexpected error: %v", i, err)
|
||||
|
||||
case tc.err != "" && err == nil:
|
||||
t.Errorf("#%d: unexpected nil error", i)
|
||||
|
||||
case tc.err != "" && err != nil && tc.err != err.Error():
|
||||
t.Errorf("#%d: incorrect error: %q != %q", i, err, tc.err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user