Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
0cb90e4bea | |||
df83b1b34e | |||
f2bef04009 | |||
02198336f6 | |||
0c9a226e0e | |||
5bd1d420bb | |||
a1cb5cb768 | |||
acba49fe81 |
@ -208,6 +208,9 @@ func (cfg *config) Parse(arguments []string) error {
|
|||||||
default:
|
default:
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
if len(cfg.FlagSet.Args()) != 0 {
|
||||||
|
return fmt.Errorf("'%s' is not a valid flag", cfg.FlagSet.Arg(0))
|
||||||
|
}
|
||||||
|
|
||||||
if cfg.printVersion {
|
if cfg.printVersion {
|
||||||
fmt.Println("etcd version", version.Version)
|
fmt.Println("etcd version", version.Version)
|
||||||
|
@ -56,7 +56,7 @@ func Main() {
|
|||||||
cfg := NewConfig()
|
cfg := NewConfig()
|
||||||
err := cfg.Parse(os.Args[1:])
|
err := cfg.Parse(os.Args[1:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("etcd: error verifying flags, %v", err)
|
log.Printf("etcd: error verifying flags, %v. See 'etcd -help'.", err)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,9 +46,39 @@ type ServerConfig struct {
|
|||||||
ElectionTicks int
|
ElectionTicks int
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyBootstrapConfig sanity-checks the initial config and returns an error
|
// VerifyBootstrapConfig sanity-checks the initial config for bootstrap case
|
||||||
// for things that should never happen.
|
// and returns an error for things that should never happen.
|
||||||
func (c *ServerConfig) VerifyBootstrapConfig() error {
|
func (c *ServerConfig) VerifyBootstrap() error {
|
||||||
|
if err := c.verifyLocalMember(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.Cluster.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c.Cluster.String() == "" && c.DiscoveryURL == "" {
|
||||||
|
return fmt.Errorf("initial cluster unset and no discovery URL found")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyJoinExisting sanity-checks the initial config for join existing cluster
|
||||||
|
// case and returns an error for things that should never happen.
|
||||||
|
func (c *ServerConfig) VerifyJoinExisting() error {
|
||||||
|
if err := c.verifyLocalMember(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.Cluster.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c.DiscoveryURL != "" {
|
||||||
|
return fmt.Errorf("discovery URL should not be set when joining existing initial cluster")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// verifyLocalMember verifies that the local member is valid and is listed
|
||||||
|
// in the cluster correctly.
|
||||||
|
func (c *ServerConfig) verifyLocalMember() error {
|
||||||
m := c.Cluster.MemberByName(c.Name)
|
m := c.Cluster.MemberByName(c.Name)
|
||||||
// Make sure the cluster at least contains the local server.
|
// Make sure the cluster at least contains the local server.
|
||||||
if m == nil {
|
if m == nil {
|
||||||
@ -58,14 +88,6 @@ func (c *ServerConfig) VerifyBootstrapConfig() error {
|
|||||||
return fmt.Errorf("cannot use %x as member id", raft.None)
|
return fmt.Errorf("cannot use %x as member id", raft.None)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.DiscoveryURL == "" && !c.NewCluster {
|
|
||||||
return fmt.Errorf("initial cluster state unset and no wal or discovery URL found")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.Cluster.Validate(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Advertised peer URLs must match those in the cluster peer list
|
// Advertised peer URLs must match those in the cluster peer list
|
||||||
// TODO: Remove URLStringsEqual after improvement of using hostnames #2150 #2123
|
// TODO: Remove URLStringsEqual after improvement of using hostnames #2150 #2123
|
||||||
apurls := c.PeerURLs.StringSlice()
|
apurls := c.PeerURLs.StringSlice()
|
||||||
|
@ -29,74 +29,76 @@ func mustNewURLs(t *testing.T, urls []string) []url.URL {
|
|||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBootstrapConfigVerify(t *testing.T) {
|
func TestConfigVerifyBootstrapWithoutClusterAndDiscoveryURLFail(t *testing.T) {
|
||||||
|
cluster, err := NewClusterFromString("", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewClusterFromString error: %v", err)
|
||||||
|
}
|
||||||
|
c := &ServerConfig{
|
||||||
|
Name: "node1",
|
||||||
|
DiscoveryURL: "",
|
||||||
|
Cluster: cluster,
|
||||||
|
}
|
||||||
|
if err := c.VerifyBootstrap(); err == nil {
|
||||||
|
t.Errorf("err = nil, want not nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigVerifyExistingWithDiscoveryURLFail(t *testing.T) {
|
||||||
|
cluster, err := NewClusterFromString("", "node1=http://127.0.0.1:2380")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewClusterFromString error: %v", err)
|
||||||
|
}
|
||||||
|
c := &ServerConfig{
|
||||||
|
Name: "node1",
|
||||||
|
DiscoveryURL: "http://127.0.0.1:4001/abcdefg",
|
||||||
|
PeerURLs: mustNewURLs(t, []string{"http://127.0.0.1:2380"}),
|
||||||
|
Cluster: cluster,
|
||||||
|
NewCluster: false,
|
||||||
|
}
|
||||||
|
if err := c.VerifyJoinExisting(); err == nil {
|
||||||
|
t.Errorf("err = nil, want not nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigVerifyLocalMember(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
clusterSetting string
|
clusterSetting string
|
||||||
newclst bool
|
|
||||||
apurls []string
|
apurls []string
|
||||||
disc string
|
|
||||||
shouldError bool
|
shouldError bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
// Node must exist in cluster
|
// Node must exist in cluster
|
||||||
"",
|
"",
|
||||||
true,
|
|
||||||
nil,
|
nil,
|
||||||
"",
|
|
||||||
|
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Cannot have duplicate URLs in cluster config
|
// Initial cluster set
|
||||||
"node1=http://localhost:7001,node2=http://localhost:7001,node2=http://localhost:7002",
|
|
||||||
true,
|
|
||||||
nil,
|
|
||||||
"",
|
|
||||||
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Node defined, ClusterState OK
|
|
||||||
"node1=http://localhost:7001,node2=http://localhost:7002",
|
"node1=http://localhost:7001,node2=http://localhost:7002",
|
||||||
true,
|
|
||||||
[]string{"http://localhost:7001"},
|
[]string{"http://localhost:7001"},
|
||||||
"",
|
|
||||||
|
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Node defined, discovery OK
|
// Default initial cluster
|
||||||
"node1=http://localhost:7001",
|
"node1=http://localhost:2380,node1=http://localhost:7001",
|
||||||
false,
|
[]string{"http://localhost:2380", "http://localhost:7001"},
|
||||||
[]string{"http://localhost:7001"},
|
|
||||||
"http://discovery",
|
|
||||||
|
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
// Cannot have ClusterState!=new && !discovery
|
|
||||||
"node1=http://localhost:7001",
|
|
||||||
false,
|
|
||||||
nil,
|
|
||||||
"",
|
|
||||||
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
// Advertised peer URLs must match those in cluster-state
|
// Advertised peer URLs must match those in cluster-state
|
||||||
"node1=http://localhost:7001",
|
"node1=http://localhost:7001",
|
||||||
true,
|
|
||||||
[]string{"http://localhost:12345"},
|
[]string{"http://localhost:12345"},
|
||||||
"",
|
|
||||||
|
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Advertised peer URLs must match those in cluster-state
|
// Advertised peer URLs must match those in cluster-state
|
||||||
"node1=http://localhost:7001,node1=http://localhost:12345",
|
"node1=http://localhost:7001,node1=http://localhost:12345",
|
||||||
true,
|
|
||||||
[]string{"http://localhost:12345"},
|
[]string{"http://localhost:12345"},
|
||||||
"",
|
|
||||||
|
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
@ -108,15 +110,13 @@ func TestBootstrapConfigVerify(t *testing.T) {
|
|||||||
t.Fatalf("#%d: Got unexpected error: %v", i, err)
|
t.Fatalf("#%d: Got unexpected error: %v", i, err)
|
||||||
}
|
}
|
||||||
cfg := ServerConfig{
|
cfg := ServerConfig{
|
||||||
Name: "node1",
|
Name: "node1",
|
||||||
DiscoveryURL: tt.disc,
|
Cluster: cluster,
|
||||||
Cluster: cluster,
|
|
||||||
NewCluster: tt.newclst,
|
|
||||||
}
|
}
|
||||||
if tt.apurls != nil {
|
if tt.apurls != nil {
|
||||||
cfg.PeerURLs = mustNewURLs(t, tt.apurls)
|
cfg.PeerURLs = mustNewURLs(t, tt.apurls)
|
||||||
}
|
}
|
||||||
err = cfg.VerifyBootstrapConfig()
|
err = cfg.verifyLocalMember()
|
||||||
if (err == nil) && tt.shouldError {
|
if (err == nil) && tt.shouldError {
|
||||||
t.Errorf("%#v", *cluster)
|
t.Errorf("%#v", *cluster)
|
||||||
t.Errorf("#%d: Got no error where one was expected", i)
|
t.Errorf("#%d: Got no error where one was expected", i)
|
||||||
|
@ -42,6 +42,7 @@ import (
|
|||||||
"github.com/coreos/etcd/rafthttp"
|
"github.com/coreos/etcd/rafthttp"
|
||||||
"github.com/coreos/etcd/snap"
|
"github.com/coreos/etcd/snap"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
|
"github.com/coreos/etcd/version"
|
||||||
"github.com/coreos/etcd/wal"
|
"github.com/coreos/etcd/wal"
|
||||||
|
|
||||||
"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
|
"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
|
||||||
@ -145,18 +146,23 @@ func NewServer(cfg *ServerConfig) (*EtcdServer, error) {
|
|||||||
var s *raft.MemoryStorage
|
var s *raft.MemoryStorage
|
||||||
var id types.ID
|
var id types.ID
|
||||||
|
|
||||||
walVersion, err := wal.DetectVersion(cfg.DataDir)
|
// Run the migrations.
|
||||||
|
dataVer, err := version.DetectDataDir(cfg.DataDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if walVersion == wal.WALUnknown {
|
if err := upgradeDataDir(cfg.DataDir, cfg.Name, dataVer); err != nil {
|
||||||
return nil, fmt.Errorf("unknown wal version in data dir %s", cfg.DataDir)
|
return nil, err
|
||||||
}
|
}
|
||||||
haveWAL := walVersion != wal.WALNotExist
|
|
||||||
|
haveWAL := wal.Exist(cfg.WALDir())
|
||||||
ss := snap.New(cfg.SnapDir())
|
ss := snap.New(cfg.SnapDir())
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case !haveWAL && !cfg.NewCluster:
|
case !haveWAL && !cfg.NewCluster:
|
||||||
|
if err := cfg.VerifyJoinExisting(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
existingCluster, err := GetClusterFromRemotePeers(getRemotePeerURLs(cfg.Cluster, cfg.Name), cfg.Transport)
|
existingCluster, err := GetClusterFromRemotePeers(getRemotePeerURLs(cfg.Cluster, cfg.Name), cfg.Transport)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot fetch cluster info from peer urls: %v", err)
|
return nil, fmt.Errorf("cannot fetch cluster info from peer urls: %v", err)
|
||||||
@ -170,7 +176,7 @@ func NewServer(cfg *ServerConfig) (*EtcdServer, error) {
|
|||||||
cfg.Print()
|
cfg.Print()
|
||||||
id, n, s, w = startNode(cfg, nil)
|
id, n, s, w = startNode(cfg, nil)
|
||||||
case !haveWAL && cfg.NewCluster:
|
case !haveWAL && cfg.NewCluster:
|
||||||
if err := cfg.VerifyBootstrapConfig(); err != nil {
|
if err := cfg.VerifyBootstrap(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
m := cfg.Cluster.MemberByName(cfg.Name)
|
m := cfg.Cluster.MemberByName(cfg.Name)
|
||||||
@ -193,11 +199,6 @@ func NewServer(cfg *ServerConfig) (*EtcdServer, error) {
|
|||||||
cfg.PrintWithInitial()
|
cfg.PrintWithInitial()
|
||||||
id, n, s, w = startNode(cfg, cfg.Cluster.MemberIDs())
|
id, n, s, w = startNode(cfg, cfg.Cluster.MemberIDs())
|
||||||
case haveWAL:
|
case haveWAL:
|
||||||
// Run the migrations.
|
|
||||||
if err := upgradeWAL(cfg.DataDir, cfg.Name, walVersion); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := fileutil.IsDirWriteable(cfg.DataDir); err != nil {
|
if err := fileutil.IsDirWriteable(cfg.DataDir); err != nil {
|
||||||
return nil, fmt.Errorf("cannot write to data directory: %v", err)
|
return nil, fmt.Errorf("cannot write to data directory: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
"github.com/coreos/etcd/pkg/types"
|
"github.com/coreos/etcd/pkg/types"
|
||||||
"github.com/coreos/etcd/raft/raftpb"
|
"github.com/coreos/etcd/raft/raftpb"
|
||||||
"github.com/coreos/etcd/snap"
|
"github.com/coreos/etcd/snap"
|
||||||
|
"github.com/coreos/etcd/version"
|
||||||
"github.com/coreos/etcd/wal"
|
"github.com/coreos/etcd/wal"
|
||||||
"github.com/coreos/etcd/wal/walpb"
|
"github.com/coreos/etcd/wal/walpb"
|
||||||
)
|
)
|
||||||
@ -93,9 +94,9 @@ func readWAL(waldir string, snap walpb.Snapshot) (w *wal.WAL, id, cid types.ID,
|
|||||||
|
|
||||||
// upgradeWAL converts an older version of the etcdServer data to the newest version.
|
// upgradeWAL converts an older version of the etcdServer data to the newest version.
|
||||||
// It must ensure that, after upgrading, the most recent version is present.
|
// It must ensure that, after upgrading, the most recent version is present.
|
||||||
func upgradeWAL(baseDataDir string, name string, ver wal.WalVersion) error {
|
func upgradeDataDir(baseDataDir string, name string, ver version.DataDirVersion) error {
|
||||||
switch ver {
|
switch ver {
|
||||||
case wal.WALv0_4:
|
case version.DataDir0_4:
|
||||||
log.Print("etcdserver: converting v0.4 log to v2.0")
|
log.Print("etcdserver: converting v0.4 log to v2.0")
|
||||||
err := migrate.Migrate4To2(baseDataDir, name)
|
err := migrate.Migrate4To2(baseDataDir, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -103,16 +104,16 @@ func upgradeWAL(baseDataDir string, name string, ver wal.WalVersion) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fallthrough
|
fallthrough
|
||||||
case wal.WALv2_0:
|
case version.DataDir2_0:
|
||||||
err := makeMemberDir(baseDataDir)
|
err := makeMemberDir(baseDataDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fallthrough
|
fallthrough
|
||||||
case wal.WALv2_0_1:
|
case version.DataDir2_0_1:
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
log.Printf("datadir is valid for the 2.0.1 format")
|
log.Printf("etcdserver: datadir is valid for the 2.0.1 format")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
2
test
2
test
@ -15,7 +15,7 @@ COVER=${COVER:-"-cover"}
|
|||||||
source ./build
|
source ./build
|
||||||
|
|
||||||
# Hack: gofmt ./ will recursively check the .git directory. So use *.go for gofmt.
|
# Hack: gofmt ./ will recursively check the .git directory. So use *.go for gofmt.
|
||||||
TESTABLE_AND_FORMATTABLE="client discovery error etcdctl/command etcdmain etcdserver etcdserver/etcdhttp etcdserver/etcdhttp/httptypes migrate pkg/fileutil pkg/flags pkg/idutil pkg/ioutil pkg/netutil pkg/osutil pkg/pbutil pkg/types pkg/transport pkg/wait proxy raft rafthttp snap store wal"
|
TESTABLE_AND_FORMATTABLE="client discovery error etcdctl/command etcdmain etcdserver etcdserver/etcdhttp etcdserver/etcdhttp/httptypes migrate pkg/fileutil pkg/flags pkg/idutil pkg/ioutil pkg/netutil pkg/osutil pkg/pbutil pkg/types pkg/transport pkg/wait proxy raft rafthttp snap store version wal"
|
||||||
FORMATTABLE="$TESTABLE_AND_FORMATTABLE *.go etcdctl/ integration"
|
FORMATTABLE="$TESTABLE_AND_FORMATTABLE *.go etcdctl/ integration"
|
||||||
|
|
||||||
# user has not provided PKG override
|
# user has not provided PKG override
|
||||||
|
@ -14,7 +14,66 @@
|
|||||||
|
|
||||||
package version
|
package version
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/pkg/fileutil"
|
||||||
|
"github.com/coreos/etcd/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Version = "2.0.6"
|
Version = "2.0.7"
|
||||||
InternalVersion = "2"
|
InternalVersion = "2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// WalVersion is an enum for versions of etcd logs.
|
||||||
|
type DataDirVersion string
|
||||||
|
|
||||||
|
const (
|
||||||
|
DataDirUnknown DataDirVersion = "Unknown WAL"
|
||||||
|
DataDir0_4 DataDirVersion = "0.4.x"
|
||||||
|
DataDir2_0 DataDirVersion = "2.0.0"
|
||||||
|
DataDir2_0Proxy DataDirVersion = "2.0 proxy"
|
||||||
|
DataDir2_0_1 DataDirVersion = "2.0.1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DetectDataDir(dirpath string) (DataDirVersion, error) {
|
||||||
|
names, err := fileutil.ReadDir(dirpath)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
// Error reading the directory
|
||||||
|
return DataDirUnknown, err
|
||||||
|
}
|
||||||
|
nameSet := types.NewUnsafeSet(names...)
|
||||||
|
if nameSet.Contains("member") {
|
||||||
|
ver, err := DetectDataDir(path.Join(dirpath, "member"))
|
||||||
|
if ver == DataDir2_0 {
|
||||||
|
return DataDir2_0_1, nil
|
||||||
|
} else if ver == DataDir0_4 {
|
||||||
|
// How in the blazes did it get there?
|
||||||
|
return DataDirUnknown, nil
|
||||||
|
}
|
||||||
|
return ver, err
|
||||||
|
}
|
||||||
|
if nameSet.ContainsAll([]string{"snap", "wal"}) {
|
||||||
|
// .../wal cannot be empty to exist.
|
||||||
|
walnames, err := fileutil.ReadDir(path.Join(dirpath, "wal"))
|
||||||
|
if err == nil && len(walnames) > 0 {
|
||||||
|
return DataDir2_0, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if nameSet.ContainsAll([]string{"proxy"}) {
|
||||||
|
return DataDir2_0Proxy, nil
|
||||||
|
}
|
||||||
|
if nameSet.ContainsAll([]string{"snapshot", "conf", "log"}) {
|
||||||
|
return DataDir0_4, nil
|
||||||
|
}
|
||||||
|
if nameSet.ContainsAll([]string{"standby_info"}) {
|
||||||
|
return DataDir0_4, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return DataDirUnknown, nil
|
||||||
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package wal
|
package version
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -22,21 +22,20 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDetectVersion(t *testing.T) {
|
func TestDetectDataDir(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
names []string
|
names []string
|
||||||
wver WalVersion
|
wver DataDirVersion
|
||||||
}{
|
}{
|
||||||
{[]string{}, WALNotExist},
|
{[]string{"member/", "member/wal/", "member/wal/1", "member/snap/"}, DataDir2_0_1},
|
||||||
{[]string{"member/", "member/wal/", "member/wal/1", "member/snap/"}, WALv2_0_1},
|
{[]string{"snap/", "wal/", "wal/1"}, DataDir2_0},
|
||||||
{[]string{"snap/", "wal/", "wal/1"}, WALv2_0},
|
{[]string{"snapshot/", "conf", "log"}, DataDir0_4},
|
||||||
{[]string{"snapshot/", "conf", "log"}, WALv0_4},
|
{[]string{"weird"}, DataDirUnknown},
|
||||||
{[]string{"weird"}, WALUnknown},
|
{[]string{"snap/", "wal/"}, DataDirUnknown},
|
||||||
{[]string{"snap/", "wal/"}, WALUnknown},
|
|
||||||
}
|
}
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
p := mustMakeDir(t, tt.names...)
|
p := mustMakeDir(t, tt.names...)
|
||||||
ver, err := DetectVersion(p)
|
ver, err := DetectDataDir(p)
|
||||||
if ver != tt.wver {
|
if ver != tt.wver {
|
||||||
t.Errorf("#%d: version = %s, want %s", i, ver, tt.wver)
|
t.Errorf("#%d: version = %s, want %s", i, ver, tt.wver)
|
||||||
}
|
}
|
||||||
@ -45,15 +44,6 @@ func TestDetectVersion(t *testing.T) {
|
|||||||
}
|
}
|
||||||
os.RemoveAll(p)
|
os.RemoveAll(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// detect on non-exist directory
|
|
||||||
v, err := DetectVersion(path.Join(os.TempDir(), "waltest", "not-exist"))
|
|
||||||
if v != WALNotExist {
|
|
||||||
t.Errorf("#non-exist: version = %s, want %s", v, WALNotExist)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("#non-exist: err = %s, want %s", v, WALNotExist)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// mustMakeDir builds the directory that contains files with the given
|
// mustMakeDir builds the directory that contains files with the given
|
58
wal/util.go
58
wal/util.go
@ -17,68 +17,10 @@ package wal
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
|
|
||||||
"github.com/coreos/etcd/pkg/fileutil"
|
"github.com/coreos/etcd/pkg/fileutil"
|
||||||
"github.com/coreos/etcd/pkg/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// WalVersion is an enum for versions of etcd logs.
|
|
||||||
type WalVersion string
|
|
||||||
|
|
||||||
const (
|
|
||||||
WALUnknown WalVersion = "Unknown WAL"
|
|
||||||
WALNotExist WalVersion = "No WAL"
|
|
||||||
WALv0_4 WalVersion = "0.4.x"
|
|
||||||
WALv2_0 WalVersion = "2.0.0"
|
|
||||||
WALv2_0Proxy WalVersion = "2.0 proxy"
|
|
||||||
WALv2_0_1 WalVersion = "2.0.1"
|
|
||||||
)
|
|
||||||
|
|
||||||
func DetectVersion(dirpath string) (WalVersion, error) {
|
|
||||||
names, err := fileutil.ReadDir(dirpath)
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
// Error reading the directory
|
|
||||||
return WALNotExist, err
|
|
||||||
}
|
|
||||||
if len(names) == 0 {
|
|
||||||
// Empty WAL directory
|
|
||||||
return WALNotExist, nil
|
|
||||||
}
|
|
||||||
nameSet := types.NewUnsafeSet(names...)
|
|
||||||
if nameSet.Contains("member") {
|
|
||||||
ver, err := DetectVersion(path.Join(dirpath, "member"))
|
|
||||||
if ver == WALv2_0 {
|
|
||||||
return WALv2_0_1, nil
|
|
||||||
} else if ver == WALv0_4 {
|
|
||||||
// How in the blazes did it get there?
|
|
||||||
return WALUnknown, nil
|
|
||||||
}
|
|
||||||
return ver, err
|
|
||||||
}
|
|
||||||
if nameSet.ContainsAll([]string{"snap", "wal"}) {
|
|
||||||
// .../wal cannot be empty to exist.
|
|
||||||
if Exist(path.Join(dirpath, "wal")) {
|
|
||||||
return WALv2_0, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if nameSet.ContainsAll([]string{"proxy"}) {
|
|
||||||
return WALv2_0Proxy, nil
|
|
||||||
}
|
|
||||||
if nameSet.ContainsAll([]string{"snapshot", "conf", "log"}) {
|
|
||||||
return WALv0_4, nil
|
|
||||||
}
|
|
||||||
if nameSet.ContainsAll([]string{"standby_info"}) {
|
|
||||||
return WALv0_4, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return WALUnknown, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Exist(dirpath string) bool {
|
func Exist(dirpath string) bool {
|
||||||
names, err := fileutil.ReadDir(dirpath)
|
names, err := fileutil.ReadDir(dirpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -326,6 +326,7 @@ func (w *WAL) sync() error {
|
|||||||
// lock 1,2 but keep 3. ReleaseLockTo(5) will release 1,2,3 but keep 4.
|
// lock 1,2 but keep 3. ReleaseLockTo(5) will release 1,2,3 but keep 4.
|
||||||
func (w *WAL) ReleaseLockTo(index uint64) error {
|
func (w *WAL) ReleaseLockTo(index uint64) error {
|
||||||
var smaller int
|
var smaller int
|
||||||
|
found := false
|
||||||
|
|
||||||
for i, l := range w.locks {
|
for i, l := range w.locks {
|
||||||
_, lockIndex, err := parseWalName(path.Base(l.Name()))
|
_, lockIndex, err := parseWalName(path.Base(l.Name()))
|
||||||
@ -334,10 +335,17 @@ func (w *WAL) ReleaseLockTo(index uint64) error {
|
|||||||
}
|
}
|
||||||
if lockIndex >= index {
|
if lockIndex >= index {
|
||||||
smaller = i - 1
|
smaller = i - 1
|
||||||
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if no lock index is greater than the release index, we can
|
||||||
|
// release lock upto the last one(excluding).
|
||||||
|
if !found && len(w.locks) != 0 {
|
||||||
|
smaller = len(w.locks) - 1
|
||||||
|
}
|
||||||
|
|
||||||
if smaller <= 0 {
|
if smaller <= 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -504,4 +504,21 @@ func TestReleaseLockTo(t *testing.T) {
|
|||||||
t.Errorf("#%d: lockindex = %d, want %d", i, lockIndex, uint64(i+4))
|
t.Errorf("#%d: lockindex = %d, want %d", i, lockIndex, uint64(i+4))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// release the lock to 15
|
||||||
|
unlockIndex = uint64(15)
|
||||||
|
w.ReleaseLockTo(unlockIndex)
|
||||||
|
|
||||||
|
// expected remaining is 10
|
||||||
|
if len(w.locks) != 1 {
|
||||||
|
t.Errorf("len(w.locks) = %d, want %d", len(w.locks), 1)
|
||||||
|
}
|
||||||
|
_, lockIndex, err := parseWalName(path.Base(w.locks[0].Name()))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if lockIndex != uint64(10) {
|
||||||
|
t.Errorf("lockindex = %d, want %d", lockIndex, 10)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user