Merge pull request #80037 from dims/remove-support-for-etcd2-from-images-etcd
Remove support for etcd2 from cluster/images/etcd image
This commit is contained in:
commit
aaad86c210
@ -15,7 +15,7 @@
|
|||||||
# Build the etcd image
|
# Build the etcd image
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# [BUNDLED_ETCD_VERSIONS=2.2.1 2.3.7 3.0.17 3.1.12 3.2.24 3.3.10] [REGISTRY=k8s.gcr.io] [ARCH=amd64] [BASEIMAGE=busybox] make (build|push)
|
# [BUNDLED_ETCD_VERSIONS=3.0.17 3.1.12 3.2.24 3.3.10] [REGISTRY=k8s.gcr.io] [ARCH=amd64] [BASEIMAGE=busybox] make (build|push)
|
||||||
#
|
#
|
||||||
# The image contains different etcd versions to simplify
|
# The image contains different etcd versions to simplify
|
||||||
# upgrades. Thus be careful when removing any versions from here.
|
# upgrades. Thus be careful when removing any versions from here.
|
||||||
@ -26,7 +26,7 @@
|
|||||||
# Except from etcd-$(version) and etcdctl-$(version) binaries, we also
|
# Except from etcd-$(version) and etcdctl-$(version) binaries, we also
|
||||||
# need etcd and etcdctl binaries for backward compatibility reasons.
|
# need etcd and etcdctl binaries for backward compatibility reasons.
|
||||||
# That binary will be set to the last version from $(BUNDLED_ETCD_VERSIONS).
|
# That binary will be set to the last version from $(BUNDLED_ETCD_VERSIONS).
|
||||||
BUNDLED_ETCD_VERSIONS?=2.2.1 2.3.7 3.0.17 3.1.12 3.2.24 3.3.10
|
BUNDLED_ETCD_VERSIONS?=3.0.17 3.1.12 3.2.24 3.3.10
|
||||||
|
|
||||||
# LATEST_ETCD_VERSION identifies the most recent etcd version available.
|
# LATEST_ETCD_VERSION identifies the most recent etcd version available.
|
||||||
LATEST_ETCD_VERSION?=3.3.10
|
LATEST_ETCD_VERSION?=3.3.10
|
||||||
@ -34,7 +34,7 @@ LATEST_ETCD_VERSION?=3.3.10
|
|||||||
# REVISION provides a version number fo this image and all it's bundled
|
# REVISION provides a version number fo this image and all it's bundled
|
||||||
# artifacts. It should start at zero for each LATEST_ETCD_VERSION and increment
|
# artifacts. It should start at zero for each LATEST_ETCD_VERSION and increment
|
||||||
# for each revision of this image at that etcd version.
|
# for each revision of this image at that etcd version.
|
||||||
REVISION?=1
|
REVISION?=2
|
||||||
|
|
||||||
# IMAGE_TAG Uniquely identifies k8s.gcr.io/etcd docker image with a tag of the form "<etcd-version>-<revision>".
|
# IMAGE_TAG Uniquely identifies k8s.gcr.io/etcd docker image with a tag of the form "<etcd-version>-<revision>".
|
||||||
IMAGE_TAG=$(LATEST_ETCD_VERSION)-$(REVISION)
|
IMAGE_TAG=$(LATEST_ETCD_VERSION)-$(REVISION)
|
||||||
|
@ -34,7 +34,7 @@ version.
|
|||||||
Upgrades to any target version are supported. The data will be automatically upgraded
|
Upgrades to any target version are supported. The data will be automatically upgraded
|
||||||
in steps to each minor version until the target version is reached.
|
in steps to each minor version until the target version is reached.
|
||||||
|
|
||||||
Downgrades to the previous minor version of the 3.x series and from 3.0 to 2.3.7 are supported.
|
Downgrades to the previous minor version of the 3.x series is supported.
|
||||||
|
|
||||||
#### Permissions
|
#### Permissions
|
||||||
|
|
||||||
|
@ -17,17 +17,15 @@
|
|||||||
# NOTES
|
# NOTES
|
||||||
# This script performs etcd upgrade based on the following environmental
|
# This script performs etcd upgrade based on the following environmental
|
||||||
# variables:
|
# variables:
|
||||||
# TARGET_STORAGE - API of etcd to be used (supported: 'etcd2', 'etcd3')
|
# TARGET_STORAGE - API of etcd to be used (supported: 'etcd3')
|
||||||
# TARGET_VERSION - etcd release to be used (supported: '2.2.1', '2.3.7', '3.0.17', '3.1.12', '3.2.24', "3.3.10")
|
# TARGET_VERSION - etcd release to be used (supported: '3.0.17', '3.1.12', '3.2.24', "3.3.10")
|
||||||
# DATA_DIRECTORY - directory with etcd data
|
# DATA_DIRECTORY - directory with etcd data
|
||||||
#
|
#
|
||||||
# The current etcd version and storage format is detected based on the
|
# The current etcd version and storage format is detected based on the
|
||||||
# contents of "${DATA_DIRECTORY}/version.txt" file (if the file doesn't
|
# contents of "${DATA_DIRECTORY}/version.txt" file (if the file doesn't
|
||||||
# exist, we default it to "2.2.1/etcd2".
|
# exist, we default it to "3.0.17/etcd2".
|
||||||
#
|
#
|
||||||
# The update workflow support the following upgrade steps:
|
# The update workflow support the following upgrade steps:
|
||||||
# - 2.2.1/etcd2 -> 2.3.7/etcd2
|
|
||||||
# - 2.3.7/etcd2 -> 3.0.17/etcd2
|
|
||||||
# - 3.0.17/etcd3 -> 3.1.12/etcd3
|
# - 3.0.17/etcd3 -> 3.1.12/etcd3
|
||||||
# - 3.1.12/etcd3 -> 3.2.24/etcd3
|
# - 3.1.12/etcd3 -> 3.2.24/etcd3
|
||||||
# - 3.2.24/etcd3 -> 3.3.10/etcd3
|
# - 3.2.24/etcd3 -> 3.3.10/etcd3
|
||||||
@ -43,7 +41,7 @@ set -o nounset
|
|||||||
|
|
||||||
# NOTE: BUNDLED_VERSION has to match release binaries present in the
|
# NOTE: BUNDLED_VERSION has to match release binaries present in the
|
||||||
# etcd image (to make this script work correctly).
|
# etcd image (to make this script work correctly).
|
||||||
BUNDLED_VERSIONS="2.2.1, 2.3.7, 3.0.17, 3.1.12, 3.2.24, 3.3.10"
|
BUNDLED_VERSIONS="3.0.17, 3.1.12, 3.2.24, 3.3.10"
|
||||||
|
|
||||||
ETCD_NAME="${ETCD_NAME:-etcd-$(hostname)}"
|
ETCD_NAME="${ETCD_NAME:-etcd-$(hostname)}"
|
||||||
if [ -z "${DATA_DIRECTORY:-}" ]; then
|
if [ -z "${DATA_DIRECTORY:-}" ]; then
|
||||||
|
@ -20,28 +20,13 @@ go_library(
|
|||||||
"migrate_client.go",
|
"migrate_client.go",
|
||||||
"migrate_server.go",
|
"migrate_server.go",
|
||||||
"migrator.go",
|
"migrator.go",
|
||||||
"rollback_v2.go",
|
|
||||||
"versions.go",
|
"versions.go",
|
||||||
],
|
],
|
||||||
importpath = "k8s.io/kubernetes/cluster/images/etcd/migrate",
|
importpath = "k8s.io/kubernetes/cluster/images/etcd/migrate",
|
||||||
deps = [
|
deps = [
|
||||||
"//third_party/forked/etcd221/wal:go_default_library",
|
|
||||||
"//vendor/github.com/blang/semver:go_default_library",
|
"//vendor/github.com/blang/semver:go_default_library",
|
||||||
"//vendor/github.com/coreos/etcd/client:go_default_library",
|
"//vendor/github.com/coreos/etcd/client:go_default_library",
|
||||||
"//vendor/github.com/coreos/etcd/clientv3:go_default_library",
|
"//vendor/github.com/coreos/etcd/clientv3:go_default_library",
|
||||||
"//vendor/github.com/coreos/etcd/etcdserver:go_default_library",
|
|
||||||
"//vendor/github.com/coreos/etcd/etcdserver/etcdserverpb:go_default_library",
|
|
||||||
"//vendor/github.com/coreos/etcd/etcdserver/membership:go_default_library",
|
|
||||||
"//vendor/github.com/coreos/etcd/mvcc/backend:go_default_library",
|
|
||||||
"//vendor/github.com/coreos/etcd/mvcc/mvccpb:go_default_library",
|
|
||||||
"//vendor/github.com/coreos/etcd/pkg/pbutil:go_default_library",
|
|
||||||
"//vendor/github.com/coreos/etcd/pkg/types:go_default_library",
|
|
||||||
"//vendor/github.com/coreos/etcd/raft/raftpb:go_default_library",
|
|
||||||
"//vendor/github.com/coreos/etcd/snap:go_default_library",
|
|
||||||
"//vendor/github.com/coreos/etcd/store:go_default_library",
|
|
||||||
"//vendor/github.com/coreos/etcd/wal:go_default_library",
|
|
||||||
"//vendor/github.com/coreos/etcd/wal/walpb:go_default_library",
|
|
||||||
"//vendor/github.com/coreos/go-semver/semver:go_default_library",
|
|
||||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||||
"//vendor/k8s.io/klog:go_default_library",
|
"//vendor/k8s.io/klog:go_default_library",
|
||||||
],
|
],
|
||||||
|
@ -40,8 +40,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
testSupportedVersions = MustParseSupportedVersions("2.2.1, 2.3.7, 3.0.17, 3.1.12")
|
testSupportedVersions = MustParseSupportedVersions("3.0.17, 3.1.12")
|
||||||
testVersionOldest = &EtcdVersion{semver.MustParse("2.2.1")}
|
|
||||||
testVersionPrevious = &EtcdVersion{semver.MustParse("3.0.17")}
|
testVersionPrevious = &EtcdVersion{semver.MustParse("3.0.17")}
|
||||||
testVersionLatest = &EtcdVersion{semver.MustParse("3.1.12")}
|
testVersionLatest = &EtcdVersion{semver.MustParse("3.1.12")}
|
||||||
)
|
)
|
||||||
@ -55,15 +54,13 @@ func TestMigrate(t *testing.T) {
|
|||||||
protocol string
|
protocol string
|
||||||
}{
|
}{
|
||||||
// upgrades
|
// upgrades
|
||||||
{"v2-v3-up", 1, "2.2.1/etcd2", "3.0.17/etcd3", "https"},
|
|
||||||
{"v3-v3-up", 1, "3.0.17/etcd3", "3.1.12/etcd3", "https"},
|
{"v3-v3-up", 1, "3.0.17/etcd3", "3.1.12/etcd3", "https"},
|
||||||
{"oldest-newest-up", 1, "2.2.1/etcd2", "3.1.12/etcd3", "https"},
|
{"oldest-newest-up", 1, "3.0.17/etcd3", "3.1.12/etcd3", "https"},
|
||||||
|
|
||||||
// warning: v2->v3 ha upgrades not currently supported.
|
// warning: v2->v3 ha upgrades not currently supported.
|
||||||
{"ha-v3-v3-up", 3, "3.0.17/etcd3", "3.1.12/etcd3", "https"},
|
{"ha-v3-v3-up", 3, "3.0.17/etcd3", "3.1.12/etcd3", "https"},
|
||||||
|
|
||||||
// downgrades
|
// downgrades
|
||||||
{"v3-v2-down", 1, "3.0.17/etcd3", "2.2.1/etcd2", "https"},
|
|
||||||
{"v3-v3-down", 1, "3.1.12/etcd3", "3.0.17/etcd3", "https"},
|
{"v3-v3-down", 1, "3.1.12/etcd3", "3.0.17/etcd3", "https"},
|
||||||
|
|
||||||
// warning: ha downgrades not yet supported.
|
// warning: ha downgrades not yet supported.
|
||||||
|
@ -95,11 +95,9 @@ func (m *Migrator) MigrateIfNeeded(target *EtcdVersionPair) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
case current.storageVersion == storageEtcd2 && target.storageVersion == storageEtcd3:
|
case current.storageVersion == storageEtcd2 && target.storageVersion == storageEtcd3:
|
||||||
klog.Info("upgrading from etcd2 storage to etcd3 storage")
|
return fmt.Errorf("upgrading from etcd2 storage to etcd3 storage is not supported")
|
||||||
current, err = m.etcd2ToEtcd3Upgrade(current, target)
|
|
||||||
case current.version.Major == 3 && target.version.Major == 2:
|
case current.version.Major == 3 && target.version.Major == 2:
|
||||||
klog.Info("downgrading from etcd 3.x to 2.x")
|
return fmt.Errorf("downgrading from etcd 3.x to 2.x is not supported")
|
||||||
current, err = m.rollbackToEtcd2(current, target)
|
|
||||||
case current.version.Major == target.version.Major && current.version.Minor < target.version.Minor:
|
case current.version.Major == target.version.Major && current.version.Minor < target.version.Minor:
|
||||||
stepVersion := m.cfg.supportedVersions.NextVersionPair(current)
|
stepVersion := m.cfg.supportedVersions.NextVersionPair(current)
|
||||||
klog.Infof("upgrading etcd from %s to %s", current, stepVersion)
|
klog.Infof("upgrading etcd from %s to %s", current, stepVersion)
|
||||||
@ -114,18 +112,6 @@ func (m *Migrator) MigrateIfNeeded(target *EtcdVersionPair) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Migrator) backupEtcd2(current *EtcdVersion) error {
|
|
||||||
backupDir := fmt.Sprintf("%s/%s", m.dataDirectory, "migration-backup")
|
|
||||||
klog.Info("Backup etcd before starting migration")
|
|
||||||
err := os.Mkdir(backupDir, 0666)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to create backup directory before starting migration: %v", err)
|
|
||||||
}
|
|
||||||
m.client.Backup(current, backupDir)
|
|
||||||
klog.Infof("Backup done in %s", backupDir)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Migrator) rollbackEtcd3MinorVersion(current *EtcdVersionPair, target *EtcdVersionPair) (*EtcdVersionPair, error) {
|
func (m *Migrator) rollbackEtcd3MinorVersion(current *EtcdVersionPair, target *EtcdVersionPair) (*EtcdVersionPair, error) {
|
||||||
if target.version.Minor != current.version.Minor-1 {
|
if target.version.Minor != current.version.Minor-1 {
|
||||||
return nil, fmt.Errorf("rollback from %s to %s not supported, only rollbacks to the previous minor version are supported", current.version, target.version)
|
return nil, fmt.Errorf("rollback from %s to %s not supported, only rollbacks to the previous minor version are supported", current.version, target.version)
|
||||||
@ -191,56 +177,6 @@ func (m *Migrator) rollbackEtcd3MinorVersion(current *EtcdVersionPair, target *E
|
|||||||
return target, nil
|
return target, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Migrator) rollbackToEtcd2(current *EtcdVersionPair, target *EtcdVersionPair) (*EtcdVersionPair, error) {
|
|
||||||
if !(current.version.Major == 3 && current.version.Minor == 0 && target.version.Major == 2 && target.version.Minor == 2) {
|
|
||||||
return nil, fmt.Errorf("etcd3 -> etcd2 downgrade is supported only between 3.0.x and 2.2.x, got current %s target %s", current, target)
|
|
||||||
}
|
|
||||||
klog.Info("Backup and remove all existing v2 data")
|
|
||||||
err := m.dataDirectory.Backup()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = RollbackV3ToV2(m.dataDirectory.path, time.Hour)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("rollback to etcd 2.x failed: %v", err)
|
|
||||||
}
|
|
||||||
return target, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Migrator) etcd2ToEtcd3Upgrade(current *EtcdVersionPair, target *EtcdVersionPair) (*EtcdVersionPair, error) {
|
|
||||||
if current.storageVersion != storageEtcd2 || target.version.Major != 3 || target.storageVersion != storageEtcd3 {
|
|
||||||
return nil, fmt.Errorf("etcd2 to etcd3 upgrade is supported only for x.x.x/etcd2 to 3.0.x/etcd3, got current %s target %s", current, target)
|
|
||||||
}
|
|
||||||
runner := m.newServer()
|
|
||||||
|
|
||||||
klog.Info("Performing etcd2 -> etcd3 migration")
|
|
||||||
err := m.client.Migrate(target.version)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
klog.Info("Attaching leases to TTL entries")
|
|
||||||
|
|
||||||
// Now attach lease to all keys.
|
|
||||||
// To do it, we temporarily start etcd on a random port (so that
|
|
||||||
// apiserver actually cannot access it).
|
|
||||||
err = runner.Start(target.version)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
err = runner.Stop()
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Create a lease and attach all keys to it.
|
|
||||||
err = m.client.AttachLease(1 * time.Hour)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return target, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Migrator) minorVersionUpgrade(current *EtcdVersionPair, target *EtcdVersionPair) (*EtcdVersionPair, error) {
|
func (m *Migrator) minorVersionUpgrade(current *EtcdVersionPair, target *EtcdVersionPair) (*EtcdVersionPair, error) {
|
||||||
runner := m.newServer()
|
runner := m.newServer()
|
||||||
|
|
||||||
|
@ -1,347 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2016 The Kubernetes 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 main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
// Uncomment when you want to rollback to 2.2.1 version.
|
|
||||||
oldwal "k8s.io/kubernetes/third_party/forked/etcd221/wal"
|
|
||||||
// Uncomment when you want to rollback to 2.3.7 version.
|
|
||||||
// oldwal "k8s.io/kubernetes/third_party/forked/etcd237/wal"
|
|
||||||
|
|
||||||
"github.com/coreos/etcd/etcdserver"
|
|
||||||
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
|
||||||
"github.com/coreos/etcd/etcdserver/membership"
|
|
||||||
"github.com/coreos/etcd/mvcc/backend"
|
|
||||||
"github.com/coreos/etcd/mvcc/mvccpb"
|
|
||||||
"github.com/coreos/etcd/pkg/pbutil"
|
|
||||||
"github.com/coreos/etcd/pkg/types"
|
|
||||||
"github.com/coreos/etcd/raft/raftpb"
|
|
||||||
"github.com/coreos/etcd/snap"
|
|
||||||
"github.com/coreos/etcd/store"
|
|
||||||
"github.com/coreos/etcd/wal"
|
|
||||||
"github.com/coreos/etcd/wal/walpb"
|
|
||||||
"github.com/coreos/go-semver/semver"
|
|
||||||
"k8s.io/klog"
|
|
||||||
)
|
|
||||||
|
|
||||||
const rollbackVersion = "2.2.0"
|
|
||||||
|
|
||||||
// RollbackV3ToV2 rolls back an etcd 3.0.x data directory to the 2.x.x version specified by rollbackVersion.
|
|
||||||
func RollbackV3ToV2(migrateDatadir string, ttl time.Duration) error {
|
|
||||||
dbpath := path.Join(migrateDatadir, "member", "snap", "db")
|
|
||||||
klog.Infof("Rolling db file %s back to etcd 2.x", dbpath)
|
|
||||||
|
|
||||||
// etcd3 store backend. We will use it to parse v3 data files and extract information.
|
|
||||||
be := backend.NewDefaultBackend(dbpath)
|
|
||||||
tx := be.BatchTx()
|
|
||||||
|
|
||||||
// etcd2 store backend. We will use v3 data to update this and then save snapshot to disk.
|
|
||||||
st := store.New(etcdserver.StoreClusterPrefix, etcdserver.StoreKeysPrefix)
|
|
||||||
expireTime := time.Now().Add(ttl)
|
|
||||||
|
|
||||||
tx.Lock()
|
|
||||||
err := tx.UnsafeForEach([]byte("key"), func(k, v []byte) error {
|
|
||||||
kv := &mvccpb.KeyValue{}
|
|
||||||
kv.Unmarshal(v)
|
|
||||||
|
|
||||||
// This is compact key.
|
|
||||||
if !strings.HasPrefix(string(kv.Key), "/") {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ttlOpt := store.TTLOptionSet{}
|
|
||||||
if kv.Lease != 0 {
|
|
||||||
ttlOpt = store.TTLOptionSet{ExpireTime: expireTime}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isTombstone(k) {
|
|
||||||
sk := path.Join(strings.Trim(etcdserver.StoreKeysPrefix, "/"), string(kv.Key))
|
|
||||||
_, err := st.Set(sk, false, string(kv.Value), ttlOpt)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
st.Delete(string(kv.Key), false, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tx.Unlock()
|
|
||||||
|
|
||||||
if err := traverseAndDeleteEmptyDir(st, "/"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// rebuild cluster state.
|
|
||||||
metadata, hardstate, oldSt, err := rebuild(migrateDatadir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// In the following, it's low level logic that saves metadata and data into v2 snapshot.
|
|
||||||
backupPath := migrateDatadir + ".rollback.backup"
|
|
||||||
if err := os.Rename(migrateDatadir, backupPath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := os.MkdirAll(path.Join(migrateDatadir, "member", "snap"), 0777); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
walDir := path.Join(migrateDatadir, "member", "wal")
|
|
||||||
|
|
||||||
w, err := oldwal.Create(walDir, metadata)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = w.SaveSnapshot(walpb.Snapshot{Index: hardstate.Commit, Term: hardstate.Term})
|
|
||||||
w.Close()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
event, err := oldSt.Get(etcdserver.StoreClusterPrefix, true, false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// nodes (members info) for ConfState
|
|
||||||
nodes := []uint64{}
|
|
||||||
traverseMetadata(event.Node, func(n *store.NodeExtern) {
|
|
||||||
if n.Key != etcdserver.StoreClusterPrefix {
|
|
||||||
// update store metadata
|
|
||||||
v := ""
|
|
||||||
if !n.Dir {
|
|
||||||
v = *n.Value
|
|
||||||
}
|
|
||||||
if n.Key == path.Join(etcdserver.StoreClusterPrefix, "version") {
|
|
||||||
v = rollbackVersion
|
|
||||||
}
|
|
||||||
if _, err := st.Set(n.Key, n.Dir, v, store.TTLOptionSet{}); err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// update nodes
|
|
||||||
fields := strings.Split(n.Key, "/")
|
|
||||||
if len(fields) == 4 && fields[2] == "members" {
|
|
||||||
nodeID, err := strconv.ParseUint(fields[3], 16, 64)
|
|
||||||
if err != nil {
|
|
||||||
klog.Fatalf("failed to parse member ID (%s): %v", fields[3], err)
|
|
||||||
}
|
|
||||||
nodes = append(nodes, nodeID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
data, err := st.Save()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
raftSnap := raftpb.Snapshot{
|
|
||||||
Data: data,
|
|
||||||
Metadata: raftpb.SnapshotMetadata{
|
|
||||||
Index: hardstate.Commit,
|
|
||||||
Term: hardstate.Term,
|
|
||||||
ConfState: raftpb.ConfState{
|
|
||||||
Nodes: nodes,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
snapshotter := snap.New(path.Join(migrateDatadir, "member", "snap"))
|
|
||||||
if err := snapshotter.SaveSnap(raftSnap); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
klog.Info("Finished successfully")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func traverseMetadata(head *store.NodeExtern, handleFunc func(*store.NodeExtern)) {
|
|
||||||
q := []*store.NodeExtern{head}
|
|
||||||
|
|
||||||
for len(q) > 0 {
|
|
||||||
n := q[0]
|
|
||||||
q = q[1:]
|
|
||||||
|
|
||||||
handleFunc(n)
|
|
||||||
|
|
||||||
q = append(q, n.Nodes...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
revBytesLen = 8 + 1 + 8
|
|
||||||
markedRevBytesLen = revBytesLen + 1
|
|
||||||
markBytePosition = markedRevBytesLen - 1
|
|
||||||
|
|
||||||
markTombstone byte = 't'
|
|
||||||
)
|
|
||||||
|
|
||||||
func isTombstone(b []byte) bool {
|
|
||||||
return len(b) == markedRevBytesLen && b[markBytePosition] == markTombstone
|
|
||||||
}
|
|
||||||
|
|
||||||
func traverseAndDeleteEmptyDir(st store.Store, dir string) error {
|
|
||||||
e, err := st.Get(dir, true, false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(e.Node.Nodes) == 0 {
|
|
||||||
st.Delete(dir, true, true)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for _, node := range e.Node.Nodes {
|
|
||||||
if !node.Dir {
|
|
||||||
klog.V(2).Infof("key: %s", node.Key[len(etcdserver.StoreKeysPrefix):])
|
|
||||||
} else {
|
|
||||||
err := traverseAndDeleteEmptyDir(st, node.Key)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func rebuild(datadir string) ([]byte, *raftpb.HardState, store.Store, error) {
|
|
||||||
waldir := path.Join(datadir, "member", "wal")
|
|
||||||
snapdir := path.Join(datadir, "member", "snap")
|
|
||||||
|
|
||||||
ss := snap.New(snapdir)
|
|
||||||
snapshot, err := ss.Load()
|
|
||||||
if err != nil && err != snap.ErrNoSnapshot {
|
|
||||||
return nil, nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var walsnap walpb.Snapshot
|
|
||||||
if snapshot != nil {
|
|
||||||
walsnap.Index, walsnap.Term = snapshot.Metadata.Index, snapshot.Metadata.Term
|
|
||||||
}
|
|
||||||
|
|
||||||
w, err := wal.OpenForRead(waldir, walsnap)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, nil, err
|
|
||||||
}
|
|
||||||
defer w.Close()
|
|
||||||
|
|
||||||
meta, hardstate, ents, err := w.ReadAll()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
st := store.New(etcdserver.StoreClusterPrefix, etcdserver.StoreKeysPrefix)
|
|
||||||
if snapshot != nil {
|
|
||||||
err := st.Recovery(snapshot.Data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cluster := membership.NewCluster("")
|
|
||||||
cluster.SetStore(st)
|
|
||||||
cluster.Recover(func(*semver.Version) {})
|
|
||||||
|
|
||||||
applier := etcdserver.NewApplierV2(st, cluster)
|
|
||||||
for _, ent := range ents {
|
|
||||||
if ent.Type == raftpb.EntryConfChange {
|
|
||||||
var cc raftpb.ConfChange
|
|
||||||
pbutil.MustUnmarshal(&cc, ent.Data)
|
|
||||||
switch cc.Type {
|
|
||||||
case raftpb.ConfChangeAddNode:
|
|
||||||
m := new(membership.Member)
|
|
||||||
if err := json.Unmarshal(cc.Context, m); err != nil {
|
|
||||||
return nil, nil, nil, err
|
|
||||||
}
|
|
||||||
cluster.AddMember(m)
|
|
||||||
case raftpb.ConfChangeRemoveNode:
|
|
||||||
id := types.ID(cc.NodeID)
|
|
||||||
cluster.RemoveMember(id)
|
|
||||||
case raftpb.ConfChangeUpdateNode:
|
|
||||||
m := new(membership.Member)
|
|
||||||
if err := json.Unmarshal(cc.Context, m); err != nil {
|
|
||||||
return nil, nil, nil, err
|
|
||||||
}
|
|
||||||
cluster.UpdateRaftAttributes(m.ID, m.RaftAttributes)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var raftReq pb.InternalRaftRequest
|
|
||||||
if !pbutil.MaybeUnmarshal(&raftReq, ent.Data) { // backward compatible
|
|
||||||
var r pb.Request
|
|
||||||
pbutil.MustUnmarshal(&r, ent.Data)
|
|
||||||
applyRequest(&r, applier)
|
|
||||||
} else {
|
|
||||||
if raftReq.V2 != nil {
|
|
||||||
req := raftReq.V2
|
|
||||||
applyRequest(req, applier)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return meta, &hardstate, st, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func toTTLOptions(r *pb.Request) store.TTLOptionSet {
|
|
||||||
refresh, _ := pbutil.GetBool(r.Refresh)
|
|
||||||
ttlOptions := store.TTLOptionSet{Refresh: refresh}
|
|
||||||
if r.Expiration != 0 {
|
|
||||||
ttlOptions.ExpireTime = time.Unix(0, r.Expiration)
|
|
||||||
}
|
|
||||||
return ttlOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
func applyRequest(r *pb.Request, applyV2 etcdserver.ApplierV2) {
|
|
||||||
// TODO: find a sane way to perform this cast or avoid it in the first place
|
|
||||||
reqV2 := &etcdserver.RequestV2{
|
|
||||||
ID: r.ID,
|
|
||||||
Method: r.Method,
|
|
||||||
Path: r.Path,
|
|
||||||
Val: r.Val,
|
|
||||||
Dir: r.Dir,
|
|
||||||
PrevValue: r.PrevValue,
|
|
||||||
PrevIndex: r.PrevIndex,
|
|
||||||
PrevExist: r.PrevExist,
|
|
||||||
Expiration: r.Expiration,
|
|
||||||
Wait: r.Wait,
|
|
||||||
Since: r.Since,
|
|
||||||
Recursive: r.Recursive,
|
|
||||||
Sorted: r.Sorted,
|
|
||||||
Quorum: r.Quorum,
|
|
||||||
Time: r.Time,
|
|
||||||
Stream: r.Stream,
|
|
||||||
Refresh: r.Refresh,
|
|
||||||
XXX_unrecognized: r.XXX_unrecognized,
|
|
||||||
}
|
|
||||||
toTTLOptions(r)
|
|
||||||
switch r.Method {
|
|
||||||
case "PUT":
|
|
||||||
applyV2.Put(reqV2)
|
|
||||||
case "DELETE":
|
|
||||||
applyV2.Delete(reqV2)
|
|
||||||
case "POST", "QGET", "SYNC":
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
klog.Fatal("unknown command")
|
|
||||||
}
|
|
||||||
}
|
|
@ -29,12 +29,8 @@ func TestSerializeEtcdVersionPair(t *testing.T) {
|
|||||||
match bool
|
match bool
|
||||||
}{
|
}{
|
||||||
{"3.1.2/etcd3", &EtcdVersionPair{&EtcdVersion{semver.MustParse("3.1.2")}, storageEtcd3}, true},
|
{"3.1.2/etcd3", &EtcdVersionPair{&EtcdVersion{semver.MustParse("3.1.2")}, storageEtcd3}, true},
|
||||||
{"2.2.1/etcd2", &EtcdVersionPair{&EtcdVersion{semver.MustParse("2.2.1")}, storageEtcd2}, true},
|
|
||||||
{"1.1.1-rc.0/etcd3", &EtcdVersionPair{&EtcdVersion{semver.MustParse("1.1.1-rc.0")}, storageEtcd3}, true},
|
{"1.1.1-rc.0/etcd3", &EtcdVersionPair{&EtcdVersion{semver.MustParse("1.1.1-rc.0")}, storageEtcd3}, true},
|
||||||
{"10.100.1000/etcd3", &EtcdVersionPair{&EtcdVersion{semver.MustParse("10.100.1000")}, storageEtcd3}, true},
|
{"10.100.1000/etcd3", &EtcdVersionPair{&EtcdVersion{semver.MustParse("10.100.1000")}, storageEtcd3}, true},
|
||||||
|
|
||||||
{"2.2.2/etcd2", &EtcdVersionPair{&EtcdVersion{semver.MustParse("2.2.1")}, storageEtcd2}, false},
|
|
||||||
{"2.2.1/etcd3", &EtcdVersionPair{&EtcdVersion{semver.MustParse("2.2.1")}, storageEtcd2}, false},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
# $ etcdctl --endpoints=<address> snapshot save
|
# $ etcdctl --endpoints=<address> snapshot save
|
||||||
# produced .db file
|
# produced .db file
|
||||||
# - version.txt file is in the current directory (if it isn't it will be
|
# - version.txt file is in the current directory (if it isn't it will be
|
||||||
# defaulted to "2.2.1/etcd2"). Based on this file, the script will
|
# defaulted to "3.0.17/etcd3"). Based on this file, the script will
|
||||||
# decide to which version we are restoring (procedures are different
|
# decide to which version we are restoring (procedures are different
|
||||||
# for etcd2 and etcd3).
|
# for etcd2 and etcd3).
|
||||||
# - in case of etcd2 - *.snap and *.wal files are in current directory
|
# - in case of etcd2 - *.snap and *.wal files are in current directory
|
||||||
@ -40,7 +40,7 @@ set -o pipefail
|
|||||||
# Version file contains information about current version in the format:
|
# Version file contains information about current version in the format:
|
||||||
# <etcd binary version>/<etcd api mode> (e.g. "3.0.12/etcd3").
|
# <etcd binary version>/<etcd api mode> (e.g. "3.0.12/etcd3").
|
||||||
#
|
#
|
||||||
# If the file doesn't exist we assume "2.2.1/etcd2" configuration is
|
# If the file doesn't exist we assume "3.0.17/etcd3" configuration is
|
||||||
# the current one and create a file with such configuration.
|
# the current one and create a file with such configuration.
|
||||||
# The restore procedure is chosen based on this information.
|
# The restore procedure is chosen based on this information.
|
||||||
VERSION_FILE="version.txt"
|
VERSION_FILE="version.txt"
|
||||||
@ -51,7 +51,7 @@ if [ -n "${VERSION_CONTENTS:-}" ]; then
|
|||||||
echo "${VERSION_CONTENTS}" > "${VERSION_FILE}"
|
echo "${VERSION_CONTENTS}" > "${VERSION_FILE}"
|
||||||
fi
|
fi
|
||||||
if [ ! -f "${VERSION_FILE}" ]; then
|
if [ ! -f "${VERSION_FILE}" ]; then
|
||||||
echo "2.2.1/etcd2" > "${VERSION_FILE}"
|
echo "3.0.17/etcd3" > "${VERSION_FILE}"
|
||||||
fi
|
fi
|
||||||
VERSION_CONTENTS="$(cat ${VERSION_FILE})"
|
VERSION_CONTENTS="$(cat ${VERSION_FILE})"
|
||||||
ETCD_VERSION="$(echo "$VERSION_CONTENTS" | cut -d '/' -f 1)"
|
ETCD_VERSION="$(echo "$VERSION_CONTENTS" | cut -d '/' -f 1)"
|
||||||
|
1
go.mod
1
go.mod
@ -39,7 +39,6 @@ require (
|
|||||||
github.com/containerd/typeurl v0.0.0-20190228175220-2a93cfde8c20 // indirect
|
github.com/containerd/typeurl v0.0.0-20190228175220-2a93cfde8c20 // indirect
|
||||||
github.com/containernetworking/cni v0.7.1
|
github.com/containernetworking/cni v0.7.1
|
||||||
github.com/coreos/etcd v3.3.13+incompatible
|
github.com/coreos/etcd v3.3.13+incompatible
|
||||||
github.com/coreos/go-semver v0.3.0
|
|
||||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7
|
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7
|
||||||
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea
|
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea
|
||||||
github.com/coreos/rkt v1.30.0 // indirect
|
github.com/coreos/rkt v1.30.0 // indirect
|
||||||
|
4
third_party/BUILD
vendored
4
third_party/BUILD
vendored
@ -18,10 +18,6 @@ filegroup(
|
|||||||
name = "all-srcs",
|
name = "all-srcs",
|
||||||
srcs = [
|
srcs = [
|
||||||
":package-srcs",
|
":package-srcs",
|
||||||
"//third_party/forked/etcd221/pkg/fileutil:all-srcs",
|
|
||||||
"//third_party/forked/etcd221/wal:all-srcs",
|
|
||||||
"//third_party/forked/etcd237/pkg/fileutil:all-srcs",
|
|
||||||
"//third_party/forked/etcd237/wal:all-srcs",
|
|
||||||
"//third_party/forked/golang/expansion:all-srcs",
|
"//third_party/forked/golang/expansion:all-srcs",
|
||||||
"//third_party/forked/golang/reflect:all-srcs",
|
"//third_party/forked/golang/reflect:all-srcs",
|
||||||
"//third_party/forked/golang/template:all-srcs",
|
"//third_party/forked/golang/template:all-srcs",
|
||||||
|
1
third_party/forked/etcd221/README.md
vendored
1
third_party/forked/etcd221/README.md
vendored
@ -1 +0,0 @@
|
|||||||
Forked from etcd 2.2 release branch to support migration from 3.0 WAL to 2.2 WAL format
|
|
51
third_party/forked/etcd221/pkg/fileutil/BUILD
vendored
51
third_party/forked/etcd221/pkg/fileutil/BUILD
vendored
@ -1,51 +0,0 @@
|
|||||||
package(default_visibility = ["//visibility:public"])
|
|
||||||
|
|
||||||
licenses(["notice"])
|
|
||||||
|
|
||||||
load(
|
|
||||||
"@io_bazel_rules_go//go:def.bzl",
|
|
||||||
"go_library",
|
|
||||||
"go_test",
|
|
||||||
)
|
|
||||||
|
|
||||||
go_test(
|
|
||||||
name = "go_default_test",
|
|
||||||
srcs = [
|
|
||||||
"fileutil_test.go",
|
|
||||||
"lock_test.go",
|
|
||||||
"preallocate_test.go",
|
|
||||||
"purge_test.go",
|
|
||||||
],
|
|
||||||
embed = [":go_default_library"],
|
|
||||||
)
|
|
||||||
|
|
||||||
go_library(
|
|
||||||
name = "go_default_library",
|
|
||||||
srcs = [
|
|
||||||
"fileutil.go",
|
|
||||||
"lock_plan9.go",
|
|
||||||
"lock_solaris.go",
|
|
||||||
"lock_unix.go",
|
|
||||||
"lock_windows.go",
|
|
||||||
"perallocate_unsupported.go",
|
|
||||||
"preallocate.go",
|
|
||||||
"purge.go",
|
|
||||||
],
|
|
||||||
importpath = "k8s.io/kubernetes/third_party/forked/etcd221/pkg/fileutil",
|
|
||||||
deps = [
|
|
||||||
"//vendor/github.com/coreos/pkg/capnslog:go_default_library",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "package-srcs",
|
|
||||||
srcs = glob(["**"]),
|
|
||||||
tags = ["automanaged"],
|
|
||||||
visibility = ["//visibility:private"],
|
|
||||||
)
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "all-srcs",
|
|
||||||
srcs = [":package-srcs"],
|
|
||||||
tags = ["automanaged"],
|
|
||||||
)
|
|
@ -1,57 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 fileutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/coreos/pkg/capnslog"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
privateFileMode = 0600
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
plog = capnslog.NewPackageLogger("github.com/coreos/etcd/pkg", "fileutil")
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsDirWriteable checks if dir is writable by writing and removing a file
|
|
||||||
// to dir. It returns nil if dir is writable.
|
|
||||||
func IsDirWriteable(dir string) error {
|
|
||||||
f := path.Join(dir, ".touch")
|
|
||||||
if err := ioutil.WriteFile(f, []byte(""), privateFileMode); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return os.Remove(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadDir returns the filenames in the given directory in sorted order.
|
|
||||||
func ReadDir(dirpath string) ([]string, error) {
|
|
||||||
dir, err := os.Open(dirpath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer dir.Close()
|
|
||||||
names, err := dir.Readdirnames(-1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
return names, nil
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 fileutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestIsDirWriteable(t *testing.T) {
|
|
||||||
tmpdir, err := ioutil.TempDir("", "")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected ioutil.TempDir error: %v", err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tmpdir)
|
|
||||||
if err := IsDirWriteable(tmpdir); err != nil {
|
|
||||||
t.Fatalf("unexpected IsDirWriteable error: %v", err)
|
|
||||||
}
|
|
||||||
if err := os.Chmod(tmpdir, 0444); err != nil {
|
|
||||||
t.Fatalf("unexpected os.Chmod error: %v", err)
|
|
||||||
}
|
|
||||||
if err := IsDirWriteable(tmpdir); err == nil {
|
|
||||||
t.Fatalf("expected IsDirWriteable to error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReadDir(t *testing.T) {
|
|
||||||
tmpdir, err := ioutil.TempDir("", "")
|
|
||||||
defer os.RemoveAll(tmpdir)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected ioutil.TempDir error: %v", err)
|
|
||||||
}
|
|
||||||
files := []string{"def", "abc", "xyz", "ghi"}
|
|
||||||
for _, f := range files {
|
|
||||||
var fh *os.File
|
|
||||||
fh, err = os.Create(filepath.Join(tmpdir, f))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error creating file: %v", err)
|
|
||||||
}
|
|
||||||
if err := fh.Close(); err != nil {
|
|
||||||
t.Fatalf("error closing file: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fs, err := ReadDir(tmpdir)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error calling ReadDir: %v", err)
|
|
||||||
}
|
|
||||||
wfs := []string{"abc", "def", "ghi", "xyz"}
|
|
||||||
if !reflect.DeepEqual(fs, wfs) {
|
|
||||||
t.Fatalf("ReadDir: got %v, want %v", fs, wfs)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,90 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 fileutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrLocked = errors.New("file already locked")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Lock interface {
|
|
||||||
Name() string
|
|
||||||
TryLock() error
|
|
||||||
Lock() error
|
|
||||||
Unlock() error
|
|
||||||
Destroy() error
|
|
||||||
}
|
|
||||||
|
|
||||||
type lock struct {
|
|
||||||
fname string
|
|
||||||
file *os.File
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Name() string {
|
|
||||||
return l.fname
|
|
||||||
}
|
|
||||||
|
|
||||||
// TryLock acquires exclusivity on the lock without blocking
|
|
||||||
func (l *lock) TryLock() error {
|
|
||||||
err := os.Chmod(l.fname, syscall.DMEXCL|0600)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := os.Open(l.fname)
|
|
||||||
if err != nil {
|
|
||||||
return ErrLocked
|
|
||||||
}
|
|
||||||
|
|
||||||
l.file = f
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lock acquires exclusivity on the lock with blocking
|
|
||||||
func (l *lock) Lock() error {
|
|
||||||
err := os.Chmod(l.fname, syscall.DMEXCL|0600)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
f, err := os.Open(l.fname)
|
|
||||||
if err == nil {
|
|
||||||
l.file = f
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
time.Sleep(10 * time.Millisecond)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unlock unlocks the lock
|
|
||||||
func (l *lock) Unlock() error {
|
|
||||||
return l.file.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Destroy() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLock(file string) (Lock, error) {
|
|
||||||
l := &lock{fname: file}
|
|
||||||
return l, nil
|
|
||||||
}
|
|
@ -1,98 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
// +build solaris
|
|
||||||
|
|
||||||
package fileutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrLocked = errors.New("file already locked")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Lock interface {
|
|
||||||
Name() string
|
|
||||||
TryLock() error
|
|
||||||
Lock() error
|
|
||||||
Unlock() error
|
|
||||||
Destroy() error
|
|
||||||
}
|
|
||||||
|
|
||||||
type lock struct {
|
|
||||||
fd int
|
|
||||||
file *os.File
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Name() string {
|
|
||||||
return l.file.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TryLock acquires exclusivity on the lock without blocking
|
|
||||||
func (l *lock) TryLock() error {
|
|
||||||
var lock syscall.Flock_t
|
|
||||||
lock.Start = 0
|
|
||||||
lock.Len = 0
|
|
||||||
lock.Pid = 0
|
|
||||||
lock.Type = syscall.F_WRLCK
|
|
||||||
lock.Whence = 0
|
|
||||||
lock.Pid = 0
|
|
||||||
err := syscall.FcntlFlock(uintptr(l.fd), syscall.F_SETLK, &lock)
|
|
||||||
if err != nil && err == syscall.EAGAIN {
|
|
||||||
return ErrLocked
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lock acquires exclusivity on the lock without blocking
|
|
||||||
func (l *lock) Lock() error {
|
|
||||||
var lock syscall.Flock_t
|
|
||||||
lock.Start = 0
|
|
||||||
lock.Len = 0
|
|
||||||
lock.Type = syscall.F_WRLCK
|
|
||||||
lock.Whence = 0
|
|
||||||
lock.Pid = 0
|
|
||||||
return syscall.FcntlFlock(uintptr(l.fd), syscall.F_SETLK, &lock)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unlock unlocks the lock
|
|
||||||
func (l *lock) Unlock() error {
|
|
||||||
var lock syscall.Flock_t
|
|
||||||
lock.Start = 0
|
|
||||||
lock.Len = 0
|
|
||||||
lock.Type = syscall.F_UNLCK
|
|
||||||
lock.Whence = 0
|
|
||||||
err := syscall.FcntlFlock(uintptr(l.fd), syscall.F_SETLK, &lock)
|
|
||||||
if err != nil && err == syscall.EAGAIN {
|
|
||||||
return ErrLocked
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Destroy() error {
|
|
||||||
return l.file.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLock(file string) (Lock, error) {
|
|
||||||
f, err := os.OpenFile(file, os.O_WRONLY, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
l := &lock{int(f.Fd()), f}
|
|
||||||
return l, nil
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 fileutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestLockAndUnlock(t *testing.T) {
|
|
||||||
f, err := ioutil.TempFile("", "lock")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
f.Close()
|
|
||||||
defer func() {
|
|
||||||
err := os.Remove(f.Name())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// lock the file
|
|
||||||
l, err := NewLock(f.Name())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer l.Destroy()
|
|
||||||
err = l.Lock()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// try lock a locked file
|
|
||||||
dupl, err := NewLock(f.Name())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
err = dupl.TryLock()
|
|
||||||
if err != ErrLocked {
|
|
||||||
t.Errorf("err = %v, want %v", err, ErrLocked)
|
|
||||||
}
|
|
||||||
|
|
||||||
// unlock the file
|
|
||||||
err = l.Unlock()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// try lock the unlocked file
|
|
||||||
err = dupl.TryLock()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("err = %v, want %v", err, nil)
|
|
||||||
}
|
|
||||||
defer dupl.Destroy()
|
|
||||||
|
|
||||||
// blocking on locked file
|
|
||||||
locked := make(chan struct{}, 1)
|
|
||||||
go func() {
|
|
||||||
l.Lock()
|
|
||||||
locked <- struct{}{}
|
|
||||||
}()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-locked:
|
|
||||||
t.Error("unexpected unblocking")
|
|
||||||
case <-time.After(10 * time.Millisecond):
|
|
||||||
}
|
|
||||||
|
|
||||||
// unlock
|
|
||||||
err = dupl.Unlock()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// the previously blocked routine should be unblocked
|
|
||||||
select {
|
|
||||||
case <-locked:
|
|
||||||
case <-time.After(20 * time.Millisecond):
|
|
||||||
t.Error("unexpected blocking")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,76 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
// +build !windows,!plan9,!solaris
|
|
||||||
|
|
||||||
package fileutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrLocked = errors.New("file already locked")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Lock interface {
|
|
||||||
Name() string
|
|
||||||
TryLock() error
|
|
||||||
Lock() error
|
|
||||||
Unlock() error
|
|
||||||
Destroy() error
|
|
||||||
}
|
|
||||||
|
|
||||||
type lock struct {
|
|
||||||
fd int
|
|
||||||
file *os.File
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Name() string {
|
|
||||||
return l.file.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TryLock acquires exclusivity on the lock without blocking
|
|
||||||
func (l *lock) TryLock() error {
|
|
||||||
err := syscall.Flock(l.fd, syscall.LOCK_EX|syscall.LOCK_NB)
|
|
||||||
if err != nil && err == syscall.EWOULDBLOCK {
|
|
||||||
return ErrLocked
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lock acquires exclusivity on the lock without blocking
|
|
||||||
func (l *lock) Lock() error {
|
|
||||||
return syscall.Flock(l.fd, syscall.LOCK_EX)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unlock unlocks the lock
|
|
||||||
func (l *lock) Unlock() error {
|
|
||||||
return syscall.Flock(l.fd, syscall.LOCK_UN)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Destroy() error {
|
|
||||||
return l.file.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLock(file string) (Lock, error) {
|
|
||||||
f, err := os.Open(file)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
l := &lock{int(f.Fd()), f}
|
|
||||||
return l, nil
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package fileutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrLocked = errors.New("file already locked")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Lock interface {
|
|
||||||
Name() string
|
|
||||||
TryLock() error
|
|
||||||
Lock() error
|
|
||||||
Unlock() error
|
|
||||||
Destroy() error
|
|
||||||
}
|
|
||||||
|
|
||||||
type lock struct {
|
|
||||||
fd int
|
|
||||||
file *os.File
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Name() string {
|
|
||||||
return l.file.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TryLock acquires exclusivity on the lock without blocking
|
|
||||||
func (l *lock) TryLock() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lock acquires exclusivity on the lock without blocking
|
|
||||||
func (l *lock) Lock() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unlock unlocks the lock
|
|
||||||
func (l *lock) Unlock() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Destroy() error {
|
|
||||||
return l.file.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLock(file string) (Lock, error) {
|
|
||||||
f, err := os.Open(file)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
l := &lock{int(f.Fd()), f}
|
|
||||||
return l, nil
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
// +build !linux
|
|
||||||
|
|
||||||
package fileutil
|
|
||||||
|
|
||||||
import "os"
|
|
||||||
|
|
||||||
// Preallocate tries to allocate the space for given
|
|
||||||
// file. This operation is only supported on linux by a
|
|
||||||
// few filesystems (btrfs, ext4, etc.).
|
|
||||||
// If the operation is unsupported, no error will be returned.
|
|
||||||
// Otherwise, the error encountered will be returned.
|
|
||||||
func Preallocate(f *os.File, sizeInBytes int) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
// +build linux
|
|
||||||
|
|
||||||
package fileutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Preallocate tries to allocate the space for given
|
|
||||||
// file. This operation is only supported on linux by a
|
|
||||||
// few filesystems (btrfs, ext4, etc.).
|
|
||||||
// If the operation is unsupported, no error will be returned.
|
|
||||||
// Otherwise, the error encountered will be returned.
|
|
||||||
func Preallocate(f *os.File, sizeInBytes int) error {
|
|
||||||
// use mode = 1 to keep size
|
|
||||||
// see FALLOC_FL_KEEP_SIZE
|
|
||||||
err := syscall.Fallocate(int(f.Fd()), 1, 0, int64(sizeInBytes))
|
|
||||||
if err != nil {
|
|
||||||
errno, ok := err.(syscall.Errno)
|
|
||||||
// treat not support as nil error
|
|
||||||
if ok && errno == syscall.ENOTSUP {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 fileutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPreallocate(t *testing.T) {
|
|
||||||
if runtime.GOOS != "linux" {
|
|
||||||
t.Skipf("skip testPreallocate, OS = %s", runtime.GOOS)
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err := ioutil.TempDir(os.TempDir(), "preallocateTest")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(p)
|
|
||||||
|
|
||||||
f, err := ioutil.TempFile(p, "")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
size := 64 * 1000
|
|
||||||
err = Preallocate(f, size)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
stat, err := f.Stat()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if stat.Size() != 0 {
|
|
||||||
t.Errorf("size = %d, want %d", stat.Size(), 0)
|
|
||||||
}
|
|
||||||
}
|
|
80
third_party/forked/etcd221/pkg/fileutil/purge.go
vendored
80
third_party/forked/etcd221/pkg/fileutil/purge.go
vendored
@ -1,80 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 fileutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func PurgeFile(dirname string, suffix string, max uint, interval time.Duration, stop <-chan struct{}) <-chan error {
|
|
||||||
errC := make(chan error, 1)
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
fnames, err := ReadDir(dirname)
|
|
||||||
if err != nil {
|
|
||||||
errC <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
newfnames := make([]string, 0)
|
|
||||||
for _, fname := range fnames {
|
|
||||||
if strings.HasSuffix(fname, suffix) {
|
|
||||||
newfnames = append(newfnames, fname)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Strings(newfnames)
|
|
||||||
for len(newfnames) > int(max) {
|
|
||||||
f := path.Join(dirname, newfnames[0])
|
|
||||||
l, err := NewLock(f)
|
|
||||||
if err != nil {
|
|
||||||
errC <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = l.TryLock()
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
err = os.Remove(f)
|
|
||||||
if err != nil {
|
|
||||||
errC <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = l.Unlock()
|
|
||||||
if err != nil {
|
|
||||||
plog.Errorf("error unlocking %s when purging file (%v)", l.Name(), err)
|
|
||||||
errC <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = l.Destroy()
|
|
||||||
if err != nil {
|
|
||||||
plog.Errorf("error destroying lock %s when purging file (%v)", l.Name(), err)
|
|
||||||
errC <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
plog.Infof("purged file %s successfully", f)
|
|
||||||
newfnames = newfnames[1:]
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-time.After(interval):
|
|
||||||
case <-stop:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return errC
|
|
||||||
}
|
|
@ -1,132 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 fileutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPurgeFile(t *testing.T) {
|
|
||||||
dir, err := ioutil.TempDir("", "purgefile")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
for i := 0; i < 5; i++ {
|
|
||||||
_, err = os.Create(path.Join(dir, fmt.Sprintf("%d.test", i)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stop := make(chan struct{})
|
|
||||||
errch := PurgeFile(dir, "test", 3, time.Millisecond, stop)
|
|
||||||
for i := 5; i < 10; i++ {
|
|
||||||
_, err = os.Create(path.Join(dir, fmt.Sprintf("%d.test", i)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
time.Sleep(10 * time.Millisecond)
|
|
||||||
}
|
|
||||||
fnames, err := ReadDir(dir)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
wnames := []string{"7.test", "8.test", "9.test"}
|
|
||||||
if !reflect.DeepEqual(fnames, wnames) {
|
|
||||||
t.Errorf("filenames = %v, want %v", fnames, wnames)
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case err := <-errch:
|
|
||||||
t.Errorf("unexpected purge error %v", err)
|
|
||||||
case <-time.After(time.Millisecond):
|
|
||||||
}
|
|
||||||
close(stop)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPurgeFileHoldingLock(t *testing.T) {
|
|
||||||
dir, err := ioutil.TempDir("", "purgefile")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
_, err = os.Create(path.Join(dir, fmt.Sprintf("%d.test", i)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a purge barrier at 5
|
|
||||||
l, err := NewLock(path.Join(dir, fmt.Sprintf("%d.test", 5)))
|
|
||||||
err = l.Lock()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
stop := make(chan struct{})
|
|
||||||
errch := PurgeFile(dir, "test", 3, time.Millisecond, stop)
|
|
||||||
time.Sleep(20 * time.Millisecond)
|
|
||||||
|
|
||||||
fnames, err := ReadDir(dir)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
wnames := []string{"5.test", "6.test", "7.test", "8.test", "9.test"}
|
|
||||||
if !reflect.DeepEqual(fnames, wnames) {
|
|
||||||
t.Errorf("filenames = %v, want %v", fnames, wnames)
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case err := <-errch:
|
|
||||||
t.Errorf("unexpected purge error %v", err)
|
|
||||||
case <-time.After(time.Millisecond):
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove the purge barrier
|
|
||||||
err = l.Unlock()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
err = l.Destroy()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(20 * time.Millisecond)
|
|
||||||
|
|
||||||
fnames, err = ReadDir(dir)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
wnames = []string{"7.test", "8.test", "9.test"}
|
|
||||||
if !reflect.DeepEqual(fnames, wnames) {
|
|
||||||
t.Errorf("filenames = %v, want %v", fnames, wnames)
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case err := <-errch:
|
|
||||||
t.Errorf("unexpected purge error %v", err)
|
|
||||||
case <-time.After(time.Millisecond):
|
|
||||||
}
|
|
||||||
|
|
||||||
close(stop)
|
|
||||||
}
|
|
49
third_party/forked/etcd221/wal/BUILD
vendored
49
third_party/forked/etcd221/wal/BUILD
vendored
@ -1,49 +0,0 @@
|
|||||||
package(default_visibility = ["//visibility:public"])
|
|
||||||
|
|
||||||
licenses(["notice"])
|
|
||||||
|
|
||||||
load(
|
|
||||||
"@io_bazel_rules_go//go:def.bzl",
|
|
||||||
"go_library",
|
|
||||||
)
|
|
||||||
|
|
||||||
go_library(
|
|
||||||
name = "go_default_library",
|
|
||||||
srcs = [
|
|
||||||
"decoder.go",
|
|
||||||
"doc.go",
|
|
||||||
"encoder.go",
|
|
||||||
"metrics.go",
|
|
||||||
"multi_readcloser.go",
|
|
||||||
"repair.go",
|
|
||||||
"util.go",
|
|
||||||
"wal.go",
|
|
||||||
],
|
|
||||||
importpath = "k8s.io/kubernetes/third_party/forked/etcd221/wal",
|
|
||||||
deps = [
|
|
||||||
"//third_party/forked/etcd221/pkg/fileutil:go_default_library",
|
|
||||||
"//vendor/github.com/coreos/etcd/pkg/crc:go_default_library",
|
|
||||||
"//vendor/github.com/coreos/etcd/pkg/pbutil:go_default_library",
|
|
||||||
"//vendor/github.com/coreos/etcd/raft:go_default_library",
|
|
||||||
"//vendor/github.com/coreos/etcd/raft/raftpb:go_default_library",
|
|
||||||
"//vendor/github.com/coreos/etcd/wal/walpb:go_default_library",
|
|
||||||
"//vendor/github.com/coreos/pkg/capnslog:go_default_library",
|
|
||||||
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "package-srcs",
|
|
||||||
srcs = glob(["**"]),
|
|
||||||
tags = ["automanaged"],
|
|
||||||
visibility = ["//visibility:private"],
|
|
||||||
)
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "all-srcs",
|
|
||||||
srcs = [
|
|
||||||
":package-srcs",
|
|
||||||
"//third_party/forked/etcd221/wal/walpb:all-srcs",
|
|
||||||
],
|
|
||||||
tags = ["automanaged"],
|
|
||||||
)
|
|
103
third_party/forked/etcd221/wal/decoder.go
vendored
103
third_party/forked/etcd221/wal/decoder.go
vendored
@ -1,103 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 wal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/binary"
|
|
||||||
"hash"
|
|
||||||
"io"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/coreos/etcd/pkg/crc"
|
|
||||||
"github.com/coreos/etcd/pkg/pbutil"
|
|
||||||
"github.com/coreos/etcd/raft/raftpb"
|
|
||||||
"github.com/coreos/etcd/wal/walpb"
|
|
||||||
)
|
|
||||||
|
|
||||||
type decoder struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
br *bufio.Reader
|
|
||||||
|
|
||||||
c io.Closer
|
|
||||||
crc hash.Hash32
|
|
||||||
}
|
|
||||||
|
|
||||||
func newDecoder(rc io.ReadCloser) *decoder {
|
|
||||||
return &decoder{
|
|
||||||
br: bufio.NewReader(rc),
|
|
||||||
c: rc,
|
|
||||||
crc: crc.New(0, crcTable),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) decode(rec *walpb.Record) error {
|
|
||||||
d.mu.Lock()
|
|
||||||
defer d.mu.Unlock()
|
|
||||||
|
|
||||||
rec.Reset()
|
|
||||||
l, err := readInt64(d.br)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
data := make([]byte, l)
|
|
||||||
if _, err = io.ReadFull(d.br, data); err != nil {
|
|
||||||
// ReadFull returns io.EOF only if no bytes were read
|
|
||||||
// the decoder should treat this as an ErrUnexpectedEOF instead.
|
|
||||||
if err == io.EOF {
|
|
||||||
err = io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := rec.Unmarshal(data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// skip crc checking if the record type is crcType
|
|
||||||
if rec.Type == crcType {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
d.crc.Write(rec.Data)
|
|
||||||
return rec.Validate(d.crc.Sum32())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) updateCRC(prevCrc uint32) {
|
|
||||||
d.crc = crc.New(prevCrc, crcTable)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) lastCRC() uint32 {
|
|
||||||
return d.crc.Sum32()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) close() error {
|
|
||||||
return d.c.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustUnmarshalEntry(d []byte) raftpb.Entry {
|
|
||||||
var e raftpb.Entry
|
|
||||||
pbutil.MustUnmarshal(&e, d)
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustUnmarshalState(d []byte) raftpb.HardState {
|
|
||||||
var s raftpb.HardState
|
|
||||||
pbutil.MustUnmarshal(&s, d)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func readInt64(r io.Reader) (int64, error) {
|
|
||||||
var n int64
|
|
||||||
err := binary.Read(r, binary.LittleEndian, &n)
|
|
||||||
return n, err
|
|
||||||
}
|
|
68
third_party/forked/etcd221/wal/doc.go
vendored
68
third_party/forked/etcd221/wal/doc.go
vendored
@ -1,68 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 wal provides an implementation of a write ahead log that is used by
|
|
||||||
etcd.
|
|
||||||
|
|
||||||
A WAL is created at a particular directory and is made up of a number of
|
|
||||||
segmented WAL files. Inside of each file the raft state and entries are appended
|
|
||||||
to it with the Save method:
|
|
||||||
|
|
||||||
metadata := []byte{}
|
|
||||||
w, err := wal.Create("/var/lib/etcd", metadata)
|
|
||||||
...
|
|
||||||
err := w.Save(s, ents)
|
|
||||||
|
|
||||||
After saving an raft snapshot to disk, SaveSnapshot method should be called to
|
|
||||||
record it. So WAL can match with the saved snapshot when restarting.
|
|
||||||
|
|
||||||
err := w.SaveSnapshot(walpb.Snapshot{Index: 10, Term: 2})
|
|
||||||
|
|
||||||
When a user has finished using a WAL it must be closed:
|
|
||||||
|
|
||||||
w.Close()
|
|
||||||
|
|
||||||
WAL files are placed inside of the directory in the following format:
|
|
||||||
$seq-$index.wal
|
|
||||||
|
|
||||||
The first WAL file to be created will be 0000000000000000-0000000000000000.wal
|
|
||||||
indicating an initial sequence of 0 and an initial raft index of 0. The first
|
|
||||||
entry written to WAL MUST have raft index 0.
|
|
||||||
|
|
||||||
WAL will cuts its current wal files if its size exceeds 8MB. This will increment an internal
|
|
||||||
sequence number and cause a new file to be created. If the last raft index saved
|
|
||||||
was 0x20 and this is the first time cut has been called on this WAL then the sequence will
|
|
||||||
increment from 0x0 to 0x1. The new file will be: 0000000000000001-0000000000000021.wal.
|
|
||||||
If a second cut issues 0x10 entries with incremental index later then the file will be called:
|
|
||||||
0000000000000002-0000000000000031.wal.
|
|
||||||
|
|
||||||
At a later time a WAL can be opened at a particular snapshot. If there is no
|
|
||||||
snapshot, an empty snapshot should be passed in.
|
|
||||||
|
|
||||||
w, err := wal.Open("/var/lib/etcd", walpb.Snapshot{Index: 10, Term: 2})
|
|
||||||
...
|
|
||||||
|
|
||||||
The snapshot must have been written to the WAL.
|
|
||||||
|
|
||||||
Additional items cannot be Saved to this WAL until all of the items from the given
|
|
||||||
snapshot to the end of the WAL are read first:
|
|
||||||
|
|
||||||
metadata, state, ents, err := w.ReadAll()
|
|
||||||
|
|
||||||
This will give you the metadata, the last raft.State and the slice of
|
|
||||||
raft.Entry items in the log.
|
|
||||||
|
|
||||||
*/
|
|
||||||
package wal
|
|
89
third_party/forked/etcd221/wal/encoder.go
vendored
89
third_party/forked/etcd221/wal/encoder.go
vendored
@ -1,89 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 wal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/binary"
|
|
||||||
"hash"
|
|
||||||
"io"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/coreos/etcd/pkg/crc"
|
|
||||||
"github.com/coreos/etcd/wal/walpb"
|
|
||||||
)
|
|
||||||
|
|
||||||
type encoder struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
bw *bufio.Writer
|
|
||||||
|
|
||||||
crc hash.Hash32
|
|
||||||
buf []byte
|
|
||||||
uint64buf []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func newEncoder(w io.Writer, prevCrc uint32) *encoder {
|
|
||||||
return &encoder{
|
|
||||||
bw: bufio.NewWriter(w),
|
|
||||||
crc: crc.New(prevCrc, crcTable),
|
|
||||||
// 1MB buffer
|
|
||||||
buf: make([]byte, 1024*1024),
|
|
||||||
uint64buf: make([]byte, 8),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *encoder) encode(rec *walpb.Record) error {
|
|
||||||
e.mu.Lock()
|
|
||||||
defer e.mu.Unlock()
|
|
||||||
|
|
||||||
e.crc.Write(rec.Data)
|
|
||||||
rec.Crc = e.crc.Sum32()
|
|
||||||
var (
|
|
||||||
data []byte
|
|
||||||
err error
|
|
||||||
n int
|
|
||||||
)
|
|
||||||
|
|
||||||
if rec.Size() > len(e.buf) {
|
|
||||||
data, err = rec.Marshal()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
n, err = rec.MarshalTo(e.buf)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
data = e.buf[:n]
|
|
||||||
}
|
|
||||||
if err := writeInt64(e.bw, int64(len(data)), e.uint64buf); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = e.bw.Write(data)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *encoder) flush() error {
|
|
||||||
e.mu.Lock()
|
|
||||||
defer e.mu.Unlock()
|
|
||||||
return e.bw.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeInt64(w io.Writer, n int64, buf []byte) error {
|
|
||||||
// http://golang.org/src/encoding/binary/binary.go
|
|
||||||
binary.LittleEndian.PutUint64(buf, uint64(n))
|
|
||||||
_, err := w.Write(buf)
|
|
||||||
return err
|
|
||||||
}
|
|
37
third_party/forked/etcd221/wal/metrics.go
vendored
37
third_party/forked/etcd221/wal/metrics.go
vendored
@ -1,37 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 wal
|
|
||||||
|
|
||||||
import "github.com/prometheus/client_golang/prometheus"
|
|
||||||
|
|
||||||
var (
|
|
||||||
syncDurations = prometheus.NewSummary(prometheus.SummaryOpts{
|
|
||||||
Namespace: "etcd",
|
|
||||||
Subsystem: "wal",
|
|
||||||
Name: "fsync_durations_microseconds",
|
|
||||||
Help: "The latency distributions of fsync called by wal.",
|
|
||||||
})
|
|
||||||
lastIndexSaved = prometheus.NewGauge(prometheus.GaugeOpts{
|
|
||||||
Namespace: "etcd",
|
|
||||||
Subsystem: "wal",
|
|
||||||
Name: "last_index_saved",
|
|
||||||
Help: "The index of the last entry saved by wal.",
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
prometheus.MustRegister(syncDurations)
|
|
||||||
prometheus.MustRegister(lastIndexSaved)
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 wal
|
|
||||||
|
|
||||||
import "io"
|
|
||||||
|
|
||||||
type multiReadCloser struct {
|
|
||||||
closers []io.Closer
|
|
||||||
reader io.Reader
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *multiReadCloser) Close() error {
|
|
||||||
var err error
|
|
||||||
for i := range mc.closers {
|
|
||||||
err = mc.closers[i].Close()
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *multiReadCloser) Read(p []byte) (int, error) {
|
|
||||||
return mc.reader.Read(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func MultiReadCloser(readClosers ...io.ReadCloser) io.ReadCloser {
|
|
||||||
cs := make([]io.Closer, len(readClosers))
|
|
||||||
rs := make([]io.Reader, len(readClosers))
|
|
||||||
for i := range readClosers {
|
|
||||||
cs[i] = readClosers[i]
|
|
||||||
rs[i] = readClosers[i]
|
|
||||||
}
|
|
||||||
r := io.MultiReader(rs...)
|
|
||||||
return &multiReadCloser{cs, r}
|
|
||||||
}
|
|
107
third_party/forked/etcd221/wal/repair.go
vendored
107
third_party/forked/etcd221/wal/repair.go
vendored
@ -1,107 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 wal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
|
|
||||||
"k8s.io/kubernetes/third_party/forked/etcd221/pkg/fileutil"
|
|
||||||
|
|
||||||
"github.com/coreos/etcd/wal/walpb"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Repair tries to repair the unexpectedEOF error in the
|
|
||||||
// last wal file by truncating.
|
|
||||||
func Repair(dirpath string) bool {
|
|
||||||
f, err := openLast(dirpath)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
n := 0
|
|
||||||
rec := &walpb.Record{}
|
|
||||||
|
|
||||||
decoder := newDecoder(f)
|
|
||||||
defer decoder.close()
|
|
||||||
for {
|
|
||||||
err := decoder.decode(rec)
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
n += 8 + rec.Size()
|
|
||||||
// update crc of the decoder when necessary
|
|
||||||
switch rec.Type {
|
|
||||||
case crcType:
|
|
||||||
crc := decoder.crc.Sum32()
|
|
||||||
// current crc of decoder must match the crc of the record.
|
|
||||||
// do no need to match 0 crc, since the decoder is a new one at this case.
|
|
||||||
if crc != 0 && rec.Validate(crc) != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
decoder.updateCRC(rec.Crc)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
case io.EOF:
|
|
||||||
return true
|
|
||||||
case io.ErrUnexpectedEOF:
|
|
||||||
plog.Noticef("repairing %v", f.Name())
|
|
||||||
bf, bferr := os.Create(f.Name() + ".broken")
|
|
||||||
if bferr != nil {
|
|
||||||
plog.Errorf("could not repair %v, failed to create backup file", f.Name())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
defer bf.Close()
|
|
||||||
|
|
||||||
if _, err = f.Seek(0, os.SEEK_SET); err != nil {
|
|
||||||
plog.Errorf("could not repair %v, failed to read file", f.Name())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = io.Copy(bf, f); err != nil {
|
|
||||||
plog.Errorf("could not repair %v, failed to copy file", f.Name())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = f.Truncate(int64(n)); err != nil {
|
|
||||||
plog.Errorf("could not repair %v, failed to truncate file", f.Name())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if err = f.Sync(); err != nil {
|
|
||||||
plog.Errorf("could not repair %v, failed to sync file", f.Name())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
plog.Errorf("could not repair error (%v)", err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// openLast opens the last wal file for read and write.
|
|
||||||
func openLast(dirpath string) (*os.File, error) {
|
|
||||||
names, err := fileutil.ReadDir(dirpath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
names = checkWalNames(names)
|
|
||||||
if len(names) == 0 {
|
|
||||||
return nil, ErrFileNotFound
|
|
||||||
}
|
|
||||||
last := path.Join(dirpath, names[len(names)-1])
|
|
||||||
return os.OpenFile(last, os.O_RDWR, 0)
|
|
||||||
}
|
|
93
third_party/forked/etcd221/wal/util.go
vendored
93
third_party/forked/etcd221/wal/util.go
vendored
@ -1,93 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 wal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"k8s.io/kubernetes/third_party/forked/etcd221/pkg/fileutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
badWalName = errors.New("bad wal name")
|
|
||||||
)
|
|
||||||
|
|
||||||
func Exist(dirpath string) bool {
|
|
||||||
names, err := fileutil.ReadDir(dirpath)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return len(names) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// searchIndex returns the last array index of names whose raft index section is
|
|
||||||
// equal to or smaller than the given index.
|
|
||||||
// The given names MUST be sorted.
|
|
||||||
func searchIndex(names []string, index uint64) (int, bool) {
|
|
||||||
for i := len(names) - 1; i >= 0; i-- {
|
|
||||||
name := names[i]
|
|
||||||
_, curIndex, err := parseWalName(name)
|
|
||||||
if err != nil {
|
|
||||||
plog.Panicf("parse correct name should never fail: %v", err)
|
|
||||||
}
|
|
||||||
if index >= curIndex {
|
|
||||||
return i, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// names should have been sorted based on sequence number.
|
|
||||||
// isValidSeq checks whether seq increases continuously.
|
|
||||||
func isValidSeq(names []string) bool {
|
|
||||||
var lastSeq uint64
|
|
||||||
for _, name := range names {
|
|
||||||
curSeq, _, err := parseWalName(name)
|
|
||||||
if err != nil {
|
|
||||||
plog.Panicf("parse correct name should never fail: %v", err)
|
|
||||||
}
|
|
||||||
if lastSeq != 0 && lastSeq != curSeq-1 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
lastSeq = curSeq
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkWalNames(names []string) []string {
|
|
||||||
wnames := make([]string, 0)
|
|
||||||
for _, name := range names {
|
|
||||||
if _, _, err := parseWalName(name); err != nil {
|
|
||||||
plog.Warningf("ignored file %v in wal", name)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
wnames = append(wnames, name)
|
|
||||||
}
|
|
||||||
return wnames
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseWalName(str string) (seq, index uint64, err error) {
|
|
||||||
if !strings.HasSuffix(str, ".wal") {
|
|
||||||
return 0, 0, badWalName
|
|
||||||
}
|
|
||||||
_, err = fmt.Sscanf(str, "%016x-%016x.wal", &seq, &index)
|
|
||||||
return seq, index, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func walName(seq, index uint64) string {
|
|
||||||
return fmt.Sprintf("%016x-%016x.wal", seq, index)
|
|
||||||
}
|
|
548
third_party/forked/etcd221/wal/wal.go
vendored
548
third_party/forked/etcd221/wal/wal.go
vendored
@ -1,548 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 wal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"hash/crc32"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"reflect"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"k8s.io/kubernetes/third_party/forked/etcd221/pkg/fileutil"
|
|
||||||
|
|
||||||
"github.com/coreos/etcd/pkg/pbutil"
|
|
||||||
"github.com/coreos/etcd/raft"
|
|
||||||
"github.com/coreos/etcd/raft/raftpb"
|
|
||||||
"github.com/coreos/etcd/wal/walpb"
|
|
||||||
"github.com/coreos/pkg/capnslog"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
metadataType int64 = iota + 1
|
|
||||||
entryType
|
|
||||||
stateType
|
|
||||||
crcType
|
|
||||||
snapshotType
|
|
||||||
|
|
||||||
// the owner can make/remove files inside the directory
|
|
||||||
privateDirMode = 0700
|
|
||||||
|
|
||||||
// the expected size of each wal segment file.
|
|
||||||
// the actual size might be bigger than it.
|
|
||||||
segmentSizeBytes = 64 * 1000 * 1000 // 64MB
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "wal")
|
|
||||||
|
|
||||||
ErrMetadataConflict = errors.New("wal: conflicting metadata found")
|
|
||||||
ErrFileNotFound = errors.New("wal: file not found")
|
|
||||||
ErrCRCMismatch = errors.New("wal: crc mismatch")
|
|
||||||
ErrSnapshotMismatch = errors.New("wal: snapshot mismatch")
|
|
||||||
ErrSnapshotNotFound = errors.New("wal: snapshot not found")
|
|
||||||
crcTable = crc32.MakeTable(crc32.Castagnoli)
|
|
||||||
)
|
|
||||||
|
|
||||||
// WAL is a logical repersentation of the stable storage.
|
|
||||||
// WAL is either in read mode or append mode but not both.
|
|
||||||
// A newly created WAL is in append mode, and ready for appending records.
|
|
||||||
// A just opened WAL is in read mode, and ready for reading records.
|
|
||||||
// The WAL will be ready for appending after reading out all the previous records.
|
|
||||||
type WAL struct {
|
|
||||||
dir string // the living directory of the underlay files
|
|
||||||
metadata []byte // metadata recorded at the head of each WAL
|
|
||||||
state raftpb.HardState // hardstate recorded at the head of WAL
|
|
||||||
|
|
||||||
start walpb.Snapshot // snapshot to start reading
|
|
||||||
decoder *decoder // decoder to decode records
|
|
||||||
|
|
||||||
mu sync.Mutex
|
|
||||||
f *os.File // underlay file opened for appending, sync
|
|
||||||
seq uint64 // sequence of the wal file currently used for writes
|
|
||||||
enti uint64 // index of the last entry saved to the wal
|
|
||||||
encoder *encoder // encoder to encode records
|
|
||||||
|
|
||||||
locks []fileutil.Lock // the file locks the WAL is holding (the name is increasing)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create creates a WAL ready for appending records. The given metadata is
|
|
||||||
// recorded at the head of each WAL file, and can be retrieved with ReadAll.
|
|
||||||
func Create(dirpath string, metadata []byte) (*WAL, error) {
|
|
||||||
if Exist(dirpath) {
|
|
||||||
return nil, os.ErrExist
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := os.MkdirAll(dirpath, privateDirMode); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
p := path.Join(dirpath, walName(0, 0))
|
|
||||||
f, err := os.OpenFile(p, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
l, err := fileutil.NewLock(f.Name())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = l.Lock()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
w := &WAL{
|
|
||||||
dir: dirpath,
|
|
||||||
metadata: metadata,
|
|
||||||
seq: 0,
|
|
||||||
f: f,
|
|
||||||
encoder: newEncoder(f, 0),
|
|
||||||
}
|
|
||||||
w.locks = append(w.locks, l)
|
|
||||||
if err := w.saveCrc(0); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := w.encoder.encode(&walpb.Record{Type: metadataType, Data: metadata}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err = w.SaveSnapshot(walpb.Snapshot{}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return w, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open opens the WAL at the given snap.
|
|
||||||
// The snap SHOULD have been previously saved to the WAL, or the following
|
|
||||||
// ReadAll will fail.
|
|
||||||
// The returned WAL is ready to read and the first record will be the one after
|
|
||||||
// the given snap. The WAL cannot be appended to before reading out all of its
|
|
||||||
// previous records.
|
|
||||||
func Open(dirpath string, snap walpb.Snapshot) (*WAL, error) {
|
|
||||||
return openAtIndex(dirpath, snap, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenForRead only opens the wal files for read.
|
|
||||||
// Write on a read only wal panics.
|
|
||||||
func OpenForRead(dirpath string, snap walpb.Snapshot) (*WAL, error) {
|
|
||||||
return openAtIndex(dirpath, snap, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func openAtIndex(dirpath string, snap walpb.Snapshot, write bool) (*WAL, error) {
|
|
||||||
names, err := fileutil.ReadDir(dirpath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
names = checkWalNames(names)
|
|
||||||
if len(names) == 0 {
|
|
||||||
return nil, ErrFileNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
nameIndex, ok := searchIndex(names, snap.Index)
|
|
||||||
if !ok || !isValidSeq(names[nameIndex:]) {
|
|
||||||
return nil, ErrFileNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
// open the wal files for reading
|
|
||||||
rcs := make([]io.ReadCloser, 0)
|
|
||||||
ls := make([]fileutil.Lock, 0)
|
|
||||||
for _, name := range names[nameIndex:] {
|
|
||||||
f, err := os.Open(path.Join(dirpath, name))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
l, err := fileutil.NewLock(f.Name())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = l.TryLock()
|
|
||||||
if err != nil {
|
|
||||||
if write {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rcs = append(rcs, f)
|
|
||||||
ls = append(ls, l)
|
|
||||||
}
|
|
||||||
rc := MultiReadCloser(rcs...)
|
|
||||||
|
|
||||||
// create a WAL ready for reading
|
|
||||||
w := &WAL{
|
|
||||||
dir: dirpath,
|
|
||||||
start: snap,
|
|
||||||
decoder: newDecoder(rc),
|
|
||||||
locks: ls,
|
|
||||||
}
|
|
||||||
|
|
||||||
if write {
|
|
||||||
// open the lastest wal file for appending
|
|
||||||
seq, _, err := parseWalName(names[len(names)-1])
|
|
||||||
if err != nil {
|
|
||||||
rc.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
last := path.Join(dirpath, names[len(names)-1])
|
|
||||||
|
|
||||||
f, err := os.OpenFile(last, os.O_WRONLY|os.O_APPEND, 0)
|
|
||||||
if err != nil {
|
|
||||||
rc.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = fileutil.Preallocate(f, segmentSizeBytes)
|
|
||||||
if err != nil {
|
|
||||||
rc.Close()
|
|
||||||
plog.Errorf("failed to allocate space when creating new wal file (%v)", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
w.f = f
|
|
||||||
w.seq = seq
|
|
||||||
}
|
|
||||||
|
|
||||||
return w, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadAll reads out records of the current WAL.
|
|
||||||
// If opened in write mode, it must read out all records until EOF. Or an error
|
|
||||||
// will be returned.
|
|
||||||
// If opened in read mode, it will try to read all records if possible.
|
|
||||||
// If it cannot read out the expected snap, it will return ErrSnapshotNotFound.
|
|
||||||
// If loaded snap doesn't match with the expected one, it will return
|
|
||||||
// all the records and error ErrSnapshotMismatch.
|
|
||||||
// TODO: detect not-last-snap error.
|
|
||||||
// TODO: maybe loose the checking of match.
|
|
||||||
// After ReadAll, the WAL will be ready for appending new records.
|
|
||||||
func (w *WAL) ReadAll() (metadata []byte, state raftpb.HardState, ents []raftpb.Entry, err error) {
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
|
|
||||||
rec := &walpb.Record{}
|
|
||||||
decoder := w.decoder
|
|
||||||
|
|
||||||
var match bool
|
|
||||||
for err = decoder.decode(rec); err == nil; err = decoder.decode(rec) {
|
|
||||||
switch rec.Type {
|
|
||||||
case entryType:
|
|
||||||
e := mustUnmarshalEntry(rec.Data)
|
|
||||||
if e.Index > w.start.Index {
|
|
||||||
ents = append(ents[:e.Index-w.start.Index-1], e)
|
|
||||||
}
|
|
||||||
w.enti = e.Index
|
|
||||||
case stateType:
|
|
||||||
state = mustUnmarshalState(rec.Data)
|
|
||||||
case metadataType:
|
|
||||||
if metadata != nil && !reflect.DeepEqual(metadata, rec.Data) {
|
|
||||||
state.Reset()
|
|
||||||
return nil, state, nil, ErrMetadataConflict
|
|
||||||
}
|
|
||||||
metadata = rec.Data
|
|
||||||
case crcType:
|
|
||||||
crc := decoder.crc.Sum32()
|
|
||||||
// current crc of decoder must match the crc of the record.
|
|
||||||
// do no need to match 0 crc, since the decoder is a new one at this case.
|
|
||||||
if crc != 0 && rec.Validate(crc) != nil {
|
|
||||||
state.Reset()
|
|
||||||
return nil, state, nil, ErrCRCMismatch
|
|
||||||
}
|
|
||||||
decoder.updateCRC(rec.Crc)
|
|
||||||
case snapshotType:
|
|
||||||
var snap walpb.Snapshot
|
|
||||||
pbutil.MustUnmarshal(&snap, rec.Data)
|
|
||||||
if snap.Index == w.start.Index {
|
|
||||||
if snap.Term != w.start.Term {
|
|
||||||
state.Reset()
|
|
||||||
return nil, state, nil, ErrSnapshotMismatch
|
|
||||||
}
|
|
||||||
match = true
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
state.Reset()
|
|
||||||
return nil, state, nil, fmt.Errorf("unexpected block type %d", rec.Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch w.f {
|
|
||||||
case nil:
|
|
||||||
// We do not have to read out all entries in read mode.
|
|
||||||
// The last record maybe a partial written one, so
|
|
||||||
// ErrunexpectedEOF might be returned.
|
|
||||||
if err != io.EOF && err != io.ErrUnexpectedEOF {
|
|
||||||
state.Reset()
|
|
||||||
return nil, state, nil, err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// We must read all of the entries if WAL is opened in write mode.
|
|
||||||
if err != io.EOF {
|
|
||||||
state.Reset()
|
|
||||||
return nil, state, nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = nil
|
|
||||||
if !match {
|
|
||||||
err = ErrSnapshotNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
// close decoder, disable reading
|
|
||||||
w.decoder.close()
|
|
||||||
w.start = walpb.Snapshot{}
|
|
||||||
|
|
||||||
w.metadata = metadata
|
|
||||||
|
|
||||||
if w.f != nil {
|
|
||||||
// create encoder (chain crc with the decoder), enable appending
|
|
||||||
w.encoder = newEncoder(w.f, w.decoder.lastCRC())
|
|
||||||
w.decoder = nil
|
|
||||||
lastIndexSaved.Set(float64(w.enti))
|
|
||||||
}
|
|
||||||
|
|
||||||
return metadata, state, ents, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// cut closes current file written and creates a new one ready to append.
|
|
||||||
// cut first creates a temp wal file and writes necessary headers into it.
|
|
||||||
// Then cut atomtically rename temp wal file to a wal file.
|
|
||||||
func (w *WAL) cut() error {
|
|
||||||
// close old wal file
|
|
||||||
if err := w.sync(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := w.f.Close(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fpath := path.Join(w.dir, walName(w.seq+1, w.enti+1))
|
|
||||||
ftpath := fpath + ".tmp"
|
|
||||||
|
|
||||||
// create a temp wal file with name sequence + 1, or tuncate the existing one
|
|
||||||
ft, err := os.OpenFile(ftpath, os.O_WRONLY|os.O_APPEND|os.O_CREATE|os.O_TRUNC, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// update writer and save the previous crc
|
|
||||||
w.f = ft
|
|
||||||
prevCrc := w.encoder.crc.Sum32()
|
|
||||||
w.encoder = newEncoder(w.f, prevCrc)
|
|
||||||
if err := w.saveCrc(prevCrc); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := w.encoder.encode(&walpb.Record{Type: metadataType, Data: w.metadata}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := w.saveState(&w.state); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// close temp wal file
|
|
||||||
if err := w.sync(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := w.f.Close(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// atomically move temp wal file to wal file
|
|
||||||
if err := os.Rename(ftpath, fpath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// open the wal file and update writer again
|
|
||||||
f, err := os.OpenFile(fpath, os.O_WRONLY|os.O_APPEND, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = fileutil.Preallocate(f, segmentSizeBytes)
|
|
||||||
if err != nil {
|
|
||||||
plog.Errorf("failed to allocate space when creating new wal file (%v)", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
w.f = f
|
|
||||||
prevCrc = w.encoder.crc.Sum32()
|
|
||||||
w.encoder = newEncoder(w.f, prevCrc)
|
|
||||||
|
|
||||||
// lock the new wal file
|
|
||||||
l, err := fileutil.NewLock(f.Name())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = l.Lock()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w.locks = append(w.locks, l)
|
|
||||||
|
|
||||||
// increase the wal seq
|
|
||||||
w.seq++
|
|
||||||
|
|
||||||
plog.Infof("segmented wal file %v is created", fpath)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WAL) sync() error {
|
|
||||||
if w.encoder != nil {
|
|
||||||
if err := w.encoder.flush(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
start := time.Now()
|
|
||||||
err := w.f.Sync()
|
|
||||||
syncDurations.Observe(float64(time.Since(start).Nanoseconds() / int64(time.Microsecond)))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReleaseLockTo releases the locks, which has smaller index than the given index
|
|
||||||
// except the largest one among them.
|
|
||||||
// For example, if WAL is holding lock 1,2,3,4,5,6, ReleaseLockTo(4) will release
|
|
||||||
// lock 1,2 but keep 3. ReleaseLockTo(5) will release 1,2,3 but keep 4.
|
|
||||||
func (w *WAL) ReleaseLockTo(index uint64) error {
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
|
|
||||||
var smaller int
|
|
||||||
found := false
|
|
||||||
|
|
||||||
for i, l := range w.locks {
|
|
||||||
_, lockIndex, err := parseWalName(path.Base(l.Name()))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if lockIndex >= index {
|
|
||||||
smaller = i - 1
|
|
||||||
found = true
|
|
||||||
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 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < smaller; i++ {
|
|
||||||
w.locks[i].Unlock()
|
|
||||||
w.locks[i].Destroy()
|
|
||||||
}
|
|
||||||
w.locks = w.locks[smaller:]
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WAL) Close() error {
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
|
|
||||||
if w.f != nil {
|
|
||||||
if err := w.sync(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := w.f.Close(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, l := range w.locks {
|
|
||||||
err := l.Unlock()
|
|
||||||
if err != nil {
|
|
||||||
plog.Errorf("failed to unlock during closing wal: %s", err)
|
|
||||||
}
|
|
||||||
err = l.Destroy()
|
|
||||||
if err != nil {
|
|
||||||
plog.Errorf("failed to destroy lock during closing wal: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WAL) saveEntry(e *raftpb.Entry) error {
|
|
||||||
// TODO: add MustMarshalTo to reduce one allocation.
|
|
||||||
b := pbutil.MustMarshal(e)
|
|
||||||
rec := &walpb.Record{Type: entryType, Data: b}
|
|
||||||
if err := w.encoder.encode(rec); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w.enti = e.Index
|
|
||||||
lastIndexSaved.Set(float64(w.enti))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WAL) saveState(s *raftpb.HardState) error {
|
|
||||||
if raft.IsEmptyHardState(*s) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
w.state = *s
|
|
||||||
b := pbutil.MustMarshal(s)
|
|
||||||
rec := &walpb.Record{Type: stateType, Data: b}
|
|
||||||
return w.encoder.encode(rec)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WAL) Save(st raftpb.HardState, ents []raftpb.Entry) error {
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
|
|
||||||
// short cut, do not call sync
|
|
||||||
if raft.IsEmptyHardState(st) && len(ents) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(xiangli): no more reference operator
|
|
||||||
for i := range ents {
|
|
||||||
if err := w.saveEntry(&ents[i]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := w.saveState(&st); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fstat, err := w.f.Stat()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if fstat.Size() < segmentSizeBytes {
|
|
||||||
return w.sync()
|
|
||||||
}
|
|
||||||
// TODO: add a test for this code path when refactoring the tests
|
|
||||||
return w.cut()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WAL) SaveSnapshot(e walpb.Snapshot) error {
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
|
|
||||||
b := pbutil.MustMarshal(&e)
|
|
||||||
rec := &walpb.Record{Type: snapshotType, Data: b}
|
|
||||||
if err := w.encoder.encode(rec); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// update enti only when snapshot is ahead of last index
|
|
||||||
if w.enti < e.Index {
|
|
||||||
w.enti = e.Index
|
|
||||||
}
|
|
||||||
lastIndexSaved.Set(float64(w.enti))
|
|
||||||
return w.sync()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WAL) saveCrc(prevCrc uint32) error {
|
|
||||||
return w.encoder.encode(&walpb.Record{Type: crcType, Crc: prevCrc})
|
|
||||||
}
|
|
31
third_party/forked/etcd221/wal/walpb/BUILD
vendored
31
third_party/forked/etcd221/wal/walpb/BUILD
vendored
@ -1,31 +0,0 @@
|
|||||||
package(default_visibility = ["//visibility:public"])
|
|
||||||
|
|
||||||
licenses(["notice"])
|
|
||||||
|
|
||||||
load(
|
|
||||||
"@io_bazel_rules_go//go:def.bzl",
|
|
||||||
"go_library",
|
|
||||||
)
|
|
||||||
|
|
||||||
go_library(
|
|
||||||
name = "go_default_library",
|
|
||||||
srcs = [
|
|
||||||
"record.go",
|
|
||||||
"record.pb.go",
|
|
||||||
],
|
|
||||||
importpath = "k8s.io/kubernetes/third_party/forked/etcd221/wal/walpb",
|
|
||||||
deps = ["//vendor/github.com/gogo/protobuf/proto:go_default_library"],
|
|
||||||
)
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "package-srcs",
|
|
||||||
srcs = glob(["**"]),
|
|
||||||
tags = ["automanaged"],
|
|
||||||
visibility = ["//visibility:private"],
|
|
||||||
)
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "all-srcs",
|
|
||||||
srcs = [":package-srcs"],
|
|
||||||
tags = ["automanaged"],
|
|
||||||
)
|
|
29
third_party/forked/etcd221/wal/walpb/record.go
vendored
29
third_party/forked/etcd221/wal/walpb/record.go
vendored
@ -1,29 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 walpb
|
|
||||||
|
|
||||||
import "errors"
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrCRCMismatch = errors.New("walpb: crc mismatch")
|
|
||||||
)
|
|
||||||
|
|
||||||
func (rec *Record) Validate(crc uint32) error {
|
|
||||||
if rec.Crc == crc {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
rec.Reset()
|
|
||||||
return ErrCRCMismatch
|
|
||||||
}
|
|
447
third_party/forked/etcd221/wal/walpb/record.pb.go
vendored
447
third_party/forked/etcd221/wal/walpb/record.pb.go
vendored
@ -1,447 +0,0 @@
|
|||||||
// Code generated by protoc-gen-gogo.
|
|
||||||
// source: record.proto
|
|
||||||
// DO NOT EDIT!
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package walpb is a generated protocol buffer package.
|
|
||||||
|
|
||||||
It is generated from these files:
|
|
||||||
record.proto
|
|
||||||
|
|
||||||
It has these top-level messages:
|
|
||||||
Record
|
|
||||||
Snapshot
|
|
||||||
*/
|
|
||||||
package walpb
|
|
||||||
|
|
||||||
import proto "github.com/gogo/protobuf/proto"
|
|
||||||
import math "math"
|
|
||||||
|
|
||||||
// discarding unused import gogoproto "github.com/coreos/etcd/Godeps/_workspace/src/gogoproto"
|
|
||||||
|
|
||||||
import io "io"
|
|
||||||
import fmt "fmt"
|
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
|
||||||
var _ = proto.Marshal
|
|
||||||
var _ = math.Inf
|
|
||||||
|
|
||||||
type Record struct {
|
|
||||||
Type int64 `protobuf:"varint,1,opt,name=type" json:"type"`
|
|
||||||
Crc uint32 `protobuf:"varint,2,opt,name=crc" json:"crc"`
|
|
||||||
Data []byte `protobuf:"bytes,3,opt,name=data" json:"data,omitempty"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Record) Reset() { *m = Record{} }
|
|
||||||
func (m *Record) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*Record) ProtoMessage() {}
|
|
||||||
|
|
||||||
type Snapshot struct {
|
|
||||||
Index uint64 `protobuf:"varint,1,opt,name=index" json:"index"`
|
|
||||||
Term uint64 `protobuf:"varint,2,opt,name=term" json:"term"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Snapshot) Reset() { *m = Snapshot{} }
|
|
||||||
func (m *Snapshot) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*Snapshot) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (m *Record) Marshal() (data []byte, err error) {
|
|
||||||
size := m.Size()
|
|
||||||
data = make([]byte, size)
|
|
||||||
n, err := m.MarshalTo(data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return data[:n], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Record) MarshalTo(data []byte) (int, error) {
|
|
||||||
var i int
|
|
||||||
_ = i
|
|
||||||
var l int
|
|
||||||
_ = l
|
|
||||||
data[i] = 0x8
|
|
||||||
i++
|
|
||||||
i = encodeVarintRecord(data, i, uint64(m.Type))
|
|
||||||
data[i] = 0x10
|
|
||||||
i++
|
|
||||||
i = encodeVarintRecord(data, i, uint64(m.Crc))
|
|
||||||
if m.Data != nil {
|
|
||||||
data[i] = 0x1a
|
|
||||||
i++
|
|
||||||
i = encodeVarintRecord(data, i, uint64(len(m.Data)))
|
|
||||||
i += copy(data[i:], m.Data)
|
|
||||||
}
|
|
||||||
if m.XXX_unrecognized != nil {
|
|
||||||
i += copy(data[i:], m.XXX_unrecognized)
|
|
||||||
}
|
|
||||||
return i, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Snapshot) Marshal() (data []byte, err error) {
|
|
||||||
size := m.Size()
|
|
||||||
data = make([]byte, size)
|
|
||||||
n, err := m.MarshalTo(data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return data[:n], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Snapshot) MarshalTo(data []byte) (int, error) {
|
|
||||||
var i int
|
|
||||||
_ = i
|
|
||||||
var l int
|
|
||||||
_ = l
|
|
||||||
data[i] = 0x8
|
|
||||||
i++
|
|
||||||
i = encodeVarintRecord(data, i, uint64(m.Index))
|
|
||||||
data[i] = 0x10
|
|
||||||
i++
|
|
||||||
i = encodeVarintRecord(data, i, uint64(m.Term))
|
|
||||||
if m.XXX_unrecognized != nil {
|
|
||||||
i += copy(data[i:], m.XXX_unrecognized)
|
|
||||||
}
|
|
||||||
return i, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeFixed64Record(data []byte, offset int, v uint64) int {
|
|
||||||
data[offset] = uint8(v)
|
|
||||||
data[offset+1] = uint8(v >> 8)
|
|
||||||
data[offset+2] = uint8(v >> 16)
|
|
||||||
data[offset+3] = uint8(v >> 24)
|
|
||||||
data[offset+4] = uint8(v >> 32)
|
|
||||||
data[offset+5] = uint8(v >> 40)
|
|
||||||
data[offset+6] = uint8(v >> 48)
|
|
||||||
data[offset+7] = uint8(v >> 56)
|
|
||||||
return offset + 8
|
|
||||||
}
|
|
||||||
func encodeFixed32Record(data []byte, offset int, v uint32) int {
|
|
||||||
data[offset] = uint8(v)
|
|
||||||
data[offset+1] = uint8(v >> 8)
|
|
||||||
data[offset+2] = uint8(v >> 16)
|
|
||||||
data[offset+3] = uint8(v >> 24)
|
|
||||||
return offset + 4
|
|
||||||
}
|
|
||||||
func encodeVarintRecord(data []byte, offset int, v uint64) int {
|
|
||||||
for v >= 1<<7 {
|
|
||||||
data[offset] = uint8(v&0x7f | 0x80)
|
|
||||||
v >>= 7
|
|
||||||
offset++
|
|
||||||
}
|
|
||||||
data[offset] = uint8(v)
|
|
||||||
return offset + 1
|
|
||||||
}
|
|
||||||
func (m *Record) Size() (n int) {
|
|
||||||
var l int
|
|
||||||
_ = l
|
|
||||||
n += 1 + sovRecord(uint64(m.Type))
|
|
||||||
n += 1 + sovRecord(uint64(m.Crc))
|
|
||||||
if m.Data != nil {
|
|
||||||
l = len(m.Data)
|
|
||||||
n += 1 + l + sovRecord(uint64(l))
|
|
||||||
}
|
|
||||||
if m.XXX_unrecognized != nil {
|
|
||||||
n += len(m.XXX_unrecognized)
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Snapshot) Size() (n int) {
|
|
||||||
var l int
|
|
||||||
_ = l
|
|
||||||
n += 1 + sovRecord(uint64(m.Index))
|
|
||||||
n += 1 + sovRecord(uint64(m.Term))
|
|
||||||
if m.XXX_unrecognized != nil {
|
|
||||||
n += len(m.XXX_unrecognized)
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func sovRecord(x uint64) (n int) {
|
|
||||||
for {
|
|
||||||
n++
|
|
||||||
x >>= 7
|
|
||||||
if x == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
func sozRecord(x uint64) (n int) {
|
|
||||||
return sovRecord(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
|
||||||
}
|
|
||||||
func (m *Record) Unmarshal(data []byte) error {
|
|
||||||
l := len(data)
|
|
||||||
iNdEx := 0
|
|
||||||
for iNdEx < l {
|
|
||||||
var wire uint64
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := data[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
wire |= (uint64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fieldNum := int32(wire >> 3)
|
|
||||||
wireType := int(wire & 0x7)
|
|
||||||
switch fieldNum {
|
|
||||||
case 1:
|
|
||||||
if wireType != 0 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType)
|
|
||||||
}
|
|
||||||
m.Type = 0
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := data[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
m.Type |= (int64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 2:
|
|
||||||
if wireType != 0 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field Crc", wireType)
|
|
||||||
}
|
|
||||||
m.Crc = 0
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := data[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
m.Crc |= (uint32(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 3:
|
|
||||||
if wireType != 2 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType)
|
|
||||||
}
|
|
||||||
var byteLen int
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := data[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
byteLen |= (int(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if byteLen < 0 {
|
|
||||||
return ErrInvalidLengthRecord
|
|
||||||
}
|
|
||||||
postIndex := iNdEx + byteLen
|
|
||||||
if postIndex > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
m.Data = append([]byte{}, data[iNdEx:postIndex]...)
|
|
||||||
iNdEx = postIndex
|
|
||||||
default:
|
|
||||||
var sizeOfWire int
|
|
||||||
for {
|
|
||||||
sizeOfWire++
|
|
||||||
wire >>= 7
|
|
||||||
if wire == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
iNdEx -= sizeOfWire
|
|
||||||
skippy, err := skipRecord(data[iNdEx:])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if skippy < 0 {
|
|
||||||
return ErrInvalidLengthRecord
|
|
||||||
}
|
|
||||||
if (iNdEx + skippy) > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
|
|
||||||
iNdEx += skippy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (m *Snapshot) Unmarshal(data []byte) error {
|
|
||||||
l := len(data)
|
|
||||||
iNdEx := 0
|
|
||||||
for iNdEx < l {
|
|
||||||
var wire uint64
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := data[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
wire |= (uint64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fieldNum := int32(wire >> 3)
|
|
||||||
wireType := int(wire & 0x7)
|
|
||||||
switch fieldNum {
|
|
||||||
case 1:
|
|
||||||
if wireType != 0 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType)
|
|
||||||
}
|
|
||||||
m.Index = 0
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := data[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
m.Index |= (uint64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 2:
|
|
||||||
if wireType != 0 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field Term", wireType)
|
|
||||||
}
|
|
||||||
m.Term = 0
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := data[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
m.Term |= (uint64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
var sizeOfWire int
|
|
||||||
for {
|
|
||||||
sizeOfWire++
|
|
||||||
wire >>= 7
|
|
||||||
if wire == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
iNdEx -= sizeOfWire
|
|
||||||
skippy, err := skipRecord(data[iNdEx:])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if skippy < 0 {
|
|
||||||
return ErrInvalidLengthRecord
|
|
||||||
}
|
|
||||||
if (iNdEx + skippy) > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
|
|
||||||
iNdEx += skippy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func skipRecord(data []byte) (n int, err error) {
|
|
||||||
l := len(data)
|
|
||||||
iNdEx := 0
|
|
||||||
for iNdEx < l {
|
|
||||||
var wire uint64
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if iNdEx >= l {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := data[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
wire |= (uint64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wireType := int(wire & 0x7)
|
|
||||||
switch wireType {
|
|
||||||
case 0:
|
|
||||||
for {
|
|
||||||
if iNdEx >= l {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
iNdEx++
|
|
||||||
if data[iNdEx-1] < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return iNdEx, nil
|
|
||||||
case 1:
|
|
||||||
iNdEx += 8
|
|
||||||
return iNdEx, nil
|
|
||||||
case 2:
|
|
||||||
var length int
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if iNdEx >= l {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := data[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
length |= (int(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
iNdEx += length
|
|
||||||
if length < 0 {
|
|
||||||
return 0, ErrInvalidLengthRecord
|
|
||||||
}
|
|
||||||
return iNdEx, nil
|
|
||||||
case 3:
|
|
||||||
for {
|
|
||||||
var innerWire uint64
|
|
||||||
var start int = iNdEx
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if iNdEx >= l {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := data[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
innerWire |= (uint64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
innerWireType := int(innerWire & 0x7)
|
|
||||||
if innerWireType == 4 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
next, err := skipRecord(data[start:])
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
iNdEx = start + next
|
|
||||||
}
|
|
||||||
return iNdEx, nil
|
|
||||||
case 4:
|
|
||||||
return iNdEx, nil
|
|
||||||
case 5:
|
|
||||||
iNdEx += 4
|
|
||||||
return iNdEx, nil
|
|
||||||
default:
|
|
||||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrInvalidLengthRecord = fmt.Errorf("proto: negative length found during unmarshaling")
|
|
||||||
)
|
|
@ -1,20 +0,0 @@
|
|||||||
syntax = "proto2";
|
|
||||||
package walpb;
|
|
||||||
|
|
||||||
import "gogoproto/gogo.proto";
|
|
||||||
|
|
||||||
option (gogoproto.marshaler_all) = true;
|
|
||||||
option (gogoproto.sizer_all) = true;
|
|
||||||
option (gogoproto.unmarshaler_all) = true;
|
|
||||||
option (gogoproto.goproto_getters_all) = false;
|
|
||||||
|
|
||||||
message Record {
|
|
||||||
optional int64 type = 1 [(gogoproto.nullable) = false];
|
|
||||||
optional uint32 crc = 2 [(gogoproto.nullable) = false];
|
|
||||||
optional bytes data = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Snapshot {
|
|
||||||
optional uint64 index = 1 [(gogoproto.nullable) = false];
|
|
||||||
optional uint64 term = 2 [(gogoproto.nullable) = false];
|
|
||||||
}
|
|
1
third_party/forked/etcd237/README.md
vendored
1
third_party/forked/etcd237/README.md
vendored
@ -1 +0,0 @@
|
|||||||
Forked from etcd 2.3 release branch to support migration from 3.0 WAL to 2.3 WAL format
|
|
54
third_party/forked/etcd237/pkg/fileutil/BUILD
vendored
54
third_party/forked/etcd237/pkg/fileutil/BUILD
vendored
@ -1,54 +0,0 @@
|
|||||||
package(default_visibility = ["//visibility:public"])
|
|
||||||
|
|
||||||
licenses(["notice"])
|
|
||||||
|
|
||||||
load(
|
|
||||||
"@io_bazel_rules_go//go:def.bzl",
|
|
||||||
"go_library",
|
|
||||||
"go_test",
|
|
||||||
)
|
|
||||||
|
|
||||||
go_test(
|
|
||||||
name = "go_default_test",
|
|
||||||
srcs = [
|
|
||||||
"fileutil_test.go",
|
|
||||||
"lock_test.go",
|
|
||||||
"preallocate_test.go",
|
|
||||||
"purge_test.go",
|
|
||||||
],
|
|
||||||
embed = [":go_default_library"],
|
|
||||||
)
|
|
||||||
|
|
||||||
go_library(
|
|
||||||
name = "go_default_library",
|
|
||||||
srcs = [
|
|
||||||
"fileutil.go",
|
|
||||||
"lock.go",
|
|
||||||
"lock_plan9.go",
|
|
||||||
"lock_solaris.go",
|
|
||||||
"lock_unix.go",
|
|
||||||
"lock_windows.go",
|
|
||||||
"perallocate_unsupported.go",
|
|
||||||
"preallocate.go",
|
|
||||||
"purge.go",
|
|
||||||
"sync.go",
|
|
||||||
"sync_linux.go",
|
|
||||||
],
|
|
||||||
importpath = "k8s.io/kubernetes/third_party/forked/etcd237/pkg/fileutil",
|
|
||||||
deps = [
|
|
||||||
"//vendor/github.com/coreos/pkg/capnslog:go_default_library",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "package-srcs",
|
|
||||||
srcs = glob(["**"]),
|
|
||||||
tags = ["automanaged"],
|
|
||||||
visibility = ["//visibility:private"],
|
|
||||||
)
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "all-srcs",
|
|
||||||
srcs = [":package-srcs"],
|
|
||||||
tags = ["automanaged"],
|
|
||||||
)
|
|
@ -1,75 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 fileutil implements utility functions related to files and paths.
|
|
||||||
package fileutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/coreos/pkg/capnslog"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
privateFileMode = 0600
|
|
||||||
// owner can make/remove files inside the directory
|
|
||||||
privateDirMode = 0700
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
plog = capnslog.NewPackageLogger("github.com/coreos/etcd/pkg", "fileutil")
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsDirWriteable checks if dir is writable by writing and removing a file
|
|
||||||
// to dir. It returns nil if dir is writable.
|
|
||||||
func IsDirWriteable(dir string) error {
|
|
||||||
f := path.Join(dir, ".touch")
|
|
||||||
if err := ioutil.WriteFile(f, []byte(""), privateFileMode); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return os.Remove(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadDir returns the filenames in the given directory in sorted order.
|
|
||||||
func ReadDir(dirpath string) ([]string, error) {
|
|
||||||
dir, err := os.Open(dirpath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer dir.Close()
|
|
||||||
names, err := dir.Readdirnames(-1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
return names, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TouchDirAll is similar to os.MkdirAll. It creates directories with 0700 permission if any directory
|
|
||||||
// does not exists. TouchDirAll also ensures the given directory is writable.
|
|
||||||
func TouchDirAll(dir string) error {
|
|
||||||
err := os.MkdirAll(dir, privateDirMode)
|
|
||||||
if err != nil && err != os.ErrExist {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return IsDirWriteable(dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Exist(name string) bool {
|
|
||||||
_, err := os.Stat(name)
|
|
||||||
return err == nil
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 fileutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"os/user"
|
|
||||||
"path/filepath"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestIsDirWriteable(t *testing.T) {
|
|
||||||
tmpdir, err := ioutil.TempDir("", "")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected ioutil.TempDir error: %v", err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tmpdir)
|
|
||||||
if err = IsDirWriteable(tmpdir); err != nil {
|
|
||||||
t.Fatalf("unexpected IsDirWriteable error: %v", err)
|
|
||||||
}
|
|
||||||
if err = os.Chmod(tmpdir, 0444); err != nil {
|
|
||||||
t.Fatalf("unexpected os.Chmod error: %v", err)
|
|
||||||
}
|
|
||||||
me, err := user.Current()
|
|
||||||
if err != nil {
|
|
||||||
// err can be non-nil when cross compiled
|
|
||||||
// http://stackoverflow.com/questions/20609415/cross-compiling-user-current-not-implemented-on-linux-amd64
|
|
||||||
t.Skipf("failed to get current user: %v", err)
|
|
||||||
}
|
|
||||||
if me.Name == "root" || me.Name == "Administrator" {
|
|
||||||
// ideally we should check CAP_DAC_OVERRIDE.
|
|
||||||
// but it does not matter for tests.
|
|
||||||
t.Skipf("running as a superuser")
|
|
||||||
}
|
|
||||||
if err := IsDirWriteable(tmpdir); err == nil {
|
|
||||||
t.Fatalf("expected IsDirWriteable to error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReadDir(t *testing.T) {
|
|
||||||
tmpdir, err := ioutil.TempDir("", "")
|
|
||||||
defer os.RemoveAll(tmpdir)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected ioutil.TempDir error: %v", err)
|
|
||||||
}
|
|
||||||
files := []string{"def", "abc", "xyz", "ghi"}
|
|
||||||
for _, f := range files {
|
|
||||||
var fh *os.File
|
|
||||||
fh, err = os.Create(filepath.Join(tmpdir, f))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error creating file: %v", err)
|
|
||||||
}
|
|
||||||
if err = fh.Close(); err != nil {
|
|
||||||
t.Fatalf("error closing file: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fs, err := ReadDir(tmpdir)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error calling ReadDir: %v", err)
|
|
||||||
}
|
|
||||||
wfs := []string{"abc", "def", "ghi", "xyz"}
|
|
||||||
if !reflect.DeepEqual(fs, wfs) {
|
|
||||||
t.Fatalf("ReadDir: got %v, want %v", fs, wfs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExist(t *testing.T) {
|
|
||||||
f, err := ioutil.TempFile(os.TempDir(), "fileutil")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
f.Close()
|
|
||||||
|
|
||||||
if g := Exist(f.Name()); g != true {
|
|
||||||
t.Errorf("exist = %v, want true", g)
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Remove(f.Name())
|
|
||||||
if g := Exist(f.Name()); g != false {
|
|
||||||
t.Errorf("exist = %v, want false", g)
|
|
||||||
}
|
|
||||||
}
|
|
29
third_party/forked/etcd237/pkg/fileutil/lock.go
vendored
29
third_party/forked/etcd237/pkg/fileutil/lock.go
vendored
@ -1,29 +0,0 @@
|
|||||||
// Copyright 2016 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 fileutil
|
|
||||||
|
|
||||||
type Lock interface {
|
|
||||||
// Name returns the name of the file.
|
|
||||||
Name() string
|
|
||||||
// TryLock acquires exclusivity on the lock without blocking.
|
|
||||||
TryLock() error
|
|
||||||
// Lock acquires exclusivity on the lock.
|
|
||||||
Lock() error
|
|
||||||
// Unlock unlocks the lock.
|
|
||||||
Unlock() error
|
|
||||||
// Destroy should be called after Unlock to clean up
|
|
||||||
// the resources.
|
|
||||||
Destroy() error
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 fileutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrLocked = errors.New("file already locked")
|
|
||||||
)
|
|
||||||
|
|
||||||
type lock struct {
|
|
||||||
fname string
|
|
||||||
file *os.File
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Name() string {
|
|
||||||
return l.fname
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) TryLock() error {
|
|
||||||
err := os.Chmod(l.fname, syscall.DMEXCL|0600)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := os.Open(l.fname)
|
|
||||||
if err != nil {
|
|
||||||
return ErrLocked
|
|
||||||
}
|
|
||||||
|
|
||||||
l.file = f
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Lock() error {
|
|
||||||
err := os.Chmod(l.fname, syscall.DMEXCL|0600)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
f, err := os.Open(l.fname)
|
|
||||||
if err == nil {
|
|
||||||
l.file = f
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
time.Sleep(10 * time.Millisecond)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Unlock() error {
|
|
||||||
return l.file.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Destroy() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLock(file string) (Lock, error) {
|
|
||||||
l := &lock{fname: file}
|
|
||||||
return l, nil
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
// +build solaris
|
|
||||||
|
|
||||||
package fileutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrLocked = errors.New("file already locked")
|
|
||||||
)
|
|
||||||
|
|
||||||
type lock struct {
|
|
||||||
fd int
|
|
||||||
file *os.File
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Name() string {
|
|
||||||
return l.file.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) TryLock() error {
|
|
||||||
var lock syscall.Flock_t
|
|
||||||
lock.Start = 0
|
|
||||||
lock.Len = 0
|
|
||||||
lock.Pid = 0
|
|
||||||
lock.Type = syscall.F_WRLCK
|
|
||||||
lock.Whence = 0
|
|
||||||
lock.Pid = 0
|
|
||||||
err := syscall.FcntlFlock(uintptr(l.fd), syscall.F_SETLK, &lock)
|
|
||||||
if err != nil && err == syscall.EAGAIN {
|
|
||||||
return ErrLocked
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Lock() error {
|
|
||||||
var lock syscall.Flock_t
|
|
||||||
lock.Start = 0
|
|
||||||
lock.Len = 0
|
|
||||||
lock.Type = syscall.F_WRLCK
|
|
||||||
lock.Whence = 0
|
|
||||||
lock.Pid = 0
|
|
||||||
return syscall.FcntlFlock(uintptr(l.fd), syscall.F_SETLK, &lock)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Unlock() error {
|
|
||||||
var lock syscall.Flock_t
|
|
||||||
lock.Start = 0
|
|
||||||
lock.Len = 0
|
|
||||||
lock.Type = syscall.F_UNLCK
|
|
||||||
lock.Whence = 0
|
|
||||||
err := syscall.FcntlFlock(uintptr(l.fd), syscall.F_SETLK, &lock)
|
|
||||||
if err != nil && err == syscall.EAGAIN {
|
|
||||||
return ErrLocked
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Destroy() error {
|
|
||||||
return l.file.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLock(file string) (Lock, error) {
|
|
||||||
f, err := os.OpenFile(file, os.O_WRONLY, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
l := &lock{int(f.Fd()), f}
|
|
||||||
return l, nil
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 fileutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestLockAndUnlock(t *testing.T) {
|
|
||||||
f, err := ioutil.TempFile("", "lock")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
f.Close()
|
|
||||||
defer func() {
|
|
||||||
err = os.Remove(f.Name())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// lock the file
|
|
||||||
l, err := NewLock(f.Name())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer l.Destroy()
|
|
||||||
err = l.Lock()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// try lock a locked file
|
|
||||||
dupl, err := NewLock(f.Name())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
err = dupl.TryLock()
|
|
||||||
if err != ErrLocked {
|
|
||||||
t.Errorf("err = %v, want %v", err, ErrLocked)
|
|
||||||
}
|
|
||||||
|
|
||||||
// unlock the file
|
|
||||||
err = l.Unlock()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// try lock the unlocked file
|
|
||||||
err = dupl.TryLock()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("err = %v, want %v", err, nil)
|
|
||||||
}
|
|
||||||
defer dupl.Destroy()
|
|
||||||
|
|
||||||
// blocking on locked file
|
|
||||||
locked := make(chan struct{}, 1)
|
|
||||||
go func() {
|
|
||||||
l.Lock()
|
|
||||||
locked <- struct{}{}
|
|
||||||
}()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-locked:
|
|
||||||
t.Error("unexpected unblocking")
|
|
||||||
case <-time.After(100 * time.Millisecond):
|
|
||||||
}
|
|
||||||
|
|
||||||
// unlock
|
|
||||||
err = dupl.Unlock()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// the previously blocked routine should be unblocked
|
|
||||||
select {
|
|
||||||
case <-locked:
|
|
||||||
case <-time.After(1 * time.Second):
|
|
||||||
t.Error("unexpected blocking")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
// +build !windows,!plan9,!solaris
|
|
||||||
|
|
||||||
package fileutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrLocked = errors.New("file already locked")
|
|
||||||
)
|
|
||||||
|
|
||||||
type lock struct {
|
|
||||||
fd int
|
|
||||||
file *os.File
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Name() string {
|
|
||||||
return l.file.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) TryLock() error {
|
|
||||||
err := syscall.Flock(l.fd, syscall.LOCK_EX|syscall.LOCK_NB)
|
|
||||||
if err != nil && err == syscall.EWOULDBLOCK {
|
|
||||||
return ErrLocked
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Lock() error {
|
|
||||||
return syscall.Flock(l.fd, syscall.LOCK_EX)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Unlock() error {
|
|
||||||
return syscall.Flock(l.fd, syscall.LOCK_UN)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Destroy() error {
|
|
||||||
return l.file.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLock(file string) (Lock, error) {
|
|
||||||
f, err := os.Open(file)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
l := &lock{int(f.Fd()), f}
|
|
||||||
return l, nil
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package fileutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrLocked = errors.New("file already locked")
|
|
||||||
)
|
|
||||||
|
|
||||||
type lock struct {
|
|
||||||
fd int
|
|
||||||
file *os.File
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Name() string {
|
|
||||||
return l.file.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) TryLock() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Lock() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Unlock() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lock) Destroy() error {
|
|
||||||
return l.file.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLock(file string) (Lock, error) {
|
|
||||||
f, err := os.Open(file)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
l := &lock{int(f.Fd()), f}
|
|
||||||
return l, nil
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
// +build !linux
|
|
||||||
|
|
||||||
package fileutil
|
|
||||||
|
|
||||||
import "os"
|
|
||||||
|
|
||||||
// Preallocate tries to allocate the space for given
|
|
||||||
// file. This operation is only supported on linux by a
|
|
||||||
// few filesystems (btrfs, ext4, etc.).
|
|
||||||
// If the operation is unsupported, no error will be returned.
|
|
||||||
// Otherwise, the error encountered will be returned.
|
|
||||||
func Preallocate(f *os.File, sizeInBytes int) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
// +build linux
|
|
||||||
|
|
||||||
package fileutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Preallocate tries to allocate the space for given
|
|
||||||
// file. This operation is only supported on linux by a
|
|
||||||
// few filesystems (btrfs, ext4, etc.).
|
|
||||||
// If the operation is unsupported, no error will be returned.
|
|
||||||
// Otherwise, the error encountered will be returned.
|
|
||||||
func Preallocate(f *os.File, sizeInBytes int) error {
|
|
||||||
// use mode = 1 to keep size
|
|
||||||
// see FALLOC_FL_KEEP_SIZE
|
|
||||||
err := syscall.Fallocate(int(f.Fd()), 1, 0, int64(sizeInBytes))
|
|
||||||
if err != nil {
|
|
||||||
errno, ok := err.(syscall.Errno)
|
|
||||||
// treat not support as nil error
|
|
||||||
if ok && errno == syscall.ENOTSUP {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 fileutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPreallocate(t *testing.T) {
|
|
||||||
if runtime.GOOS != "linux" {
|
|
||||||
t.Skipf("skip testPreallocate, OS = %s", runtime.GOOS)
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err := ioutil.TempDir(os.TempDir(), "preallocateTest")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(p)
|
|
||||||
|
|
||||||
f, err := ioutil.TempFile(p, "")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
size := 64 * 1000
|
|
||||||
err = Preallocate(f, size)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
stat, err := f.Stat()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if stat.Size() != 0 {
|
|
||||||
t.Errorf("size = %d, want %d", stat.Size(), 0)
|
|
||||||
}
|
|
||||||
}
|
|
80
third_party/forked/etcd237/pkg/fileutil/purge.go
vendored
80
third_party/forked/etcd237/pkg/fileutil/purge.go
vendored
@ -1,80 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 fileutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func PurgeFile(dirname string, suffix string, max uint, interval time.Duration, stop <-chan struct{}) <-chan error {
|
|
||||||
errC := make(chan error, 1)
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
fnames, err := ReadDir(dirname)
|
|
||||||
if err != nil {
|
|
||||||
errC <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
newfnames := make([]string, 0)
|
|
||||||
for _, fname := range fnames {
|
|
||||||
if strings.HasSuffix(fname, suffix) {
|
|
||||||
newfnames = append(newfnames, fname)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Strings(newfnames)
|
|
||||||
for len(newfnames) > int(max) {
|
|
||||||
f := path.Join(dirname, newfnames[0])
|
|
||||||
l, err := NewLock(f)
|
|
||||||
if err != nil {
|
|
||||||
errC <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = l.TryLock()
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
err = os.Remove(f)
|
|
||||||
if err != nil {
|
|
||||||
errC <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = l.Unlock()
|
|
||||||
if err != nil {
|
|
||||||
plog.Errorf("error unlocking %s when purging file (%v)", l.Name(), err)
|
|
||||||
errC <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = l.Destroy()
|
|
||||||
if err != nil {
|
|
||||||
plog.Errorf("error destroying lock %s when purging file (%v)", l.Name(), err)
|
|
||||||
errC <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
plog.Infof("purged file %s successfully", f)
|
|
||||||
newfnames = newfnames[1:]
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-time.After(interval):
|
|
||||||
case <-stop:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return errC
|
|
||||||
}
|
|
@ -1,161 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 fileutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPurgeFile(t *testing.T) {
|
|
||||||
dir, err := ioutil.TempDir("", "purgefile")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
for i := 0; i < 5; i++ {
|
|
||||||
_, err = os.Create(path.Join(dir, fmt.Sprintf("%d.test", i)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stop := make(chan struct{})
|
|
||||||
|
|
||||||
// keep at most 3 most recent files
|
|
||||||
errch := PurgeFile(dir, "test", 3, time.Millisecond, stop)
|
|
||||||
|
|
||||||
// create 5 more files
|
|
||||||
for i := 5; i < 10; i++ {
|
|
||||||
_, err = os.Create(path.Join(dir, fmt.Sprintf("%d.test", i)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
time.Sleep(10 * time.Millisecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
// purge routine should purge 7 out of 10 files and only keep the
|
|
||||||
// 3 most recent ones.
|
|
||||||
// wait for purging for at most 100ms.
|
|
||||||
var fnames []string
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
fnames, err = ReadDir(dir)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(fnames) <= 3 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
time.Sleep(10 * time.Millisecond)
|
|
||||||
}
|
|
||||||
wnames := []string{"7.test", "8.test", "9.test"}
|
|
||||||
if !reflect.DeepEqual(fnames, wnames) {
|
|
||||||
t.Errorf("filenames = %v, want %v", fnames, wnames)
|
|
||||||
}
|
|
||||||
|
|
||||||
// no error should be reported from purge routine
|
|
||||||
select {
|
|
||||||
case err := <-errch:
|
|
||||||
t.Errorf("unexpected purge error %v", err)
|
|
||||||
case <-time.After(time.Millisecond):
|
|
||||||
}
|
|
||||||
close(stop)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPurgeFileHoldingLock(t *testing.T) {
|
|
||||||
dir, err := ioutil.TempDir("", "purgefile")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
_, err = os.Create(path.Join(dir, fmt.Sprintf("%d.test", i)))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a purge barrier at 5
|
|
||||||
l, err := NewLock(path.Join(dir, fmt.Sprintf("%d.test", 5)))
|
|
||||||
err = l.Lock()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
stop := make(chan struct{})
|
|
||||||
errch := PurgeFile(dir, "test", 3, time.Millisecond, stop)
|
|
||||||
|
|
||||||
var fnames []string
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
fnames, err = ReadDir(dir)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(fnames) <= 5 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
time.Sleep(10 * time.Millisecond)
|
|
||||||
}
|
|
||||||
wnames := []string{"5.test", "6.test", "7.test", "8.test", "9.test"}
|
|
||||||
if !reflect.DeepEqual(fnames, wnames) {
|
|
||||||
t.Errorf("filenames = %v, want %v", fnames, wnames)
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case err = <-errch:
|
|
||||||
t.Errorf("unexpected purge error %v", err)
|
|
||||||
case <-time.After(time.Millisecond):
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove the purge barrier
|
|
||||||
err = l.Unlock()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
err = l.Destroy()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
fnames, err = ReadDir(dir)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(fnames) <= 3 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
time.Sleep(10 * time.Millisecond)
|
|
||||||
}
|
|
||||||
wnames = []string{"7.test", "8.test", "9.test"}
|
|
||||||
if !reflect.DeepEqual(fnames, wnames) {
|
|
||||||
t.Errorf("filenames = %v, want %v", fnames, wnames)
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case err := <-errch:
|
|
||||||
t.Errorf("unexpected purge error %v", err)
|
|
||||||
case <-time.After(time.Millisecond):
|
|
||||||
}
|
|
||||||
|
|
||||||
close(stop)
|
|
||||||
}
|
|
26
third_party/forked/etcd237/pkg/fileutil/sync.go
vendored
26
third_party/forked/etcd237/pkg/fileutil/sync.go
vendored
@ -1,26 +0,0 @@
|
|||||||
// Copyright 2016 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
// +build !linux
|
|
||||||
|
|
||||||
package fileutil
|
|
||||||
|
|
||||||
import "os"
|
|
||||||
|
|
||||||
// Fdatasync is similar to fsync(), but does not flush modified metadata
|
|
||||||
// unless that metadata is needed in order to allow a subsequent data retrieval
|
|
||||||
// to be correctly handled.
|
|
||||||
func Fdatasync(f *os.File) error {
|
|
||||||
return f.Sync()
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
// Copyright 2016 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
// +build linux
|
|
||||||
|
|
||||||
package fileutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Fdatasync is similar to fsync(), but does not flush modified metadata
|
|
||||||
// unless that metadata is needed in order to allow a subsequent data retrieval
|
|
||||||
// to be correctly handled.
|
|
||||||
func Fdatasync(f *os.File) error {
|
|
||||||
return syscall.Fdatasync(int(f.Fd()))
|
|
||||||
}
|
|
48
third_party/forked/etcd237/wal/BUILD
vendored
48
third_party/forked/etcd237/wal/BUILD
vendored
@ -1,48 +0,0 @@
|
|||||||
package(default_visibility = ["//visibility:public"])
|
|
||||||
|
|
||||||
licenses(["notice"])
|
|
||||||
|
|
||||||
load(
|
|
||||||
"@io_bazel_rules_go//go:def.bzl",
|
|
||||||
"go_library",
|
|
||||||
)
|
|
||||||
|
|
||||||
go_library(
|
|
||||||
name = "go_default_library",
|
|
||||||
srcs = [
|
|
||||||
"decoder.go",
|
|
||||||
"doc.go",
|
|
||||||
"encoder.go",
|
|
||||||
"metrics.go",
|
|
||||||
"multi_readcloser.go",
|
|
||||||
"repair.go",
|
|
||||||
"util.go",
|
|
||||||
"wal.go",
|
|
||||||
],
|
|
||||||
importpath = "k8s.io/kubernetes/third_party/forked/etcd237/wal",
|
|
||||||
deps = [
|
|
||||||
"//third_party/forked/etcd237/pkg/fileutil:go_default_library",
|
|
||||||
"//vendor/github.com/coreos/etcd/pkg/crc:go_default_library",
|
|
||||||
"//vendor/github.com/coreos/etcd/pkg/pbutil:go_default_library",
|
|
||||||
"//vendor/github.com/coreos/etcd/raft/raftpb:go_default_library",
|
|
||||||
"//vendor/github.com/coreos/etcd/wal/walpb:go_default_library",
|
|
||||||
"//vendor/github.com/coreos/pkg/capnslog:go_default_library",
|
|
||||||
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "package-srcs",
|
|
||||||
srcs = glob(["**"]),
|
|
||||||
tags = ["automanaged"],
|
|
||||||
visibility = ["//visibility:private"],
|
|
||||||
)
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "all-srcs",
|
|
||||||
srcs = [
|
|
||||||
":package-srcs",
|
|
||||||
"//third_party/forked/etcd237/wal/walpb:all-srcs",
|
|
||||||
],
|
|
||||||
tags = ["automanaged"],
|
|
||||||
)
|
|
103
third_party/forked/etcd237/wal/decoder.go
vendored
103
third_party/forked/etcd237/wal/decoder.go
vendored
@ -1,103 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 wal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/binary"
|
|
||||||
"hash"
|
|
||||||
"io"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/coreos/etcd/pkg/crc"
|
|
||||||
"github.com/coreos/etcd/pkg/pbutil"
|
|
||||||
"github.com/coreos/etcd/raft/raftpb"
|
|
||||||
"github.com/coreos/etcd/wal/walpb"
|
|
||||||
)
|
|
||||||
|
|
||||||
type decoder struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
br *bufio.Reader
|
|
||||||
|
|
||||||
c io.Closer
|
|
||||||
crc hash.Hash32
|
|
||||||
}
|
|
||||||
|
|
||||||
func newDecoder(rc io.ReadCloser) *decoder {
|
|
||||||
return &decoder{
|
|
||||||
br: bufio.NewReader(rc),
|
|
||||||
c: rc,
|
|
||||||
crc: crc.New(0, crcTable),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) decode(rec *walpb.Record) error {
|
|
||||||
d.mu.Lock()
|
|
||||||
defer d.mu.Unlock()
|
|
||||||
|
|
||||||
rec.Reset()
|
|
||||||
l, err := readInt64(d.br)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
data := make([]byte, l)
|
|
||||||
if _, err = io.ReadFull(d.br, data); err != nil {
|
|
||||||
// ReadFull returns io.EOF only if no bytes were read
|
|
||||||
// the decoder should treat this as an ErrUnexpectedEOF instead.
|
|
||||||
if err == io.EOF {
|
|
||||||
err = io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := rec.Unmarshal(data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// skip crc checking if the record type is crcType
|
|
||||||
if rec.Type == crcType {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
d.crc.Write(rec.Data)
|
|
||||||
return rec.Validate(d.crc.Sum32())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) updateCRC(prevCrc uint32) {
|
|
||||||
d.crc = crc.New(prevCrc, crcTable)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) lastCRC() uint32 {
|
|
||||||
return d.crc.Sum32()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) close() error {
|
|
||||||
return d.c.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustUnmarshalEntry(d []byte) raftpb.Entry {
|
|
||||||
var e raftpb.Entry
|
|
||||||
pbutil.MustUnmarshal(&e, d)
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustUnmarshalState(d []byte) raftpb.HardState {
|
|
||||||
var s raftpb.HardState
|
|
||||||
pbutil.MustUnmarshal(&s, d)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func readInt64(r io.Reader) (int64, error) {
|
|
||||||
var n int64
|
|
||||||
err := binary.Read(r, binary.LittleEndian, &n)
|
|
||||||
return n, err
|
|
||||||
}
|
|
68
third_party/forked/etcd237/wal/doc.go
vendored
68
third_party/forked/etcd237/wal/doc.go
vendored
@ -1,68 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 wal provides an implementation of a write ahead log that is used by
|
|
||||||
etcd.
|
|
||||||
|
|
||||||
A WAL is created at a particular directory and is made up of a number of
|
|
||||||
segmented WAL files. Inside of each file the raft state and entries are appended
|
|
||||||
to it with the Save method:
|
|
||||||
|
|
||||||
metadata := []byte{}
|
|
||||||
w, err := wal.Create("/var/lib/etcd", metadata)
|
|
||||||
...
|
|
||||||
err := w.Save(s, ents)
|
|
||||||
|
|
||||||
After saving an raft snapshot to disk, SaveSnapshot method should be called to
|
|
||||||
record it. So WAL can match with the saved snapshot when restarting.
|
|
||||||
|
|
||||||
err := w.SaveSnapshot(walpb.Snapshot{Index: 10, Term: 2})
|
|
||||||
|
|
||||||
When a user has finished using a WAL it must be closed:
|
|
||||||
|
|
||||||
w.Close()
|
|
||||||
|
|
||||||
WAL files are placed inside of the directory in the following format:
|
|
||||||
$seq-$index.wal
|
|
||||||
|
|
||||||
The first WAL file to be created will be 0000000000000000-0000000000000000.wal
|
|
||||||
indicating an initial sequence of 0 and an initial raft index of 0. The first
|
|
||||||
entry written to WAL MUST have raft index 0.
|
|
||||||
|
|
||||||
WAL will cuts its current wal files if its size exceeds 8MB. This will increment an internal
|
|
||||||
sequence number and cause a new file to be created. If the last raft index saved
|
|
||||||
was 0x20 and this is the first time cut has been called on this WAL then the sequence will
|
|
||||||
increment from 0x0 to 0x1. The new file will be: 0000000000000001-0000000000000021.wal.
|
|
||||||
If a second cut issues 0x10 entries with incremental index later then the file will be called:
|
|
||||||
0000000000000002-0000000000000031.wal.
|
|
||||||
|
|
||||||
At a later time a WAL can be opened at a particular snapshot. If there is no
|
|
||||||
snapshot, an empty snapshot should be passed in.
|
|
||||||
|
|
||||||
w, err := wal.Open("/var/lib/etcd", walpb.Snapshot{Index: 10, Term: 2})
|
|
||||||
...
|
|
||||||
|
|
||||||
The snapshot must have been written to the WAL.
|
|
||||||
|
|
||||||
Additional items cannot be Saved to this WAL until all of the items from the given
|
|
||||||
snapshot to the end of the WAL are read first:
|
|
||||||
|
|
||||||
metadata, state, ents, err := w.ReadAll()
|
|
||||||
|
|
||||||
This will give you the metadata, the last raft.State and the slice of
|
|
||||||
raft.Entry items in the log.
|
|
||||||
|
|
||||||
*/
|
|
||||||
package wal
|
|
89
third_party/forked/etcd237/wal/encoder.go
vendored
89
third_party/forked/etcd237/wal/encoder.go
vendored
@ -1,89 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 wal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/binary"
|
|
||||||
"hash"
|
|
||||||
"io"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/coreos/etcd/pkg/crc"
|
|
||||||
"github.com/coreos/etcd/wal/walpb"
|
|
||||||
)
|
|
||||||
|
|
||||||
type encoder struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
bw *bufio.Writer
|
|
||||||
|
|
||||||
crc hash.Hash32
|
|
||||||
buf []byte
|
|
||||||
uint64buf []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func newEncoder(w io.Writer, prevCrc uint32) *encoder {
|
|
||||||
return &encoder{
|
|
||||||
bw: bufio.NewWriter(w),
|
|
||||||
crc: crc.New(prevCrc, crcTable),
|
|
||||||
// 1MB buffer
|
|
||||||
buf: make([]byte, 1024*1024),
|
|
||||||
uint64buf: make([]byte, 8),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *encoder) encode(rec *walpb.Record) error {
|
|
||||||
e.mu.Lock()
|
|
||||||
defer e.mu.Unlock()
|
|
||||||
|
|
||||||
e.crc.Write(rec.Data)
|
|
||||||
rec.Crc = e.crc.Sum32()
|
|
||||||
var (
|
|
||||||
data []byte
|
|
||||||
err error
|
|
||||||
n int
|
|
||||||
)
|
|
||||||
|
|
||||||
if rec.Size() > len(e.buf) {
|
|
||||||
data, err = rec.Marshal()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
n, err = rec.MarshalTo(e.buf)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
data = e.buf[:n]
|
|
||||||
}
|
|
||||||
if err = writeInt64(e.bw, int64(len(data)), e.uint64buf); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = e.bw.Write(data)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *encoder) flush() error {
|
|
||||||
e.mu.Lock()
|
|
||||||
defer e.mu.Unlock()
|
|
||||||
return e.bw.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeInt64(w io.Writer, n int64, buf []byte) error {
|
|
||||||
// http://golang.org/src/encoding/binary/binary.go
|
|
||||||
binary.LittleEndian.PutUint64(buf, uint64(n))
|
|
||||||
_, err := w.Write(buf)
|
|
||||||
return err
|
|
||||||
}
|
|
38
third_party/forked/etcd237/wal/metrics.go
vendored
38
third_party/forked/etcd237/wal/metrics.go
vendored
@ -1,38 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 wal
|
|
||||||
|
|
||||||
import "github.com/prometheus/client_golang/prometheus"
|
|
||||||
|
|
||||||
var (
|
|
||||||
syncDurations = prometheus.NewHistogram(prometheus.HistogramOpts{
|
|
||||||
Namespace: "etcd",
|
|
||||||
Subsystem: "wal",
|
|
||||||
Name: "fsync_durations_seconds",
|
|
||||||
Help: "The latency distributions of fsync called by wal.",
|
|
||||||
Buckets: prometheus.ExponentialBuckets(0.001, 2, 14),
|
|
||||||
})
|
|
||||||
lastIndexSaved = prometheus.NewGauge(prometheus.GaugeOpts{
|
|
||||||
Namespace: "etcd",
|
|
||||||
Subsystem: "wal",
|
|
||||||
Name: "last_index_saved",
|
|
||||||
Help: "The index of the last entry saved by wal.",
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
prometheus.MustRegister(syncDurations)
|
|
||||||
prometheus.MustRegister(lastIndexSaved)
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 wal
|
|
||||||
|
|
||||||
import "io"
|
|
||||||
|
|
||||||
type multiReadCloser struct {
|
|
||||||
closers []io.Closer
|
|
||||||
reader io.Reader
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *multiReadCloser) Close() error {
|
|
||||||
var err error
|
|
||||||
for i := range mc.closers {
|
|
||||||
err = mc.closers[i].Close()
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *multiReadCloser) Read(p []byte) (int, error) {
|
|
||||||
return mc.reader.Read(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func MultiReadCloser(readClosers ...io.ReadCloser) io.ReadCloser {
|
|
||||||
cs := make([]io.Closer, len(readClosers))
|
|
||||||
rs := make([]io.Reader, len(readClosers))
|
|
||||||
for i := range readClosers {
|
|
||||||
cs[i] = readClosers[i]
|
|
||||||
rs[i] = readClosers[i]
|
|
||||||
}
|
|
||||||
r := io.MultiReader(rs...)
|
|
||||||
return &multiReadCloser{cs, r}
|
|
||||||
}
|
|
107
third_party/forked/etcd237/wal/repair.go
vendored
107
third_party/forked/etcd237/wal/repair.go
vendored
@ -1,107 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 wal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
|
|
||||||
"k8s.io/kubernetes/third_party/forked/etcd237/pkg/fileutil"
|
|
||||||
|
|
||||||
"github.com/coreos/etcd/wal/walpb"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Repair tries to repair ErrUnexpectedEOF in the
|
|
||||||
// last wal file by truncating.
|
|
||||||
func Repair(dirpath string) bool {
|
|
||||||
f, err := openLast(dirpath)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
n := 0
|
|
||||||
rec := &walpb.Record{}
|
|
||||||
|
|
||||||
decoder := newDecoder(f)
|
|
||||||
defer decoder.close()
|
|
||||||
for {
|
|
||||||
err := decoder.decode(rec)
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
n += 8 + rec.Size()
|
|
||||||
// update crc of the decoder when necessary
|
|
||||||
switch rec.Type {
|
|
||||||
case crcType:
|
|
||||||
crc := decoder.crc.Sum32()
|
|
||||||
// current crc of decoder must match the crc of the record.
|
|
||||||
// do no need to match 0 crc, since the decoder is a new one at this case.
|
|
||||||
if crc != 0 && rec.Validate(crc) != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
decoder.updateCRC(rec.Crc)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
case io.EOF:
|
|
||||||
return true
|
|
||||||
case io.ErrUnexpectedEOF:
|
|
||||||
plog.Noticef("repairing %v", f.Name())
|
|
||||||
bf, bferr := os.Create(f.Name() + ".broken")
|
|
||||||
if bferr != nil {
|
|
||||||
plog.Errorf("could not repair %v, failed to create backup file", f.Name())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
defer bf.Close()
|
|
||||||
|
|
||||||
if _, err = f.Seek(0, os.SEEK_SET); err != nil {
|
|
||||||
plog.Errorf("could not repair %v, failed to read file", f.Name())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = io.Copy(bf, f); err != nil {
|
|
||||||
plog.Errorf("could not repair %v, failed to copy file", f.Name())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = f.Truncate(int64(n)); err != nil {
|
|
||||||
plog.Errorf("could not repair %v, failed to truncate file", f.Name())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if err = f.Sync(); err != nil {
|
|
||||||
plog.Errorf("could not repair %v, failed to sync file", f.Name())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
plog.Errorf("could not repair error (%v)", err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// openLast opens the last wal file for read and write.
|
|
||||||
func openLast(dirpath string) (*os.File, error) {
|
|
||||||
names, err := fileutil.ReadDir(dirpath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
names = checkWalNames(names)
|
|
||||||
if len(names) == 0 {
|
|
||||||
return nil, ErrFileNotFound
|
|
||||||
}
|
|
||||||
last := path.Join(dirpath, names[len(names)-1])
|
|
||||||
return os.OpenFile(last, os.O_RDWR, 0)
|
|
||||||
}
|
|
93
third_party/forked/etcd237/wal/util.go
vendored
93
third_party/forked/etcd237/wal/util.go
vendored
@ -1,93 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 wal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"k8s.io/kubernetes/third_party/forked/etcd237/pkg/fileutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
badWalName = errors.New("bad wal name")
|
|
||||||
)
|
|
||||||
|
|
||||||
func Exist(dirpath string) bool {
|
|
||||||
names, err := fileutil.ReadDir(dirpath)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return len(names) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// searchIndex returns the last array index of names whose raft index section is
|
|
||||||
// equal to or smaller than the given index.
|
|
||||||
// The given names MUST be sorted.
|
|
||||||
func searchIndex(names []string, index uint64) (int, bool) {
|
|
||||||
for i := len(names) - 1; i >= 0; i-- {
|
|
||||||
name := names[i]
|
|
||||||
_, curIndex, err := parseWalName(name)
|
|
||||||
if err != nil {
|
|
||||||
plog.Panicf("parse correct name should never fail: %v", err)
|
|
||||||
}
|
|
||||||
if index >= curIndex {
|
|
||||||
return i, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// names should have been sorted based on sequence number.
|
|
||||||
// isValidSeq checks whether seq increases continuously.
|
|
||||||
func isValidSeq(names []string) bool {
|
|
||||||
var lastSeq uint64
|
|
||||||
for _, name := range names {
|
|
||||||
curSeq, _, err := parseWalName(name)
|
|
||||||
if err != nil {
|
|
||||||
plog.Panicf("parse correct name should never fail: %v", err)
|
|
||||||
}
|
|
||||||
if lastSeq != 0 && lastSeq != curSeq-1 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
lastSeq = curSeq
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkWalNames(names []string) []string {
|
|
||||||
wnames := make([]string, 0)
|
|
||||||
for _, name := range names {
|
|
||||||
if _, _, err := parseWalName(name); err != nil {
|
|
||||||
plog.Warningf("ignored file %v in wal", name)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
wnames = append(wnames, name)
|
|
||||||
}
|
|
||||||
return wnames
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseWalName(str string) (seq, index uint64, err error) {
|
|
||||||
if !strings.HasSuffix(str, ".wal") {
|
|
||||||
return 0, 0, badWalName
|
|
||||||
}
|
|
||||||
_, err = fmt.Sscanf(str, "%016x-%016x.wal", &seq, &index)
|
|
||||||
return seq, index, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func walName(seq, index uint64) string {
|
|
||||||
return fmt.Sprintf("%016x-%016x.wal", seq, index)
|
|
||||||
}
|
|
571
third_party/forked/etcd237/wal/wal.go
vendored
571
third_party/forked/etcd237/wal/wal.go
vendored
@ -1,571 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 wal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"hash/crc32"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"reflect"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"k8s.io/kubernetes/third_party/forked/etcd237/pkg/fileutil"
|
|
||||||
|
|
||||||
"github.com/coreos/etcd/pkg/pbutil"
|
|
||||||
"github.com/coreos/etcd/raft/raftpb"
|
|
||||||
"github.com/coreos/etcd/wal/walpb"
|
|
||||||
"github.com/coreos/pkg/capnslog"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
metadataType int64 = iota + 1
|
|
||||||
entryType
|
|
||||||
stateType
|
|
||||||
crcType
|
|
||||||
snapshotType
|
|
||||||
|
|
||||||
// the owner can make/remove files inside the directory
|
|
||||||
privateDirMode = 0700
|
|
||||||
|
|
||||||
// the expected size of each wal segment file.
|
|
||||||
// the actual size might be bigger than it.
|
|
||||||
segmentSizeBytes = 64 * 1000 * 1000 // 64MB
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "wal")
|
|
||||||
|
|
||||||
ErrMetadataConflict = errors.New("wal: conflicting metadata found")
|
|
||||||
ErrFileNotFound = errors.New("wal: file not found")
|
|
||||||
ErrCRCMismatch = errors.New("wal: crc mismatch")
|
|
||||||
ErrSnapshotMismatch = errors.New("wal: snapshot mismatch")
|
|
||||||
ErrSnapshotNotFound = errors.New("wal: snapshot not found")
|
|
||||||
crcTable = crc32.MakeTable(crc32.Castagnoli)
|
|
||||||
)
|
|
||||||
|
|
||||||
// WAL is a logical representation of the stable storage.
|
|
||||||
// WAL is either in read mode or append mode but not both.
|
|
||||||
// A newly created WAL is in append mode, and ready for appending records.
|
|
||||||
// A just opened WAL is in read mode, and ready for reading records.
|
|
||||||
// The WAL will be ready for appending after reading out all the previous records.
|
|
||||||
type WAL struct {
|
|
||||||
dir string // the living directory of the underlay files
|
|
||||||
metadata []byte // metadata recorded at the head of each WAL
|
|
||||||
state raftpb.HardState // hardstate recorded at the head of WAL
|
|
||||||
|
|
||||||
start walpb.Snapshot // snapshot to start reading
|
|
||||||
decoder *decoder // decoder to decode records
|
|
||||||
|
|
||||||
mu sync.Mutex
|
|
||||||
f *os.File // underlay file opened for appending, sync
|
|
||||||
seq uint64 // sequence of the wal file currently used for writes
|
|
||||||
enti uint64 // index of the last entry saved to the wal
|
|
||||||
encoder *encoder // encoder to encode records
|
|
||||||
|
|
||||||
locks []fileutil.Lock // the file locks the WAL is holding (the name is increasing)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create creates a WAL ready for appending records. The given metadata is
|
|
||||||
// recorded at the head of each WAL file, and can be retrieved with ReadAll.
|
|
||||||
func Create(dirpath string, metadata []byte) (*WAL, error) {
|
|
||||||
if Exist(dirpath) {
|
|
||||||
return nil, os.ErrExist
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := os.MkdirAll(dirpath, privateDirMode); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
p := path.Join(dirpath, walName(0, 0))
|
|
||||||
f, err := os.OpenFile(p, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
l, err := fileutil.NewLock(f.Name())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err = l.Lock(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
w := &WAL{
|
|
||||||
dir: dirpath,
|
|
||||||
metadata: metadata,
|
|
||||||
seq: 0,
|
|
||||||
f: f,
|
|
||||||
encoder: newEncoder(f, 0),
|
|
||||||
}
|
|
||||||
w.locks = append(w.locks, l)
|
|
||||||
if err := w.saveCrc(0); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := w.encoder.encode(&walpb.Record{Type: metadataType, Data: metadata}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := w.SaveSnapshot(walpb.Snapshot{}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return w, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open opens the WAL at the given snap.
|
|
||||||
// The snap SHOULD have been previously saved to the WAL, or the following
|
|
||||||
// ReadAll will fail.
|
|
||||||
// The returned WAL is ready to read and the first record will be the one after
|
|
||||||
// the given snap. The WAL cannot be appended to before reading out all of its
|
|
||||||
// previous records.
|
|
||||||
func Open(dirpath string, snap walpb.Snapshot) (*WAL, error) {
|
|
||||||
return openAtIndex(dirpath, snap, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenForRead only opens the wal files for read.
|
|
||||||
// Write on a read only wal panics.
|
|
||||||
func OpenForRead(dirpath string, snap walpb.Snapshot) (*WAL, error) {
|
|
||||||
return openAtIndex(dirpath, snap, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func openAtIndex(dirpath string, snap walpb.Snapshot, write bool) (*WAL, error) {
|
|
||||||
names, err := fileutil.ReadDir(dirpath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
names = checkWalNames(names)
|
|
||||||
if len(names) == 0 {
|
|
||||||
return nil, ErrFileNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
nameIndex, ok := searchIndex(names, snap.Index)
|
|
||||||
if !ok || !isValidSeq(names[nameIndex:]) {
|
|
||||||
return nil, ErrFileNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
// open the wal files for reading
|
|
||||||
rcs := make([]io.ReadCloser, 0)
|
|
||||||
ls := make([]fileutil.Lock, 0)
|
|
||||||
for _, name := range names[nameIndex:] {
|
|
||||||
f, err := os.Open(path.Join(dirpath, name))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
l, err := fileutil.NewLock(f.Name())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = l.TryLock()
|
|
||||||
if err != nil {
|
|
||||||
if write {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rcs = append(rcs, f)
|
|
||||||
ls = append(ls, l)
|
|
||||||
}
|
|
||||||
rc := MultiReadCloser(rcs...)
|
|
||||||
|
|
||||||
// create a WAL ready for reading
|
|
||||||
w := &WAL{
|
|
||||||
dir: dirpath,
|
|
||||||
start: snap,
|
|
||||||
decoder: newDecoder(rc),
|
|
||||||
locks: ls,
|
|
||||||
}
|
|
||||||
|
|
||||||
if write {
|
|
||||||
// open the last wal file for appending
|
|
||||||
seq, _, err := parseWalName(names[len(names)-1])
|
|
||||||
if err != nil {
|
|
||||||
rc.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
last := path.Join(dirpath, names[len(names)-1])
|
|
||||||
|
|
||||||
f, err := os.OpenFile(last, os.O_WRONLY|os.O_APPEND, 0)
|
|
||||||
if err != nil {
|
|
||||||
rc.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = fileutil.Preallocate(f, segmentSizeBytes)
|
|
||||||
if err != nil {
|
|
||||||
rc.Close()
|
|
||||||
plog.Errorf("failed to allocate space when creating new wal file (%v)", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
w.f = f
|
|
||||||
w.seq = seq
|
|
||||||
}
|
|
||||||
|
|
||||||
return w, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadAll reads out records of the current WAL.
|
|
||||||
// If opened in write mode, it must read out all records until EOF. Or an error
|
|
||||||
// will be returned.
|
|
||||||
// If opened in read mode, it will try to read all records if possible.
|
|
||||||
// If it cannot read out the expected snap, it will return ErrSnapshotNotFound.
|
|
||||||
// If loaded snap doesn't match with the expected one, it will return
|
|
||||||
// all the records and error ErrSnapshotMismatch.
|
|
||||||
// TODO: detect not-last-snap error.
|
|
||||||
// TODO: maybe loose the checking of match.
|
|
||||||
// After ReadAll, the WAL will be ready for appending new records.
|
|
||||||
func (w *WAL) ReadAll() (metadata []byte, state raftpb.HardState, ents []raftpb.Entry, err error) {
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
|
|
||||||
rec := &walpb.Record{}
|
|
||||||
decoder := w.decoder
|
|
||||||
|
|
||||||
var match bool
|
|
||||||
for err = decoder.decode(rec); err == nil; err = decoder.decode(rec) {
|
|
||||||
switch rec.Type {
|
|
||||||
case entryType:
|
|
||||||
e := mustUnmarshalEntry(rec.Data)
|
|
||||||
if e.Index > w.start.Index {
|
|
||||||
ents = append(ents[:e.Index-w.start.Index-1], e)
|
|
||||||
}
|
|
||||||
w.enti = e.Index
|
|
||||||
case stateType:
|
|
||||||
state = mustUnmarshalState(rec.Data)
|
|
||||||
case metadataType:
|
|
||||||
if metadata != nil && !reflect.DeepEqual(metadata, rec.Data) {
|
|
||||||
state.Reset()
|
|
||||||
return nil, state, nil, ErrMetadataConflict
|
|
||||||
}
|
|
||||||
metadata = rec.Data
|
|
||||||
case crcType:
|
|
||||||
crc := decoder.crc.Sum32()
|
|
||||||
// current crc of decoder must match the crc of the record.
|
|
||||||
// do no need to match 0 crc, since the decoder is a new one at this case.
|
|
||||||
if crc != 0 && rec.Validate(crc) != nil {
|
|
||||||
state.Reset()
|
|
||||||
return nil, state, nil, ErrCRCMismatch
|
|
||||||
}
|
|
||||||
decoder.updateCRC(rec.Crc)
|
|
||||||
case snapshotType:
|
|
||||||
var snap walpb.Snapshot
|
|
||||||
pbutil.MustUnmarshal(&snap, rec.Data)
|
|
||||||
if snap.Index == w.start.Index {
|
|
||||||
if snap.Term != w.start.Term {
|
|
||||||
state.Reset()
|
|
||||||
return nil, state, nil, ErrSnapshotMismatch
|
|
||||||
}
|
|
||||||
match = true
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
state.Reset()
|
|
||||||
return nil, state, nil, fmt.Errorf("unexpected block type %d", rec.Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch w.f {
|
|
||||||
case nil:
|
|
||||||
// We do not have to read out all entries in read mode.
|
|
||||||
// The last record maybe a partial written one, so
|
|
||||||
// ErrunexpectedEOF might be returned.
|
|
||||||
if err != io.EOF && err != io.ErrUnexpectedEOF {
|
|
||||||
state.Reset()
|
|
||||||
return nil, state, nil, err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// We must read all of the entries if WAL is opened in write mode.
|
|
||||||
if err != io.EOF {
|
|
||||||
state.Reset()
|
|
||||||
return nil, state, nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = nil
|
|
||||||
if !match {
|
|
||||||
err = ErrSnapshotNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
// close decoder, disable reading
|
|
||||||
w.decoder.close()
|
|
||||||
w.start = walpb.Snapshot{}
|
|
||||||
|
|
||||||
w.metadata = metadata
|
|
||||||
|
|
||||||
if w.f != nil {
|
|
||||||
// create encoder (chain crc with the decoder), enable appending
|
|
||||||
w.encoder = newEncoder(w.f, w.decoder.lastCRC())
|
|
||||||
w.decoder = nil
|
|
||||||
lastIndexSaved.Set(float64(w.enti))
|
|
||||||
}
|
|
||||||
|
|
||||||
return metadata, state, ents, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// cut closes current file written and creates a new one ready to append.
|
|
||||||
// cut first creates a temp wal file and writes necessary headers into it.
|
|
||||||
// Then cut atomically rename temp wal file to a wal file.
|
|
||||||
func (w *WAL) cut() error {
|
|
||||||
// close old wal file
|
|
||||||
if err := w.sync(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := w.f.Close(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fpath := path.Join(w.dir, walName(w.seq+1, w.enti+1))
|
|
||||||
ftpath := fpath + ".tmp"
|
|
||||||
|
|
||||||
// create a temp wal file with name sequence + 1, or truncate the existing one
|
|
||||||
ft, err := os.OpenFile(ftpath, os.O_WRONLY|os.O_APPEND|os.O_CREATE|os.O_TRUNC, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// update writer and save the previous crc
|
|
||||||
w.f = ft
|
|
||||||
prevCrc := w.encoder.crc.Sum32()
|
|
||||||
w.encoder = newEncoder(w.f, prevCrc)
|
|
||||||
if err = w.saveCrc(prevCrc); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = w.encoder.encode(&walpb.Record{Type: metadataType, Data: w.metadata}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = w.saveState(&w.state); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// close temp wal file
|
|
||||||
if err = w.sync(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = w.f.Close(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// atomically move temp wal file to wal file
|
|
||||||
if err = os.Rename(ftpath, fpath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// open the wal file and update writer again
|
|
||||||
f, err := os.OpenFile(fpath, os.O_WRONLY|os.O_APPEND, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = fileutil.Preallocate(f, segmentSizeBytes); err != nil {
|
|
||||||
plog.Errorf("failed to allocate space when creating new wal file (%v)", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
w.f = f
|
|
||||||
prevCrc = w.encoder.crc.Sum32()
|
|
||||||
w.encoder = newEncoder(w.f, prevCrc)
|
|
||||||
|
|
||||||
// lock the new wal file
|
|
||||||
l, err := fileutil.NewLock(f.Name())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := l.Lock(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w.locks = append(w.locks, l)
|
|
||||||
|
|
||||||
// increase the wal seq
|
|
||||||
w.seq++
|
|
||||||
|
|
||||||
plog.Infof("segmented wal file %v is created", fpath)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WAL) sync() error {
|
|
||||||
if w.encoder != nil {
|
|
||||||
if err := w.encoder.flush(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
start := time.Now()
|
|
||||||
err := fileutil.Fdatasync(w.f)
|
|
||||||
syncDurations.Observe(float64(time.Since(start)) / float64(time.Second))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReleaseLockTo releases the locks, which has smaller index than the given index
|
|
||||||
// except the largest one among them.
|
|
||||||
// For example, if WAL is holding lock 1,2,3,4,5,6, ReleaseLockTo(4) will release
|
|
||||||
// lock 1,2 but keep 3. ReleaseLockTo(5) will release 1,2,3 but keep 4.
|
|
||||||
func (w *WAL) ReleaseLockTo(index uint64) error {
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
|
|
||||||
var smaller int
|
|
||||||
found := false
|
|
||||||
|
|
||||||
for i, l := range w.locks {
|
|
||||||
_, lockIndex, err := parseWalName(path.Base(l.Name()))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if lockIndex >= index {
|
|
||||||
smaller = i - 1
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if no lock index is greater than the release index, we can
|
|
||||||
// release lock up to the last one(excluding).
|
|
||||||
if !found && len(w.locks) != 0 {
|
|
||||||
smaller = len(w.locks) - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if smaller <= 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < smaller; i++ {
|
|
||||||
w.locks[i].Unlock()
|
|
||||||
w.locks[i].Destroy()
|
|
||||||
}
|
|
||||||
w.locks = w.locks[smaller:]
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WAL) Close() error {
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
|
|
||||||
if w.f != nil {
|
|
||||||
if err := w.sync(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := w.f.Close(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, l := range w.locks {
|
|
||||||
err := l.Unlock()
|
|
||||||
if err != nil {
|
|
||||||
plog.Errorf("failed to unlock during closing wal: %s", err)
|
|
||||||
}
|
|
||||||
err = l.Destroy()
|
|
||||||
if err != nil {
|
|
||||||
plog.Errorf("failed to destroy lock during closing wal: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WAL) saveEntry(e *raftpb.Entry) error {
|
|
||||||
// TODO: add MustMarshalTo to reduce one allocation.
|
|
||||||
b := pbutil.MustMarshal(e)
|
|
||||||
rec := &walpb.Record{Type: entryType, Data: b}
|
|
||||||
if err := w.encoder.encode(rec); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w.enti = e.Index
|
|
||||||
lastIndexSaved.Set(float64(w.enti))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WAL) saveState(s *raftpb.HardState) error {
|
|
||||||
if isEmptyHardState(*s) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
w.state = *s
|
|
||||||
b := pbutil.MustMarshal(s)
|
|
||||||
rec := &walpb.Record{Type: stateType, Data: b}
|
|
||||||
return w.encoder.encode(rec)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WAL) Save(st raftpb.HardState, ents []raftpb.Entry) error {
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
|
|
||||||
// short cut, do not call sync
|
|
||||||
if isEmptyHardState(st) && len(ents) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
mustSync := mustSync(st, w.state, len(ents))
|
|
||||||
|
|
||||||
// TODO(xiangli): no more reference operator
|
|
||||||
for i := range ents {
|
|
||||||
if err := w.saveEntry(&ents[i]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := w.saveState(&st); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fstat, err := w.f.Stat()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if fstat.Size() < segmentSizeBytes {
|
|
||||||
if mustSync {
|
|
||||||
return w.sync()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// TODO: add a test for this code path when refactoring the tests
|
|
||||||
return w.cut()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WAL) SaveSnapshot(e walpb.Snapshot) error {
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
|
|
||||||
b := pbutil.MustMarshal(&e)
|
|
||||||
rec := &walpb.Record{Type: snapshotType, Data: b}
|
|
||||||
if err := w.encoder.encode(rec); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// update enti only when snapshot is ahead of last index
|
|
||||||
if w.enti < e.Index {
|
|
||||||
w.enti = e.Index
|
|
||||||
}
|
|
||||||
lastIndexSaved.Set(float64(w.enti))
|
|
||||||
return w.sync()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WAL) saveCrc(prevCrc uint32) error {
|
|
||||||
return w.encoder.encode(&walpb.Record{Type: crcType, Crc: prevCrc})
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustSync(st, prevst raftpb.HardState, entsnum int) bool {
|
|
||||||
// Persistent state on all servers:
|
|
||||||
// (Updated on stable storage before responding to RPCs)
|
|
||||||
// currentTerm
|
|
||||||
// votedFor
|
|
||||||
// log entries[]
|
|
||||||
if entsnum != 0 || st.Vote != prevst.Vote || st.Term != prevst.Term {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func isHardStateEqual(a, b raftpb.HardState) bool {
|
|
||||||
return a.Term == b.Term && a.Vote == b.Vote && a.Commit == b.Commit
|
|
||||||
}
|
|
||||||
|
|
||||||
var emptyState = raftpb.HardState{}
|
|
||||||
|
|
||||||
func isEmptyHardState(st raftpb.HardState) bool {
|
|
||||||
return isHardStateEqual(st, emptyState)
|
|
||||||
}
|
|
31
third_party/forked/etcd237/wal/walpb/BUILD
vendored
31
third_party/forked/etcd237/wal/walpb/BUILD
vendored
@ -1,31 +0,0 @@
|
|||||||
package(default_visibility = ["//visibility:public"])
|
|
||||||
|
|
||||||
licenses(["notice"])
|
|
||||||
|
|
||||||
load(
|
|
||||||
"@io_bazel_rules_go//go:def.bzl",
|
|
||||||
"go_library",
|
|
||||||
)
|
|
||||||
|
|
||||||
go_library(
|
|
||||||
name = "go_default_library",
|
|
||||||
srcs = [
|
|
||||||
"record.go",
|
|
||||||
"record.pb.go",
|
|
||||||
],
|
|
||||||
importpath = "k8s.io/kubernetes/third_party/forked/etcd237/wal/walpb",
|
|
||||||
deps = ["//vendor/github.com/golang/protobuf/proto:go_default_library"],
|
|
||||||
)
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "package-srcs",
|
|
||||||
srcs = glob(["**"]),
|
|
||||||
tags = ["automanaged"],
|
|
||||||
visibility = ["//visibility:private"],
|
|
||||||
)
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "all-srcs",
|
|
||||||
srcs = [":package-srcs"],
|
|
||||||
tags = ["automanaged"],
|
|
||||||
)
|
|
29
third_party/forked/etcd237/wal/walpb/record.go
vendored
29
third_party/forked/etcd237/wal/walpb/record.go
vendored
@ -1,29 +0,0 @@
|
|||||||
// Copyright 2015 CoreOS, Inc.
|
|
||||||
//
|
|
||||||
// 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 walpb
|
|
||||||
|
|
||||||
import "errors"
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrCRCMismatch = errors.New("walpb: crc mismatch")
|
|
||||||
)
|
|
||||||
|
|
||||||
func (rec *Record) Validate(crc uint32) error {
|
|
||||||
if rec.Crc == crc {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
rec.Reset()
|
|
||||||
return ErrCRCMismatch
|
|
||||||
}
|
|
495
third_party/forked/etcd237/wal/walpb/record.pb.go
vendored
495
third_party/forked/etcd237/wal/walpb/record.pb.go
vendored
@ -1,495 +0,0 @@
|
|||||||
// Code generated by protoc-gen-gogo.
|
|
||||||
// source: record.proto
|
|
||||||
// DO NOT EDIT!
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package walpb is a generated protocol buffer package.
|
|
||||||
|
|
||||||
It is generated from these files:
|
|
||||||
record.proto
|
|
||||||
|
|
||||||
It has these top-level messages:
|
|
||||||
Record
|
|
||||||
Snapshot
|
|
||||||
*/
|
|
||||||
package walpb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
proto "github.com/golang/protobuf/proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
import math "math"
|
|
||||||
|
|
||||||
import io "io"
|
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
|
||||||
var _ = proto.Marshal
|
|
||||||
var _ = fmt.Errorf
|
|
||||||
var _ = math.Inf
|
|
||||||
|
|
||||||
type Record struct {
|
|
||||||
Type int64 `protobuf:"varint,1,opt,name=type" json:"type"`
|
|
||||||
Crc uint32 `protobuf:"varint,2,opt,name=crc" json:"crc"`
|
|
||||||
Data []byte `protobuf:"bytes,3,opt,name=data" json:"data,omitempty"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Record) Reset() { *m = Record{} }
|
|
||||||
func (m *Record) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*Record) ProtoMessage() {}
|
|
||||||
|
|
||||||
type Snapshot struct {
|
|
||||||
Index uint64 `protobuf:"varint,1,opt,name=index" json:"index"`
|
|
||||||
Term uint64 `protobuf:"varint,2,opt,name=term" json:"term"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Snapshot) Reset() { *m = Snapshot{} }
|
|
||||||
func (m *Snapshot) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*Snapshot) ProtoMessage() {}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
proto.RegisterType((*Record)(nil), "walpb.Record")
|
|
||||||
proto.RegisterType((*Snapshot)(nil), "walpb.Snapshot")
|
|
||||||
}
|
|
||||||
func (m *Record) Marshal() (data []byte, err error) {
|
|
||||||
size := m.Size()
|
|
||||||
data = make([]byte, size)
|
|
||||||
n, err := m.MarshalTo(data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return data[:n], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Record) MarshalTo(data []byte) (int, error) {
|
|
||||||
var i int
|
|
||||||
_ = i
|
|
||||||
var l int
|
|
||||||
_ = l
|
|
||||||
data[i] = 0x8
|
|
||||||
i++
|
|
||||||
i = encodeVarintRecord(data, i, uint64(m.Type))
|
|
||||||
data[i] = 0x10
|
|
||||||
i++
|
|
||||||
i = encodeVarintRecord(data, i, uint64(m.Crc))
|
|
||||||
if m.Data != nil {
|
|
||||||
data[i] = 0x1a
|
|
||||||
i++
|
|
||||||
i = encodeVarintRecord(data, i, uint64(len(m.Data)))
|
|
||||||
i += copy(data[i:], m.Data)
|
|
||||||
}
|
|
||||||
if m.XXX_unrecognized != nil {
|
|
||||||
i += copy(data[i:], m.XXX_unrecognized)
|
|
||||||
}
|
|
||||||
return i, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Snapshot) Marshal() (data []byte, err error) {
|
|
||||||
size := m.Size()
|
|
||||||
data = make([]byte, size)
|
|
||||||
n, err := m.MarshalTo(data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return data[:n], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Snapshot) MarshalTo(data []byte) (int, error) {
|
|
||||||
var i int
|
|
||||||
_ = i
|
|
||||||
var l int
|
|
||||||
_ = l
|
|
||||||
data[i] = 0x8
|
|
||||||
i++
|
|
||||||
i = encodeVarintRecord(data, i, uint64(m.Index))
|
|
||||||
data[i] = 0x10
|
|
||||||
i++
|
|
||||||
i = encodeVarintRecord(data, i, uint64(m.Term))
|
|
||||||
if m.XXX_unrecognized != nil {
|
|
||||||
i += copy(data[i:], m.XXX_unrecognized)
|
|
||||||
}
|
|
||||||
return i, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeFixed64Record(data []byte, offset int, v uint64) int {
|
|
||||||
data[offset] = uint8(v)
|
|
||||||
data[offset+1] = uint8(v >> 8)
|
|
||||||
data[offset+2] = uint8(v >> 16)
|
|
||||||
data[offset+3] = uint8(v >> 24)
|
|
||||||
data[offset+4] = uint8(v >> 32)
|
|
||||||
data[offset+5] = uint8(v >> 40)
|
|
||||||
data[offset+6] = uint8(v >> 48)
|
|
||||||
data[offset+7] = uint8(v >> 56)
|
|
||||||
return offset + 8
|
|
||||||
}
|
|
||||||
func encodeFixed32Record(data []byte, offset int, v uint32) int {
|
|
||||||
data[offset] = uint8(v)
|
|
||||||
data[offset+1] = uint8(v >> 8)
|
|
||||||
data[offset+2] = uint8(v >> 16)
|
|
||||||
data[offset+3] = uint8(v >> 24)
|
|
||||||
return offset + 4
|
|
||||||
}
|
|
||||||
func encodeVarintRecord(data []byte, offset int, v uint64) int {
|
|
||||||
for v >= 1<<7 {
|
|
||||||
data[offset] = uint8(v&0x7f | 0x80)
|
|
||||||
v >>= 7
|
|
||||||
offset++
|
|
||||||
}
|
|
||||||
data[offset] = uint8(v)
|
|
||||||
return offset + 1
|
|
||||||
}
|
|
||||||
func (m *Record) Size() (n int) {
|
|
||||||
var l int
|
|
||||||
_ = l
|
|
||||||
n += 1 + sovRecord(uint64(m.Type))
|
|
||||||
n += 1 + sovRecord(uint64(m.Crc))
|
|
||||||
if m.Data != nil {
|
|
||||||
l = len(m.Data)
|
|
||||||
n += 1 + l + sovRecord(uint64(l))
|
|
||||||
}
|
|
||||||
if m.XXX_unrecognized != nil {
|
|
||||||
n += len(m.XXX_unrecognized)
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Snapshot) Size() (n int) {
|
|
||||||
var l int
|
|
||||||
_ = l
|
|
||||||
n += 1 + sovRecord(uint64(m.Index))
|
|
||||||
n += 1 + sovRecord(uint64(m.Term))
|
|
||||||
if m.XXX_unrecognized != nil {
|
|
||||||
n += len(m.XXX_unrecognized)
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func sovRecord(x uint64) (n int) {
|
|
||||||
for {
|
|
||||||
n++
|
|
||||||
x >>= 7
|
|
||||||
if x == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
func sozRecord(x uint64) (n int) {
|
|
||||||
return sovRecord(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
|
||||||
}
|
|
||||||
func (m *Record) Unmarshal(data []byte) error {
|
|
||||||
l := len(data)
|
|
||||||
iNdEx := 0
|
|
||||||
for iNdEx < l {
|
|
||||||
preIndex := iNdEx
|
|
||||||
var wire uint64
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return ErrIntOverflowRecord
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := data[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
wire |= (uint64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fieldNum := int32(wire >> 3)
|
|
||||||
wireType := int(wire & 0x7)
|
|
||||||
if wireType == 4 {
|
|
||||||
return fmt.Errorf("proto: Record: wiretype end group for non-group")
|
|
||||||
}
|
|
||||||
if fieldNum <= 0 {
|
|
||||||
return fmt.Errorf("proto: Record: illegal tag %d (wire type %d)", fieldNum, wire)
|
|
||||||
}
|
|
||||||
switch fieldNum {
|
|
||||||
case 1:
|
|
||||||
if wireType != 0 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType)
|
|
||||||
}
|
|
||||||
m.Type = 0
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return ErrIntOverflowRecord
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := data[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
m.Type |= (int64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 2:
|
|
||||||
if wireType != 0 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field Crc", wireType)
|
|
||||||
}
|
|
||||||
m.Crc = 0
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return ErrIntOverflowRecord
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := data[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
m.Crc |= (uint32(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 3:
|
|
||||||
if wireType != 2 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType)
|
|
||||||
}
|
|
||||||
var byteLen int
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return ErrIntOverflowRecord
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := data[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
byteLen |= (int(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if byteLen < 0 {
|
|
||||||
return ErrInvalidLengthRecord
|
|
||||||
}
|
|
||||||
postIndex := iNdEx + byteLen
|
|
||||||
if postIndex > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
m.Data = append(m.Data[:0], data[iNdEx:postIndex]...)
|
|
||||||
if m.Data == nil {
|
|
||||||
m.Data = []byte{}
|
|
||||||
}
|
|
||||||
iNdEx = postIndex
|
|
||||||
default:
|
|
||||||
iNdEx = preIndex
|
|
||||||
skippy, err := skipRecord(data[iNdEx:])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if skippy < 0 {
|
|
||||||
return ErrInvalidLengthRecord
|
|
||||||
}
|
|
||||||
if (iNdEx + skippy) > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
|
|
||||||
iNdEx += skippy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if iNdEx > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (m *Snapshot) Unmarshal(data []byte) error {
|
|
||||||
l := len(data)
|
|
||||||
iNdEx := 0
|
|
||||||
for iNdEx < l {
|
|
||||||
preIndex := iNdEx
|
|
||||||
var wire uint64
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return ErrIntOverflowRecord
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := data[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
wire |= (uint64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fieldNum := int32(wire >> 3)
|
|
||||||
wireType := int(wire & 0x7)
|
|
||||||
if wireType == 4 {
|
|
||||||
return fmt.Errorf("proto: Snapshot: wiretype end group for non-group")
|
|
||||||
}
|
|
||||||
if fieldNum <= 0 {
|
|
||||||
return fmt.Errorf("proto: Snapshot: illegal tag %d (wire type %d)", fieldNum, wire)
|
|
||||||
}
|
|
||||||
switch fieldNum {
|
|
||||||
case 1:
|
|
||||||
if wireType != 0 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType)
|
|
||||||
}
|
|
||||||
m.Index = 0
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return ErrIntOverflowRecord
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := data[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
m.Index |= (uint64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 2:
|
|
||||||
if wireType != 0 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field Term", wireType)
|
|
||||||
}
|
|
||||||
m.Term = 0
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return ErrIntOverflowRecord
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := data[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
m.Term |= (uint64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
iNdEx = preIndex
|
|
||||||
skippy, err := skipRecord(data[iNdEx:])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if skippy < 0 {
|
|
||||||
return ErrInvalidLengthRecord
|
|
||||||
}
|
|
||||||
if (iNdEx + skippy) > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
|
|
||||||
iNdEx += skippy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if iNdEx > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func skipRecord(data []byte) (n int, err error) {
|
|
||||||
l := len(data)
|
|
||||||
iNdEx := 0
|
|
||||||
for iNdEx < l {
|
|
||||||
var wire uint64
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return 0, ErrIntOverflowRecord
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := data[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
wire |= (uint64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wireType := int(wire & 0x7)
|
|
||||||
switch wireType {
|
|
||||||
case 0:
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return 0, ErrIntOverflowRecord
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
iNdEx++
|
|
||||||
if data[iNdEx-1] < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return iNdEx, nil
|
|
||||||
case 1:
|
|
||||||
iNdEx += 8
|
|
||||||
return iNdEx, nil
|
|
||||||
case 2:
|
|
||||||
var length int
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return 0, ErrIntOverflowRecord
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := data[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
length |= (int(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
iNdEx += length
|
|
||||||
if length < 0 {
|
|
||||||
return 0, ErrInvalidLengthRecord
|
|
||||||
}
|
|
||||||
return iNdEx, nil
|
|
||||||
case 3:
|
|
||||||
for {
|
|
||||||
var innerWire uint64
|
|
||||||
var start int = iNdEx
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return 0, ErrIntOverflowRecord
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := data[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
innerWire |= (uint64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
innerWireType := int(innerWire & 0x7)
|
|
||||||
if innerWireType == 4 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
next, err := skipRecord(data[start:])
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
iNdEx = start + next
|
|
||||||
}
|
|
||||||
return iNdEx, nil
|
|
||||||
case 4:
|
|
||||||
return iNdEx, nil
|
|
||||||
case 5:
|
|
||||||
iNdEx += 4
|
|
||||||
return iNdEx, nil
|
|
||||||
default:
|
|
||||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrInvalidLengthRecord = fmt.Errorf("proto: negative length found during unmarshaling")
|
|
||||||
ErrIntOverflowRecord = fmt.Errorf("proto: integer overflow")
|
|
||||||
)
|
|
@ -1,20 +0,0 @@
|
|||||||
syntax = "proto2";
|
|
||||||
package walpb;
|
|
||||||
|
|
||||||
import "gogoproto/gogo.proto";
|
|
||||||
|
|
||||||
option (gogoproto.marshaler_all) = true;
|
|
||||||
option (gogoproto.sizer_all) = true;
|
|
||||||
option (gogoproto.unmarshaler_all) = true;
|
|
||||||
option (gogoproto.goproto_getters_all) = false;
|
|
||||||
|
|
||||||
message Record {
|
|
||||||
optional int64 type = 1 [(gogoproto.nullable) = false];
|
|
||||||
optional uint32 crc = 2 [(gogoproto.nullable) = false];
|
|
||||||
optional bytes data = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Snapshot {
|
|
||||||
optional uint64 index = 1 [(gogoproto.nullable) = false];
|
|
||||||
optional uint64 term = 2 [(gogoproto.nullable) = false];
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user