mirror of https://github.com/k3s-io/k3s
Delete additional third_party
parent
a3bf0b9338
commit
9efef1cdaa
|
@ -1,33 +0,0 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(
|
||||
["**"],
|
||||
exclude = [
|
||||
"etcd*/**",
|
||||
"etcd*.tar.gz",
|
||||
],
|
||||
),
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
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/godep:all-srcs",
|
||||
"//third_party/forked/golang/expansion:all-srcs",
|
||||
"//third_party/forked/golang/reflect:all-srcs",
|
||||
"//third_party/forked/golang/template:all-srcs",
|
||||
"//third_party/forked/gonum/graph:all-srcs",
|
||||
"//third_party/go-srcimporter:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
|
@ -1,10 +0,0 @@
|
|||
# See the OWNERS docs at https://go.k8s.io/owners
|
||||
|
||||
reviewers:
|
||||
- lavalamp
|
||||
- smarterclayton
|
||||
- thockin
|
||||
approvers:
|
||||
- lavalamp
|
||||
- smarterclayton
|
||||
- thockin
|
|
@ -1 +0,0 @@
|
|||
exports_files(["etcd"])
|
|
@ -1 +0,0 @@
|
|||
Forked from etcd 2.2 release branch to support migration from 3.0 WAL to 2.2 WAL format
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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"],
|
||||
)
|
|
@ -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
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
}
|
|
@ -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}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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})
|
||||
}
|
|
@ -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"],
|
||||
)
|
|
@ -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
|
||||
}
|
|
@ -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 +0,0 @@
|
|||
Forked from etcd 2.3 release branch to support migration from 3.0 WAL to 2.3 WAL format
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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()))
|
||||
}
|
|
@ -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"],
|
||||
)
|
|
@ -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
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
}
|
|
@ -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}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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"],
|
||||
)
|
|
@ -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
|
||||
}
|
|
@ -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];
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
/godep
|
|
@ -1,34 +0,0 @@
|
|||
language: go
|
||||
sudo: false
|
||||
go: 1.6
|
||||
script:
|
||||
# Godep's unit tests run git, and git complains
|
||||
# if we don't set these config parameters.
|
||||
# We put dummy values here because they don't matter.
|
||||
- git config --global user.email "you@example.com"
|
||||
- git config --global user.name "Your Name"
|
||||
- test -z "$(go fmt)"
|
||||
- go vet
|
||||
- go test -v
|
||||
- go test -v -race
|
||||
- test -z "$(goimports -l .)"
|
||||
before_install:
|
||||
- go get golang.org/x/tools/cmd/goimports
|
||||
before_deploy:
|
||||
- export OS_TARGETS="linux darwin windows"
|
||||
- export ARCH_TARGETS="386 amd64"
|
||||
- go get github.com/mitchellh/gox
|
||||
- gox -os "$OS_TARGETS" -arch="$ARCH_TARGETS"
|
||||
deploy:
|
||||
skip_cleanup: true
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: Q1JP8LziaXMTxFmNXiyC1YhS9e4M4WnI6UDjRTMf6mm1LZeJyUFOCCtXnifL7RyCIR1hpjp6s8M1aWE+NpuweF96IZI3Uk4ASx5C8FePC4qvhsCdtJ2sLD2GTIrp9b0MS9/+ao20AIbpVDSaLaF9IjqXpMxMyM0P8P5coRTkwItlGxmQbVJW3YuiYcPa8UojwM4EyafO2CIoUKapW8lwb9KcimBJV8PfF/XZjPVhMkn2ABhh5Hqbn2zBJtvPYMMzi0CnY50JQF5LwN3vGTMpTsRP+lOLCNbOWfkl+2hgG7VpKrtx+cX62knOodpF457sIJ31KUzmeLUVBejTGb1zuVeTojuyi8Huo8YBIBCcN+p3Dqd+n2ZK45mIrheGiEJIkf/vI4MI6A01Nu/o+xU0IPsVfAL/xU5j5nntEGfFWVoclPrl9qcfqf74xdRcARzcCJVmdc8iw49DBDHJfnPa3zxzVz//00+Rz6mZXmhk+Npk/HLLNW59vmJIjP+8XOtPor7dST9HrS1a9AcnmIjNuw9yfbwK5769SDVxCKgqNwXW/Dy5F39aIH5AL4I4y9hCEeeT8ctvSJHGOyiB9MWU5jnt5tluPtz5opG51tFXnIYP/XaWpTfO+eJ6x55pbwT+n3LfRS5l1POM+jGAFF1MFWwc14RY7qynEIEzm4Wb/UE=
|
||||
file:
|
||||
- godep_darwin_amd64
|
||||
- godep_linux_amd64
|
||||
- godep_windows_386.exe
|
||||
- godep_windows_amd64.exe
|
||||
on:
|
||||
tags: true
|
||||
repo: tools/godep
|
|
@ -1,57 +0,0 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"dep.go",
|
||||
"diff.go",
|
||||
"doc.go",
|
||||
"errors.go",
|
||||
"get.go",
|
||||
"go.go",
|
||||
"godepfile.go",
|
||||
"license.go",
|
||||
"list.go",
|
||||
"main.go",
|
||||
"msg.go",
|
||||
"path.go",
|
||||
"pkg.go",
|
||||
"restore.go",
|
||||
"rewrite.go",
|
||||
"save.go",
|
||||
"update.go",
|
||||
"util.go",
|
||||
"vcs.go",
|
||||
"version.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/third_party/forked/godep",
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//vendor/github.com/kr/fs:go_default_library",
|
||||
"//vendor/github.com/kr/pretty:go_default_library",
|
||||
"//vendor/github.com/pmezard/go-difflib/difflib:go_default_library",
|
||||
"//vendor/golang.org/x/tools/go/vcs:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_binary(
|
||||
name = "godep",
|
||||
embed = [":go_default_library"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
|
@ -1,412 +0,0 @@
|
|||
#v80 (2018/01/26)
|
||||
|
||||
* Address lin/vet feedback.
|
||||
|
||||
#v79 (2017/02/01)
|
||||
|
||||
* Fixes #531: fullPackageInDir didn't capture the error from fillPackage()
|
||||
|
||||
#v78 (2017/01/19)
|
||||
|
||||
* Don't use build.ImportDir when discovering packages for the package spec. Fixes #529
|
||||
|
||||
#v77 (2017/01/13)
|
||||
|
||||
* Don't include quotes around hg revisions
|
||||
|
||||
#v76 (2017/01/10)
|
||||
|
||||
* Default to vendor being on unless older go versions.
|
||||
|
||||
#v75 (2016/11/02)
|
||||
|
||||
* Add "AUTHORS" and "CONTRIBUTORS" to legal files list: https://github.com/tools/godep/pull/522
|
||||
|
||||
#v74 (2016/06/01)
|
||||
|
||||
* Enable vendor/ on go1.7
|
||||
* No longer use a godep workspace, use vendor/ (yay!)
|
||||
* Notify that support for Godep workspaces will be removed once go1.8 ships
|
||||
|
||||
#v73 (2016/05/31)
|
||||
|
||||
* Fix permission changes on Windows via @alexbrand. Closes #481.
|
||||
|
||||
#v72 (2016/05/27)
|
||||
|
||||
* Improve handling of git remote show origin. Should help in cases where remote HEAD is ambiguous.
|
||||
* Add ISSUE_TEMPLATE
|
||||
|
||||
#v71 (2016/05/24)
|
||||
|
||||
* Preserve permissions on copied files.
|
||||
|
||||
#v70 (2016/05/20)
|
||||
|
||||
* Fix the May changelog dates
|
||||
* No need to call build.Import, we already have the root of the dependency. Fixes an additional comment on #365
|
||||
|
||||
#v69 (2016/05/16)
|
||||
|
||||
* Make sure `devel-<short sha>` enabled `vendor/` unless there is a classic Godep _workspace already.
|
||||
|
||||
#v68 (2016/05/16)
|
||||
|
||||
* `devel-<short sha>` is always considered newer than any released go version
|
||||
|
||||
#v67 (2016/05/13)
|
||||
|
||||
* Attempt to handle missing deps a little better.
|
||||
|
||||
#v66 (2016/05/10)
|
||||
|
||||
* Use `git remote show origin` to find the default branch when restoring a git based package repository that is in detached head state
|
||||
|
||||
#v65 (2016/05/09)
|
||||
|
||||
* Rewrite update so that it considers new transitive dependencies, both in the same repo and outside of it.
|
||||
|
||||
#v64 (2016/05/09)
|
||||
|
||||
* godep update golang.org/x/tools/go/vcs
|
||||
|
||||
#v63 (2016/05/03)
|
||||
|
||||
* Support recording devel-<short sha> so development versions of Go can be matched
|
||||
|
||||
#v62 (2016/04/07)
|
||||
|
||||
* Note new go1.6+ behavior of not checking out master in README / restore help text.
|
||||
|
||||
#v61 (2016/04/06)
|
||||
|
||||
* Obey go version build tags based on recorded major go version. Fixes #448.
|
||||
|
||||
#v60 (2016/03/18)
|
||||
|
||||
* Make the $GOPATH check a warning.
|
||||
|
||||
#v59 (2016/03/18)
|
||||
|
||||
* Enforce requirement to be inside of a go src directory. A lot of time is usually spent
|
||||
tracking down bug reports where people are doign stuff from outside of their $GOPATH. This
|
||||
should help with that, at least until there it time to properly test godep use outside of a
|
||||
$GOPATH and fix the issues.
|
||||
|
||||
#v58 (2016/03/15)
|
||||
|
||||
* Add GodepVersion to Godeps.json file so that as godep changes / adds features / fixes bugs we can know which version of godep most recently wrote out the file.
|
||||
|
||||
#v57 (2016/03/07)
|
||||
|
||||
* Don't use `git rev-parse --show-toplevel` to determine git repo roots as it resolves symlinks: https://github.com/tools/godep/pull/418
|
||||
|
||||
# v56 (2016/02/26)
|
||||
|
||||
* replace path comparisons with case insensitive pathEqual()
|
||||
* add versionString() to debug output
|
||||
* Send log output to Stderr
|
||||
|
||||
# v55 2016/02/22
|
||||
|
||||
* re-saved deps to clean out extra stuff (see v54; godep restore; godep save -r=false; rm -rf Godeps; godep save -r). We're still using a workspace with rewrites so users of older go version can still go get this tool.
|
||||
* Replace simple == with strings.EqualFold in listFiles to avoid problems with case insensitive filesystems ("Code" != "code" when doing a byte by byte comparison)
|
||||
|
||||
# v54 2016/02/22
|
||||
|
||||
* Update some docs around vendor/
|
||||
* More precise recording of dependencies. Removed recursive copying of sub directories of a package (precise vendoring). This should allow using `./...` with the go tool for compilation of project using `vendor/`. See https://github.com/tools/godep/pull/415
|
||||
|
||||
# v53 2016/02/11
|
||||
|
||||
* Disable VendorExperiment if a godep workspace already exists.
|
||||
|
||||
# v52 2016/01/27
|
||||
|
||||
* Trim 'rc' out of go version strings when determining major version.
|
||||
|
||||
# v51 2016/01/21
|
||||
|
||||
* Trim 'beta' out of go version strings when determining major version.
|
||||
|
||||
# v50 2016/01/19
|
||||
|
||||
* More verbose output on save -v.
|
||||
|
||||
# v49 2016/01/13
|
||||
|
||||
* Add UK spelling license/licence to the pile + fix up a bunch of typos
|
||||
* Clarify tag handling in docs
|
||||
|
||||
# v48 2016/01/13
|
||||
|
||||
* Abort restore if there is no $GOPATH set.
|
||||
|
||||
# v47 2016/01/12
|
||||
|
||||
* Dev versions of go should honor the current meaning of GO15VENDOREXPERIMENT
|
||||
|
||||
# v46 2016/01/03
|
||||
|
||||
* Record "devel" when the release is a devel release of go (compiled from git).
|
||||
|
||||
# v45 2015/12/28
|
||||
|
||||
* Upcase windows drive letters before comparing. Fixes #383.
|
||||
|
||||
# v44 2015/12/23
|
||||
|
||||
* Clean package roots when attempting to find a vendor directory so we don't loop forever.
|
||||
* Fixes 382
|
||||
|
||||
# v43 2015/12/22
|
||||
|
||||
* Better error messages when parsing Godeps.json: Fixes #372
|
||||
|
||||
# v42 2015/12/22
|
||||
|
||||
* Fix a bunch of GO15VENDOREXPERIMENT issues
|
||||
* Find package directories better. Previously we used build.FindOnly which didn't work the way I expected it to (any dir would work w/o error).
|
||||
* Set the VendorExperiment bool based on go version as 1.6 defaults to on.
|
||||
* A bunch of extra debugging for use while sanity checking myself.
|
||||
* vendor flag for test structs.
|
||||
* Some tests for vendor/ stuff:
|
||||
* Basic Test
|
||||
* Transitive
|
||||
* Transitive, across GOPATHs + collapse vendor/ directories.
|
||||
* Should Fix #358
|
||||
|
||||
# v41 2015/12/17
|
||||
|
||||
* Don't rewrite packages outside of the project. This would happen if you specified
|
||||
an external package for vendoring when you ran `goodep save -r ./... github.com/some/other/package`
|
||||
|
||||
# v40 2015/12/17
|
||||
|
||||
* When downloading a dependency, create the base directory if needed.
|
||||
|
||||
# v39 2015/12/16
|
||||
|
||||
* Record only the major go version (ex. go1.5) instead of the complete string.
|
||||
|
||||
# v38 2015/12/16
|
||||
|
||||
* Replace `go get`, further fix up restore error handling/reporting.
|
||||
* Fixes #186
|
||||
* Don't bother restoring/downloading if already done.
|
||||
|
||||
# v37 2015/12/15
|
||||
|
||||
* Change up how download/restore works a little
|
||||
* Try to load the package after downloading/restoring. Previously
|
||||
that was done too early in the process.
|
||||
* make previous verbose output debug output
|
||||
* report a typed error instead of a string from listPackage so it can
|
||||
be asserted to provide a nicer error.
|
||||
* Catch go get errors that say there are no go files found. See code
|
||||
comment as to why.
|
||||
* do *all* downloading during download phase.
|
||||
|
||||
# v36 2015/12/14
|
||||
|
||||
* Fixes #358: Using wrong variable. Will add test after release.
|
||||
|
||||
# v35 2015/12/11
|
||||
|
||||
* Fixes #356: Major performance regressions in v34
|
||||
* Enable cpu profiling via flag on save.
|
||||
* Cache packages by dir
|
||||
* Don't do a full import pass on deps for packages in the GOROOT
|
||||
* create a bit less garbage at times
|
||||
* Generalize -v & -d flags
|
||||
|
||||
# v34 2015/12/08
|
||||
|
||||
* We now use build.Context to help locate packages only and do our own parsing (via go/ast).
|
||||
* Fixes reported issues caused by v33 (Removal of `go list`):
|
||||
* #345: Bug in godep restore
|
||||
* #346: Fix loading a dot package
|
||||
* #348: Godep save issue when importing lib/pq
|
||||
* #350: undefined: build.MultiplePackageError
|
||||
* #351: stow away helper files
|
||||
* #353: cannot find package "appengine"
|
||||
* Don't process imports of `.go` files tagged with the `appengine` build tag.
|
||||
|
||||
# v33 2015/12/07
|
||||
|
||||
* Replace the use of `go list`. This is a large change although all existing tests pass.
|
||||
* Don't process the imports of `.go` files with the `ignore` build tag.
|
||||
|
||||
# v32 2015/12/02
|
||||
|
||||
* Eval Symlinks in Contains() check.
|
||||
|
||||
# v31 2015/12/02
|
||||
|
||||
* In restore, mention which package had the problem -- @shurcool
|
||||
|
||||
# v30 2015/11/25
|
||||
|
||||
* Add `-t` flag to the `godep get` command.
|
||||
|
||||
# v29 2015/11/17
|
||||
|
||||
* Temp work around to fix issue with LICENSE files.
|
||||
|
||||
# v28 2015/11/09
|
||||
|
||||
* Make `version` an actual command.
|
||||
|
||||
# v27 2015/11/06
|
||||
|
||||
* run command once during restore -v
|
||||
|
||||
# v26 2015/11/05
|
||||
|
||||
* Better fix for the issue fixed in v25: All update paths are now path.Clean()'d
|
||||
|
||||
# v25 2015/11/05
|
||||
|
||||
* `godep update package/` == `godep update package`. Fixes #313
|
||||
|
||||
# v24 2015/11/05
|
||||
|
||||
* Honor -t during update. Fixes #312
|
||||
|
||||
# v23 2015/11/05
|
||||
|
||||
* Do not use --debug to find full revision name for mercurial repositories
|
||||
|
||||
# v22 2015/11/14
|
||||
|
||||
* s/GOVENDOREXPERIMENT/GO15VENDOREXPERIMENT :-(
|
||||
|
||||
# v21 2015/11/13
|
||||
|
||||
* Fix #310: Case insensitive fs issue
|
||||
|
||||
# v20 2015/11/13
|
||||
|
||||
* Attempt to include license files when vendoring. (@client9)
|
||||
|
||||
# v19 2015/11/3
|
||||
|
||||
* Fix conflict error message. Revisions were swapped. Also better selection of package that needs update.
|
||||
|
||||
# v18 2015/10/16
|
||||
|
||||
* Improve error message when trying to save a conflicting revision.
|
||||
|
||||
# v17 2015/10/15
|
||||
|
||||
* Fix for v16 bug. All vcs list commands now produce paths relative to the root of the vcs.
|
||||
|
||||
# v16 2015/10/15
|
||||
|
||||
* Determine repo root using vcs commands and use that instead of dep.dir
|
||||
|
||||
# v15 2015/10/14
|
||||
|
||||
* Update .travis.yml file to do releases to github
|
||||
|
||||
# v14 2015/10/08
|
||||
|
||||
* Don't print out a workspace path when GO15VENDOREXPERIMENT is active. The vendor/ directory is not a valid workspace, so can't be added to your $GOPATH.
|
||||
|
||||
# v13 2015/10/07
|
||||
|
||||
* Do restores in 2 separate steps, first download all deps and then check out the recorded revisions.
|
||||
* Update Changelog date format
|
||||
|
||||
# v12 2015/09/22
|
||||
|
||||
* Extract errors into separate file.
|
||||
|
||||
# v11 2015/08/22
|
||||
|
||||
* Amend code to pass golint.
|
||||
|
||||
# v10 2015/09/21
|
||||
|
||||
* Analyse vendored package test dependencies.
|
||||
* Update documentation.
|
||||
|
||||
# v9 2015/09/17
|
||||
|
||||
* Don't save test dependencies by default.
|
||||
|
||||
# v8 2015/09/17
|
||||
|
||||
* Reorganize code.
|
||||
|
||||
# v7 2015/09/09
|
||||
|
||||
* Add verbose flag.
|
||||
* Skip untracked files.
|
||||
* Add VCS list command.
|
||||
|
||||
# v6 2015/09/04
|
||||
|
||||
* Revert ignoring testdata directories and instead ignore it while
|
||||
processing Go files and copy the whole directory unmodified.
|
||||
|
||||
# v5 2015/09/04
|
||||
|
||||
* Fix vcs selection in restore command to work as go get does
|
||||
|
||||
# v4 2015/09/03
|
||||
|
||||
* Remove the deprecated copy option.
|
||||
|
||||
# v3 2015/08/26
|
||||
|
||||
* Ignore testdata directories
|
||||
|
||||
# v2 2015/08/11
|
||||
|
||||
* Include command line packages in the set to copy
|
||||
|
||||
This is a simplification to how we define the behavior
|
||||
of the save command. Now it has two distinct package
|
||||
parameters, the "root set" and the "destination", and
|
||||
they have clearer roles. The packages listed on the
|
||||
command line form the root set; they and all their
|
||||
dependencies will be copied into the Godeps directory.
|
||||
Additionally, the destination (always ".") will form the
|
||||
initial list of "seen" import paths to exclude from
|
||||
copying.
|
||||
|
||||
In the common case, the root set is equal to the
|
||||
destination, so the effective behavior doesn't change.
|
||||
This is primarily just a simpler definition. However, if
|
||||
the user specifies a package on the command line that
|
||||
lives outside of . then that package will be copied.
|
||||
|
||||
As a side effect, there's a simplification to the way we
|
||||
add packages to the initial "seen" set. Formerly, to
|
||||
avoid copying dependencies unnecessarily, we would try
|
||||
to find the root of the VCS repo for each package in the
|
||||
root set, and mark the import path of the entire repo as
|
||||
seen. This meant for a repo at path C, if destination
|
||||
C/S imports C/T, we would not copy C/T into C/S/Godeps.
|
||||
Now we don't treat the repo root specially, and as
|
||||
mentioned above, the destination alone is considered
|
||||
seen.
|
||||
|
||||
This also means we don't require listed packages to be
|
||||
in VCS unless they're outside of the destination.
|
||||
|
||||
# v1 2015/07/20
|
||||
|
||||
* godep version command
|
||||
|
||||
Output the version as well as some godep runtime information that is
|
||||
useful for debugging user's issues.
|
||||
|
||||
The version const would be bumped each time a PR is merged into master
|
||||
to ensure that we'll be able to tell which version someone got when they
|
||||
did a `go get github.com/tools/godep`.
|
||||
|
||||
# Older changes
|
||||
|
||||
Many and more, see `git log -p`
|
|
@ -1,22 +0,0 @@
|
|||
## Why do I need to check in `vendor/`?
|
||||
|
||||
godep's primary concern is to allow you to repeatably build your project. Your
|
||||
dependencies are part of that project. Without them it won't build. Not
|
||||
committing `vendor/` adds additional external dependencies that are outside of
|
||||
your control. In Go, fetching packages is tied to multiple external systems
|
||||
(DNS, web servers, etc). Over time other developers or code hosting sites may
|
||||
discontinue service, delete code, force push, or take any number of other
|
||||
actions that may make a package unreachable. Therefore it's the opinion of the
|
||||
godep authors that `vendor/` should always be checked in.
|
||||
|
||||
## Should I use `godep restore`?
|
||||
|
||||
Probably not, unless you **need** to. Situations where you would **need** to are:
|
||||
|
||||
1. Using older Godep Workspaces (`Godeps/_workspace`) and not using `godep go
|
||||
<cmd>`.
|
||||
1. Resetting the state of $GOPATH to what is in your `Godeps.json` file in order
|
||||
to cleanly re-vendor everything w/o upgrading/changing any deps. This is
|
||||
useful when [migrating](https://github.com/tools/godep#migrating-to-vendor)
|
||||
from workspaces to `vendor` or when a bug is fixed in `godep` that cleans up
|
||||
a previous vendoring error.
|
|
@ -1,28 +0,0 @@
|
|||
Copyright © 2013 Keith Rarick.
|
||||
Portions Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,209 +0,0 @@
|
|||
# Godep - Archived
|
||||
|
||||
Please use [dep](https://github.com/golang/dep) or another tool instead.
|
||||
|
||||
The rest of this readme is preserved for those that may still need its contents.
|
||||
|
||||
[![Build Status](https://travis-ci.org/tools/godep.svg)](https://travis-ci.org/tools/godep)
|
||||
|
||||
[![GoDoc](https://godoc.org/github.com/tools/godep?status.svg)](https://godoc.org/github.com/tools/godep)
|
||||
|
||||
godep helps build packages reproducibly by fixing their dependencies.
|
||||
|
||||
This tool assumes you are working in a standard Go workspace, as described [here](http://golang.org/doc/code.html). We
|
||||
expect godep to build on Go 1.4* or newer, but you can use it on any project that works with Go 1 or newer.
|
||||
|
||||
Please check the [FAQ](FAQ.md) if you have a question.
|
||||
|
||||
## Golang Dep
|
||||
|
||||
The Go community now has the [dep](https://github.com/golang/dep) project to
|
||||
manage dependencies. Please consider trying to migrate from Godep to dep. If there
|
||||
is an issue preventing you from migrating please file an issue with dep so the
|
||||
problem can be corrected. Godep will continue to be supported for some time but
|
||||
is considered to be in a state of support rather than active feature development.
|
||||
|
||||
## Install
|
||||
|
||||
```console
|
||||
go get github.com/tools/godep
|
||||
```
|
||||
|
||||
## How to use godep with a new project
|
||||
|
||||
Assuming you've got everything working already, so you can build your project
|
||||
with `go install` and test it with `go test`, it's one command to start using:
|
||||
|
||||
```console
|
||||
godep save
|
||||
```
|
||||
|
||||
This will save a list of dependencies to the file `Godeps/Godeps.json` and copy
|
||||
their source code into `vendor/` (or `Godeps/_workspace/` when using older
|
||||
versions of Go). Godep does **not copy**:
|
||||
|
||||
- files from source repositories that are not tracked in version control.
|
||||
- `*_test.go` files.
|
||||
- `testdata` directories.
|
||||
- files outside of the go packages.
|
||||
|
||||
Godep does not process the imports of `.go` files with either the `ignore`
|
||||
or `appengine` build tags.
|
||||
|
||||
Test files and testdata directories can be saved by adding `-t`.
|
||||
|
||||
Read over the contents of `vendor/` and make sure it looks reasonable. Then
|
||||
commit the `Godeps/` and `vendor/` directories to version control.
|
||||
|
||||
## The deprecated `-r` flag
|
||||
|
||||
For older versions of Go, the `-r` flag tells save to automatically rewrite
|
||||
package import paths. This allows your code to refer directly to the copied
|
||||
dependencies in `Godeps/_workspace`. So, a package C that depends on package
|
||||
D will actually import `C/Godeps/_workspace/src/D`. This makes C's repo
|
||||
self-contained and causes `go get` to build C with the right version of all
|
||||
dependencies.
|
||||
|
||||
If you don't use `-r`, when using older version of Go, then in order to use the
|
||||
fixed dependencies and get reproducible builds, you must make sure that **every
|
||||
time** you run a Go-related command, you wrap it in one of these two ways:
|
||||
|
||||
- If the command you are running is just `go`, run it as `godep go ...`, e.g.
|
||||
`godep go install -v ./...`
|
||||
- When using a different command, set your `$GOPATH` using `godep path` as
|
||||
described below.
|
||||
|
||||
`-r` isn't necessary with go1.6+ and isn't allowed.
|
||||
|
||||
## Additional Operations
|
||||
|
||||
### Restore
|
||||
|
||||
The `godep restore` installs the
|
||||
package versions specified in `Godeps/Godeps.json` to your `$GOPATH`. This
|
||||
modifies the state of packages in your `$GOPATH`. NOTE: `godep restore` leaves
|
||||
git repositories in a detached state. `go1.6`+ no longer checks out the master
|
||||
branch when doing a `go get`, see [here](https://github.com/golang/go/commit/42206598671a44111c8f726ad33dc7b265bdf669).
|
||||
|
||||
> If you run `godep restore` in your main `$GOPATH` `go get -u` will fail on packages that are behind master.
|
||||
|
||||
Please see the [FAQ](https://github.com/tools/godep/blob/master/FAQ.md#should-i-use-godep-restore) section about restore.
|
||||
|
||||
### Edit-test Cycle
|
||||
|
||||
1. Edit code
|
||||
1. Run `godep go test`
|
||||
1. (repeat)
|
||||
|
||||
### Add a Dependency
|
||||
|
||||
To add a new package foo/bar, do this:
|
||||
|
||||
1. Run `go get foo/bar`
|
||||
1. Edit your code to import foo/bar.
|
||||
1. Run `godep save` (or `godep save ./...`).
|
||||
|
||||
### Update a Dependency
|
||||
|
||||
To update a package from your `$GOPATH`, do this:
|
||||
|
||||
1. Run `go get -u foo/bar`
|
||||
1. Run `godep update foo/bar`.
|
||||
|
||||
You can use the `...` wildcard, for example `godep update foo/...`. Before comitting the change, you'll probably want to
|
||||
inspect the changes to Godeps, for example with `git diff`, and make sure it looks reasonable.
|
||||
|
||||
## Multiple Packages
|
||||
|
||||
If your repository has more than one package, you're probably accustomed to
|
||||
running commands like `go test ./...`, `go install ./...`, and `go fmt ./...`.
|
||||
Similarly, you should run `godep save ./...` to capture the dependencies of all
|
||||
packages in your application.
|
||||
|
||||
## File Format
|
||||
|
||||
Godeps is a json file with the following structure:
|
||||
|
||||
```go
|
||||
type Godeps struct {
|
||||
ImportPath string
|
||||
GoVersion string // Abridged output of 'go version'.
|
||||
GodepVersion string // Abridged output of 'godep version'
|
||||
Packages []string // Arguments to godep save, if any.
|
||||
Deps []struct {
|
||||
ImportPath string
|
||||
Comment string // Description of commit, if present.
|
||||
Rev string // VCS-specific commit ID.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Example Godeps:
|
||||
|
||||
```json
|
||||
{
|
||||
"ImportPath": "github.com/kr/hk",
|
||||
"GoVersion": "go1.6",
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "code.google.com/p/go-netrc/netrc",
|
||||
"Rev": "28676070ab99"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/kr/binarydist",
|
||||
"Rev": "3380ade90f8b0dfa3e363fd7d7e941fa857d0d13"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Migrating to vendor/
|
||||
|
||||
Godep supports the Go 1.5+ vendor/
|
||||
[experiment](https://github.com/golang/go/commit/183cc0cd41f06f83cb7a2490a499e3f9101befff)
|
||||
utilizing the same environment variable that the go tooling itself supports
|
||||
(`GO15VENDOREXPERIMENT`).
|
||||
|
||||
godep mostly works the same way as the `go` command line tool. If you have go
|
||||
1.5.X and set `GO15VENDOREXPERIMENT=1` or have go1.6.X (or devel) `vendor/`
|
||||
is enabled. **Unless** you already have a `Godeps/_workspace`. This is a safety
|
||||
feature and godep warns you about this.
|
||||
|
||||
When `vendor/` is enabled godep will write the vendored code into the top level
|
||||
`./vendor/` directory. A `./Godeps/Godeps.json` file is created to track
|
||||
the dependencies and revisions. `vendor/` is not compatible with rewrites.
|
||||
|
||||
There is currently no automated migration between the old Godeps workspace and
|
||||
the vendor directory, but the following steps should work:
|
||||
|
||||
```term
|
||||
# just to be safe
|
||||
$ unset GO15VENDOREXPERIMENT
|
||||
|
||||
# restore currently vendored deps to the $GOPATH
|
||||
$ godep restore
|
||||
|
||||
# The next line is only needed to automatically undo rewritten imports that were
|
||||
# created with godep save -r.
|
||||
$ godep save -r=false <pkg spec>
|
||||
|
||||
# Remove the old Godeps folder
|
||||
$ rm -rf Godeps
|
||||
|
||||
# If on go1.5.X to enable `vendor/`
|
||||
$ export GO15VENDOREXPERIMENT=1
|
||||
|
||||
# re-analyze deps and save to `vendor/`.
|
||||
$ godep save <pkg spec>
|
||||
|
||||
# Add the changes to your VCS
|
||||
$ git add -A . ; git commit -am "Godep workspace -> vendor/"
|
||||
|
||||
# You should see your Godeps/_workspace/src files "moved" to vendor/.
|
||||
```
|
||||
|
||||
## Releasing
|
||||
|
||||
1. Increment the version in `version.go`.
|
||||
1. Tag the commit with the same version number.
|
||||
1. Update `Changelog.md`.
|
|
@ -1,128 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A Dependency is a specific revision of a package.
|
||||
type Dependency struct {
|
||||
ImportPath string
|
||||
Comment string `json:",omitempty"` // Description of commit, if present.
|
||||
Rev string // VCS-specific commit ID.
|
||||
|
||||
// used by command save & update
|
||||
ws string // workspace
|
||||
root string // import path to repo root
|
||||
dir string // full path to package
|
||||
|
||||
// used by command update
|
||||
matched bool // selected for update by command line
|
||||
pkg *Package
|
||||
missing bool // packages is missing
|
||||
|
||||
// used by command go
|
||||
vcs *VCS
|
||||
}
|
||||
|
||||
func eqDeps(a, b []Dependency) bool {
|
||||
ok := true
|
||||
for _, da := range a {
|
||||
for _, db := range b {
|
||||
if da.ImportPath == db.ImportPath && da.Rev != db.Rev {
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// containsPathPrefix returns whether any string in a
|
||||
// is s or a directory containing s.
|
||||
// For example, pattern ["a"] matches "a" and "a/b"
|
||||
// (but not "ab").
|
||||
func containsPathPrefix(pats []string, s string) bool {
|
||||
for _, pat := range pats {
|
||||
if pat == s || strings.HasPrefix(s, pat+"/") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func uniq(a []string) []string {
|
||||
var s string
|
||||
var i int
|
||||
if !sort.StringsAreSorted(a) {
|
||||
sort.Strings(a)
|
||||
}
|
||||
for _, t := range a {
|
||||
if t != s {
|
||||
a[i] = t
|
||||
i++
|
||||
s = t
|
||||
}
|
||||
}
|
||||
return a[:i]
|
||||
}
|
||||
|
||||
// trimGoVersion and return the major version
|
||||
func trimGoVersion(version string) (string, error) {
|
||||
if version == "devel" {
|
||||
return "devel", nil
|
||||
}
|
||||
if strings.HasPrefix(version, "devel+") || strings.HasPrefix(version, "devel-") {
|
||||
return strings.Replace(version, "devel+", "devel-", 1), nil
|
||||
}
|
||||
p := strings.Split(version, ".")
|
||||
if len(p) < 2 {
|
||||
return "", fmt.Errorf("Error determining major go version from: %q", version)
|
||||
}
|
||||
var split string
|
||||
switch {
|
||||
case strings.Contains(p[1], "beta"):
|
||||
split = "beta"
|
||||
case strings.Contains(p[1], "rc"):
|
||||
split = "rc"
|
||||
}
|
||||
if split != "" {
|
||||
p[1] = strings.Split(p[1], split)[0]
|
||||
}
|
||||
return p[0] + "." + p[1], nil
|
||||
}
|
||||
|
||||
var goVersionTestOutput = ""
|
||||
|
||||
func getGoVersion() (string, error) {
|
||||
// For testing purposes only
|
||||
if goVersionTestOutput != "" {
|
||||
return goVersionTestOutput, nil
|
||||
}
|
||||
|
||||
// Godep might have been compiled with a different
|
||||
// version, so we can't just use runtime.Version here.
|
||||
cmd := exec.Command("go", "version")
|
||||
cmd.Stderr = os.Stderr
|
||||
out, err := cmd.Output()
|
||||
return string(out), err
|
||||
}
|
||||
|
||||
// goVersion returns the major version string of the Go compiler
|
||||
// currently installed, e.g. "go1.5".
|
||||
func goVersion() (string, error) {
|
||||
out, err := getGoVersion()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
gv := strings.Split(out, " ")
|
||||
if len(gv) < 4 {
|
||||
return "", fmt.Errorf("Error splitting output of `go version`: Expected 4 or more elements, but there are < 4: %q", out)
|
||||
}
|
||||
if gv[2] == "devel" {
|
||||
return trimGoVersion(gv[2] + gv[3])
|
||||
}
|
||||
return trimGoVersion(gv[2])
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/pmezard/go-difflib/difflib"
|
||||
)
|
||||
|
||||
var cmdDiff = &Command{
|
||||
Name: "diff",
|
||||
Short: "shows the diff between current and previously saved set of dependencies",
|
||||
Long: `
|
||||
Shows the difference, in a unified diff format, between the
|
||||
current set of dependencies and those generated on a
|
||||
previous 'go save' execution.
|
||||
`,
|
||||
Run: runDiff,
|
||||
OnlyInGOPATH: true,
|
||||
}
|
||||
|
||||
func runDiff(cmd *Command, args []string) {
|
||||
gold, err := loadDefaultGodepsFile()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
pkgs := []string{"."}
|
||||
dot, err := LoadPackages(pkgs...)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
gnew := &Godeps{
|
||||
ImportPath: dot[0].ImportPath,
|
||||
GoVersion: gold.GoVersion,
|
||||
}
|
||||
|
||||
err = gnew.fill(dot, dot[0].ImportPath)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
diff, err := diffStr(&gold, gnew)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
fmt.Println(diff)
|
||||
}
|
||||
|
||||
// diffStr returns a unified diff string of two Godeps.
|
||||
func diffStr(a, b *Godeps) (string, error) {
|
||||
var ab, bb bytes.Buffer
|
||||
|
||||
_, err := a.writeTo(&ab)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
_, err = b.writeTo(&bb)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
diff := difflib.UnifiedDiff{
|
||||
A: difflib.SplitLines(ab.String()),
|
||||
B: difflib.SplitLines(bb.String()),
|
||||
FromFile: b.file(),
|
||||
ToFile: "$GOPATH",
|
||||
Context: 10,
|
||||
}
|
||||
return difflib.GetUnifiedDiffString(diff)
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
|
||||
Command godep helps build packages reproducibly by fixing
|
||||
their dependencies.
|
||||
|
||||
Example Usage
|
||||
|
||||
Save currently-used dependencies to file Godeps:
|
||||
|
||||
$ godep save
|
||||
|
||||
Build project using saved dependencies:
|
||||
|
||||
$ godep go install
|
||||
|
||||
or
|
||||
|
||||
$ GOPATH=`godep path`:$GOPATH
|
||||
$ go install
|
||||
|
||||
*/
|
||||
package main
|
|
@ -1,18 +0,0 @@
|
|||
package main
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
errorLoadingDeps = errors.New("error loading dependencies")
|
||||
errorLoadingPackages = errors.New("error loading packages")
|
||||
errorCopyingSourceCode = errors.New("error copying source code")
|
||||
errorNoPackagesUpdatable = errors.New("no packages can be updated")
|
||||
)
|
||||
|
||||
type errPackageNotFound struct {
|
||||
path string
|
||||
}
|
||||
|
||||
func (e errPackageNotFound) Error() string {
|
||||
return "Package (" + e.path + ") not found"
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
var cmdGet = &Command{
|
||||
Name: "get",
|
||||
Args: "[-t] [packages]",
|
||||
Short: "download and install packages with specified dependencies",
|
||||
Long: `
|
||||
Get downloads to GOPATH the packages named by the import paths, and installs
|
||||
them with the dependencies specified in their Godeps files.
|
||||
|
||||
If any of the packages do not have Godeps files, those are installed
|
||||
as if by go get.
|
||||
|
||||
If -t is given, dependencies of test files are also downloaded and installed.
|
||||
|
||||
For more about specifying packages, see 'go help packages'.
|
||||
`,
|
||||
Run: runGet,
|
||||
OnlyInGOPATH: true,
|
||||
}
|
||||
|
||||
var getT bool
|
||||
|
||||
func init() {
|
||||
cmdGet.Flag.BoolVar(&getT, "t", false, "get test dependencies")
|
||||
}
|
||||
|
||||
func runGet(cmd *Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
args = []string{"."}
|
||||
}
|
||||
|
||||
cmdArgs := []interface{}{"get", "-d"}
|
||||
if verbose {
|
||||
cmdArgs = append(cmdArgs, "-v")
|
||||
}
|
||||
|
||||
if getT {
|
||||
cmdArgs = append(cmdArgs, "-t")
|
||||
}
|
||||
|
||||
err := command("go", append(cmdArgs, args)...).Run()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// group import paths by Godeps location
|
||||
groups := make(map[string][]string)
|
||||
ps, err := LoadPackages(args...)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
for _, pkg := range ps {
|
||||
if pkg.Error.Err != "" {
|
||||
log.Fatalln(pkg.Error.Err)
|
||||
}
|
||||
dir, _ := findInParents(pkg.Dir, "Godeps")
|
||||
groups[dir] = append(groups[dir], pkg.ImportPath)
|
||||
}
|
||||
for dir, packages := range groups {
|
||||
var c *exec.Cmd
|
||||
if dir == "" {
|
||||
c = command("go", "install", packages)
|
||||
} else {
|
||||
c = command("godep", "go", "install", packages)
|
||||
c.Dir = dir
|
||||
}
|
||||
if err := c.Run(); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// command is like exec.Command, but the returned
|
||||
// Cmd inherits stderr from the current process, and
|
||||
// elements of args may be either string or []string.
|
||||
func command(name string, args ...interface{}) *exec.Cmd {
|
||||
var a []string
|
||||
for _, arg := range args {
|
||||
switch v := arg.(type) {
|
||||
case string:
|
||||
a = append(a, v)
|
||||
case []string:
|
||||
a = append(a, v...)
|
||||
}
|
||||
}
|
||||
c := exec.Command(name, a...)
|
||||
c.Stderr = os.Stderr
|
||||
return c
|
||||
}
|
|
@ -1,129 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var cmdGo = &Command{
|
||||
Name: "go",
|
||||
Args: "command [arguments]",
|
||||
Short: "run the go tool with saved dependencies",
|
||||
Long: `
|
||||
Go runs the go tool with a modified GOPATH giving access to
|
||||
dependencies saved in Godeps.
|
||||
|
||||
Any go tool command can run this way, but "godep go get"
|
||||
is unnecessary and has been disabled. Instead, use
|
||||
"godep go install".
|
||||
`,
|
||||
Run: runGo,
|
||||
OnlyInGOPATH: true,
|
||||
}
|
||||
|
||||
// Find the godep GOPATH for this file tree and run the go tool.
|
||||
func runGo(cmd *Command, args []string) {
|
||||
gopath := prepareGopath()
|
||||
if s := os.Getenv("GOPATH"); s != "" {
|
||||
gopath += string(os.PathListSeparator) + os.Getenv("GOPATH")
|
||||
}
|
||||
if len(args) > 0 && args[0] == "get" {
|
||||
log.Printf("invalid subcommand: %q", "go get")
|
||||
fmt.Fprintln(os.Stderr, "Use 'godep go install' instead.")
|
||||
fmt.Fprintln(os.Stderr, "Run 'godep help go' for usage.")
|
||||
os.Exit(2)
|
||||
}
|
||||
c := exec.Command("go", args...)
|
||||
c.Env = append(envNoGopath(), "GOPATH="+gopath)
|
||||
c.Stdin = os.Stdin
|
||||
c.Stdout = os.Stdout
|
||||
c.Stderr = os.Stderr
|
||||
err := c.Run()
|
||||
if err != nil {
|
||||
log.Fatalln("go", err)
|
||||
}
|
||||
}
|
||||
|
||||
// prepareGopath reads dependency information from the filesystem
|
||||
// entry name, fetches any necessary code, and returns a gopath
|
||||
// causing the specified dependencies to be used.
|
||||
func prepareGopath() (gopath string) {
|
||||
dir, isDir := findGodeps()
|
||||
if dir == "" {
|
||||
log.Fatalln("No Godeps found (or in any parent directory)")
|
||||
}
|
||||
if !isDir {
|
||||
log.Fatalln(strings.TrimSpace(needSource))
|
||||
}
|
||||
return filepath.Join(dir, "Godeps", "_workspace")
|
||||
}
|
||||
|
||||
// findGodeps looks for a directory entry "Godeps" in the
|
||||
// current directory or any parent, and returns the containing
|
||||
// directory and whether the entry itself is a directory.
|
||||
// If Godeps can't be found, findGodeps returns "".
|
||||
// For any other error, it exits the program.
|
||||
func findGodeps() (dir string, isDir bool) {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
return findInParents(wd, "Godeps")
|
||||
}
|
||||
|
||||
// isRoot returns true iff a path is a root.
|
||||
// On Unix: "/".
|
||||
// On Windows: "C:\", "D:\", ...
|
||||
func isRoot(p string) bool {
|
||||
p = filepath.Clean(p)
|
||||
volume := filepath.VolumeName(p)
|
||||
|
||||
p = strings.TrimPrefix(p, volume)
|
||||
p = filepath.ToSlash(p)
|
||||
|
||||
return p == "/"
|
||||
}
|
||||
|
||||
// findInParents returns the path to the directory containing name
|
||||
// in dir or any ancestor, and whether name itself is a directory.
|
||||
// If name cannot be found, findInParents returns the empty string.
|
||||
func findInParents(dir, name string) (container string, isDir bool) {
|
||||
for {
|
||||
fi, err := os.Stat(filepath.Join(dir, name))
|
||||
if os.IsNotExist(err) && isRoot(dir) {
|
||||
return "", false
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
dir = filepath.Dir(dir)
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
return dir, fi.IsDir()
|
||||
}
|
||||
}
|
||||
|
||||
func envNoGopath() (a []string) {
|
||||
for _, s := range os.Environ() {
|
||||
if !strings.HasPrefix(s, "GOPATH=") {
|
||||
a = append(a, s)
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
const needSource = `
|
||||
outdated Godeps missing source code
|
||||
|
||||
This dependency list was created with an old version of godep.
|
||||
|
||||
To work around this, you have two options:
|
||||
1. Run 'godep restore', and try again.
|
||||
2. Ask the maintainer to switch to a newer version of godep,
|
||||
then try again with the updated package.
|
||||
`
|
|
@ -1,218 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var (
|
||||
godepsFile = filepath.Join("Godeps", "Godeps.json")
|
||||
oldGodepsFile = filepath.Join("Godeps")
|
||||
)
|
||||
|
||||
// Godeps describes what a package needs to be rebuilt reproducibly.
|
||||
// It's the same information stored in file Godeps.
|
||||
type Godeps struct {
|
||||
ImportPath string
|
||||
GoVersion string
|
||||
GodepVersion string
|
||||
Packages []string `json:",omitempty"` // Arguments to save, if any.
|
||||
Deps []Dependency
|
||||
isOldFile bool
|
||||
}
|
||||
|
||||
func loadGodepsFile(path string) (Godeps, error) {
|
||||
var g Godeps
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return g, err
|
||||
}
|
||||
defer f.Close()
|
||||
err = json.NewDecoder(f).Decode(&g)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Unable to parse %s: %s", path, err.Error())
|
||||
}
|
||||
return g, err
|
||||
}
|
||||
|
||||
func loadDefaultGodepsFile() (Godeps, error) {
|
||||
var g Godeps
|
||||
var err error
|
||||
g, err = loadGodepsFile(godepsFile)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
var err1 error
|
||||
g, err1 = loadGodepsFile(oldGodepsFile)
|
||||
if err1 != nil {
|
||||
if os.IsNotExist(err1) {
|
||||
return g, err
|
||||
}
|
||||
return g, err1
|
||||
}
|
||||
g.isOldFile = true
|
||||
return g, nil
|
||||
}
|
||||
}
|
||||
return g, err
|
||||
}
|
||||
|
||||
// pkgs is the list of packages to read dependencies for
|
||||
func (g *Godeps) fill(pkgs []*Package, destImportPath string) error {
|
||||
debugln("fill", destImportPath)
|
||||
ppln(pkgs)
|
||||
var err1 error
|
||||
var path, testImports []string
|
||||
dipp := []string{destImportPath}
|
||||
for _, p := range pkgs {
|
||||
if p.Standard {
|
||||
log.Println("ignoring stdlib package:", p.ImportPath)
|
||||
continue
|
||||
}
|
||||
if p.Error.Err != "" {
|
||||
log.Println(p.Error.Err)
|
||||
err1 = errorLoadingPackages
|
||||
continue
|
||||
}
|
||||
path = append(path, p.ImportPath)
|
||||
path = append(path, p.Deps...)
|
||||
testImports = append(testImports, p.TestImports...)
|
||||
testImports = append(testImports, p.XTestImports...)
|
||||
}
|
||||
ps, err := LoadPackages(testImports...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range ps {
|
||||
if p.Standard {
|
||||
continue
|
||||
}
|
||||
if p.Error.Err != "" {
|
||||
log.Println(p.Error.Err)
|
||||
err1 = errorLoadingPackages
|
||||
continue
|
||||
}
|
||||
path = append(path, p.ImportPath)
|
||||
path = append(path, p.Deps...)
|
||||
}
|
||||
debugln("path", path)
|
||||
for i, p := range path {
|
||||
path[i] = unqualify(p)
|
||||
}
|
||||
path = uniq(path)
|
||||
debugln("uniq, unqualify'd path", path)
|
||||
ps, err = LoadPackages(path...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, pkg := range ps {
|
||||
if pkg.Error.Err != "" {
|
||||
log.Println(pkg.Error.Err)
|
||||
err1 = errorLoadingDeps
|
||||
continue
|
||||
}
|
||||
if pkg.Standard || containsPathPrefix(dipp, pkg.ImportPath) {
|
||||
debugln("standard or dest skipping", pkg.ImportPath)
|
||||
continue
|
||||
}
|
||||
vcs, reporoot, err := VCSFromDir(pkg.Dir, filepath.Join(pkg.Root, "src"))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
err1 = errorLoadingDeps
|
||||
continue
|
||||
}
|
||||
id, err := vcs.identify(pkg.Dir)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
err1 = errorLoadingDeps
|
||||
continue
|
||||
}
|
||||
if vcs.isDirty(pkg.Dir, id) {
|
||||
log.Println("dirty working tree (please commit changes):", pkg.Dir)
|
||||
err1 = errorLoadingDeps
|
||||
continue
|
||||
}
|
||||
comment := vcs.describe(pkg.Dir, id)
|
||||
g.Deps = append(g.Deps, Dependency{
|
||||
ImportPath: pkg.ImportPath,
|
||||
Rev: id,
|
||||
Comment: comment,
|
||||
dir: pkg.Dir,
|
||||
ws: pkg.Root,
|
||||
root: filepath.ToSlash(reporoot),
|
||||
vcs: vcs,
|
||||
})
|
||||
}
|
||||
return err1
|
||||
}
|
||||
|
||||
func (g *Godeps) copy() *Godeps {
|
||||
h := *g
|
||||
h.Deps = make([]Dependency, len(g.Deps))
|
||||
copy(h.Deps, g.Deps)
|
||||
return &h
|
||||
}
|
||||
|
||||
func (g *Godeps) file() string {
|
||||
if g.isOldFile {
|
||||
return oldGodepsFile
|
||||
}
|
||||
return godepsFile
|
||||
}
|
||||
|
||||
func (g *Godeps) save() (int64, error) {
|
||||
f, err := os.Create(g.file())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer f.Close()
|
||||
return g.writeTo(f)
|
||||
}
|
||||
|
||||
func (g *Godeps) writeTo(w io.Writer) (int64, error) {
|
||||
g.GodepVersion = fmt.Sprintf("v%s", version) // godep always writes its current version.
|
||||
b, err := json.MarshalIndent(g, "", "\t")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n, err := w.Write(append(b, '\n'))
|
||||
return int64(n), err
|
||||
}
|
||||
|
||||
func (g *Godeps) addOrUpdateDeps(deps []Dependency) {
|
||||
var missing []Dependency
|
||||
for _, d := range deps {
|
||||
var found bool
|
||||
for i := range g.Deps {
|
||||
if g.Deps[i].ImportPath == d.ImportPath {
|
||||
g.Deps[i] = d
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
missing = append(missing, d)
|
||||
}
|
||||
}
|
||||
g.Deps = append(g.Deps, missing...)
|
||||
}
|
||||
|
||||
func (g *Godeps) removeDeps(deps []Dependency) {
|
||||
var f []Dependency
|
||||
for i := range g.Deps {
|
||||
var found bool
|
||||
for _, d := range deps {
|
||||
if g.Deps[i].ImportPath == d.ImportPath {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
f = append(f, g.Deps[i])
|
||||
}
|
||||
}
|
||||
g.Deps = f
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// LicenseFilePrefix is a list of filename prefixes that indicate it
|
||||
// might contain a software license
|
||||
var LicenseFilePrefix = []string{
|
||||
"licence", // UK spelling
|
||||
"license", // US spelling
|
||||
"copying",
|
||||
"unlicense",
|
||||
"copyright",
|
||||
"copyleft",
|
||||
"authors",
|
||||
"contributors",
|
||||
}
|
||||
|
||||
// LegalFileSubstring are substrings that indicate the file is likely
|
||||
// to contain some type of legal declaration. "legal" is often used
|
||||
// that it might moved to LicenseFilePrefix
|
||||
var LegalFileSubstring = []string{
|
||||
"legal",
|
||||
"notice",
|
||||
"disclaimer",
|
||||
"patent",
|
||||
"third-party",
|
||||
"thirdparty",
|
||||
}
|
||||
|
||||
// IsLicenseFile returns true if the filename might be contain a
|
||||
// software license
|
||||
func IsLicenseFile(filename string) bool {
|
||||
lowerfile := strings.ToLower(filename)
|
||||
for _, prefix := range LicenseFilePrefix {
|
||||
if strings.HasPrefix(lowerfile, prefix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsLegalFile returns true if the file is likely to contain some type
|
||||
// of of legal declaration or licensing information
|
||||
func IsLegalFile(filename string) bool {
|
||||
lowerfile := strings.ToLower(filename)
|
||||
for _, prefix := range LicenseFilePrefix {
|
||||
if strings.HasPrefix(lowerfile, prefix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, substring := range LegalFileSubstring {
|
||||
if strings.Contains(lowerfile, substring) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -1,601 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
pathpkg "path"
|
||||
)
|
||||
|
||||
var (
|
||||
gorootSrc = filepath.Join(build.Default.GOROOT, "src")
|
||||
ignoreTags = []string{"appengine", "ignore"} //TODO: appengine is a special case for now: https://github.com/tools/godep/issues/353
|
||||
versionMatch = regexp.MustCompile(`\Ago\d+\.\d+\z`)
|
||||
versionNegativeMatch = regexp.MustCompile(`\A\!go\d+\.\d+\z`)
|
||||
)
|
||||
|
||||
type errorMissingDep struct {
|
||||
i, dir string // import, dir
|
||||
}
|
||||
|
||||
func (e errorMissingDep) Error() string {
|
||||
return "Unable to find dependent package " + e.i + " in context of " + e.dir
|
||||
}
|
||||
|
||||
// packageContext is used to track an import and which package imported it.
|
||||
type packageContext struct {
|
||||
pkg *build.Package // package that imports the import
|
||||
imp string // import
|
||||
}
|
||||
|
||||
// depScanner tracks the processed and to be processed packageContexts
|
||||
type depScanner struct {
|
||||
processed []packageContext
|
||||
todo []packageContext
|
||||
}
|
||||
|
||||
// Next package and import to process
|
||||
func (ds *depScanner) Next() (*build.Package, string) {
|
||||
c := ds.todo[0]
|
||||
ds.processed = append(ds.processed, c)
|
||||
ds.todo = ds.todo[1:]
|
||||
return c.pkg, c.imp
|
||||
}
|
||||
|
||||
// Continue looping?
|
||||
func (ds *depScanner) Continue() bool {
|
||||
return len(ds.todo) > 0
|
||||
}
|
||||
|
||||
// Add a package and imports to the depScanner. Skips already processed/pending package/import combos
|
||||
func (ds *depScanner) Add(pkg *build.Package, imports ...string) {
|
||||
NextImport:
|
||||
for _, i := range imports {
|
||||
if i == "C" {
|
||||
i = "runtime/cgo"
|
||||
}
|
||||
for _, epc := range ds.processed {
|
||||
if pkg.Dir == epc.pkg.Dir && i == epc.imp {
|
||||
debugln("ctxts epc.pkg.Dir == pkg.Dir && i == epc.imp, skipping", epc.pkg.Dir, i)
|
||||
continue NextImport
|
||||
}
|
||||
}
|
||||
for _, epc := range ds.todo {
|
||||
if pkg.Dir == epc.pkg.Dir && i == epc.imp {
|
||||
debugln("ctxts epc.pkg.Dir == pkg.Dir && i == epc.imp, skipping", epc.pkg.Dir, i)
|
||||
continue NextImport
|
||||
}
|
||||
}
|
||||
pc := packageContext{pkg, i}
|
||||
debugln("Adding pc:", pc.pkg.Dir, pc.imp)
|
||||
ds.todo = append(ds.todo, pc)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
pkgCache = make(map[string]*build.Package) // dir => *build.Package
|
||||
)
|
||||
|
||||
// returns the package in dir either from a cache or by importing it and then caching it
|
||||
func fullPackageInDir(dir string) (*build.Package, error) {
|
||||
var err error
|
||||
pkg, ok := pkgCache[dir]
|
||||
if !ok {
|
||||
pkg, _ = build.ImportDir(dir, build.FindOnly)
|
||||
if pkg.Goroot {
|
||||
pkg, err = build.ImportDir(pkg.Dir, 0)
|
||||
} else {
|
||||
err = fillPackage(pkg)
|
||||
}
|
||||
if err == nil {
|
||||
pkgCache[dir] = pkg
|
||||
}
|
||||
}
|
||||
return pkg, err
|
||||
}
|
||||
|
||||
// listPackage specified by path
|
||||
func listPackage(path string) (*Package, error) {
|
||||
debugln("listPackage", path)
|
||||
var lp *build.Package
|
||||
dir, err := findDirForPath(path, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lp, err = fullPackageInDir(dir)
|
||||
p := &Package{
|
||||
Dir: lp.Dir,
|
||||
Root: lp.Root,
|
||||
ImportPath: lp.ImportPath,
|
||||
XTestImports: lp.XTestImports,
|
||||
TestImports: lp.TestImports,
|
||||
GoFiles: lp.GoFiles,
|
||||
CgoFiles: lp.CgoFiles,
|
||||
TestGoFiles: lp.TestGoFiles,
|
||||
XTestGoFiles: lp.XTestGoFiles,
|
||||
IgnoredGoFiles: lp.IgnoredGoFiles,
|
||||
}
|
||||
p.Standard = lp.Goroot && lp.ImportPath != "" && !strings.Contains(lp.ImportPath, ".")
|
||||
if err != nil || p.Standard {
|
||||
return p, err
|
||||
}
|
||||
debugln("Looking For Package:", path, "in", dir)
|
||||
ppln(lp)
|
||||
|
||||
ds := depScanner{}
|
||||
ds.Add(lp, lp.Imports...)
|
||||
for ds.Continue() {
|
||||
ip, i := ds.Next()
|
||||
|
||||
debugf("Processing import %s for %s\n", i, ip.Dir)
|
||||
pdir, err := findDirForPath(i, ip)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dp, err := fullPackageInDir(pdir)
|
||||
if err != nil { // This really should happen in this context though
|
||||
ppln(err)
|
||||
return nil, errorMissingDep{i: i, dir: ip.Dir}
|
||||
}
|
||||
ppln(dp)
|
||||
if !dp.Goroot {
|
||||
// Don't bother adding packages in GOROOT to the dependency scanner, they don't import things from outside of it.
|
||||
ds.Add(dp, dp.Imports...)
|
||||
}
|
||||
debugln("lp:")
|
||||
ppln(lp)
|
||||
debugln("ip:")
|
||||
ppln(ip)
|
||||
if lp == ip {
|
||||
debugln("lp == ip")
|
||||
p.Imports = append(p.Imports, dp.ImportPath)
|
||||
}
|
||||
p.Deps = append(p.Deps, dp.ImportPath)
|
||||
p.Dependencies = addDependency(p.Dependencies, dp)
|
||||
}
|
||||
p.Imports = uniq(p.Imports)
|
||||
p.Deps = uniq(p.Deps)
|
||||
debugln("Done Looking For Package:", path, "in", dir)
|
||||
ppln(p)
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func addDependency(deps []build.Package, d *build.Package) []build.Package {
|
||||
for i := range deps {
|
||||
if deps[i].Dir == d.Dir {
|
||||
return deps
|
||||
}
|
||||
}
|
||||
return append(deps, *d)
|
||||
}
|
||||
|
||||
// finds the directory for the given import path in the context of the provided build.Package (if provided)
|
||||
func findDirForPath(path string, ip *build.Package) (string, error) {
|
||||
debugln("findDirForPath", path, ip)
|
||||
var search []string
|
||||
|
||||
if build.IsLocalImport(path) {
|
||||
dir := path
|
||||
if !filepath.IsAbs(dir) {
|
||||
if abs, err := filepath.Abs(dir); err == nil {
|
||||
// interpret relative to current directory
|
||||
dir = abs
|
||||
}
|
||||
}
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
// We need to check to see if the import exists in vendor/ folders up the hierarchy of the importing package
|
||||
if VendorExperiment && ip != nil {
|
||||
debugln("resolving vendor posibilities:", ip.Dir, ip.Root)
|
||||
cr := cleanPath(ip.Root)
|
||||
|
||||
for base := cleanPath(ip.Dir); !pathEqual(base, cr); base = cleanPath(filepath.Dir(base)) {
|
||||
s := filepath.Join(base, "vendor", path)
|
||||
debugln("Adding search dir:", s)
|
||||
search = append(search, s)
|
||||
}
|
||||
}
|
||||
|
||||
for _, base := range build.Default.SrcDirs() {
|
||||
search = append(search, filepath.Join(base, path))
|
||||
}
|
||||
|
||||
for _, dir := range search {
|
||||
debugln("searching", dir)
|
||||
fi, err := stat(dir)
|
||||
if err == nil && fi.IsDir() {
|
||||
return dir, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", errPackageNotFound{path}
|
||||
}
|
||||
|
||||
type statEntry struct {
|
||||
fi os.FileInfo
|
||||
err error
|
||||
}
|
||||
|
||||
var (
|
||||
statCache = make(map[string]statEntry)
|
||||
)
|
||||
|
||||
func clearStatCache() {
|
||||
statCache = make(map[string]statEntry)
|
||||
}
|
||||
|
||||
func stat(p string) (os.FileInfo, error) {
|
||||
if e, ok := statCache[p]; ok {
|
||||
return e.fi, e.err
|
||||
}
|
||||
fi, err := os.Stat(p)
|
||||
statCache[p] = statEntry{fi, err}
|
||||
return fi, err
|
||||
}
|
||||
|
||||
// fillPackage full of info. Assumes p.Dir is set at a minimum
|
||||
func fillPackage(p *build.Package) error {
|
||||
if p.Goroot {
|
||||
return nil
|
||||
}
|
||||
|
||||
if p.SrcRoot == "" {
|
||||
for _, base := range build.Default.SrcDirs() {
|
||||
if strings.HasPrefix(p.Dir, base) {
|
||||
p.SrcRoot = base
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if p.SrcRoot == "" {
|
||||
return errors.New("Unable to find SrcRoot for package " + p.ImportPath)
|
||||
}
|
||||
|
||||
if p.Root == "" {
|
||||
p.Root = filepath.Dir(p.SrcRoot)
|
||||
}
|
||||
|
||||
var buildMatch = "+build "
|
||||
var buildFieldSplit = func(r rune) bool {
|
||||
return unicode.IsSpace(r) || r == ','
|
||||
}
|
||||
|
||||
debugln("Filling package:", p.ImportPath, "from", p.Dir)
|
||||
gofiles, err := filepath.Glob(filepath.Join(p.Dir, "*.go"))
|
||||
if err != nil {
|
||||
debugln("Error globbing", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if len(gofiles) == 0 {
|
||||
return &build.NoGoError{Dir: p.Dir}
|
||||
}
|
||||
|
||||
var testImports []string
|
||||
var imports []string
|
||||
NextFile:
|
||||
for _, file := range gofiles {
|
||||
debugln(file)
|
||||
pf, err := parser.ParseFile(token.NewFileSet(), file, nil, parser.ImportsOnly|parser.ParseComments)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
testFile := strings.HasSuffix(file, "_test.go")
|
||||
fname := filepath.Base(file)
|
||||
for _, c := range pf.Comments {
|
||||
ct := c.Text()
|
||||
if i := strings.Index(ct, buildMatch); i != -1 {
|
||||
for _, t := range strings.FieldsFunc(ct[i+len(buildMatch):], buildFieldSplit) {
|
||||
for _, tag := range ignoreTags {
|
||||
if t == tag {
|
||||
p.IgnoredGoFiles = append(p.IgnoredGoFiles, fname)
|
||||
continue NextFile
|
||||
}
|
||||
}
|
||||
|
||||
if versionMatch.MatchString(t) && !isSameOrNewer(t, majorGoVersion) {
|
||||
debugln("Adding", fname, "to ignored list because of version tag", t)
|
||||
p.IgnoredGoFiles = append(p.IgnoredGoFiles, fname)
|
||||
continue NextFile
|
||||
}
|
||||
if versionNegativeMatch.MatchString(t) && isSameOrNewer(t[1:], majorGoVersion) {
|
||||
debugln("Adding", fname, "to ignored list because of version tag", t)
|
||||
p.IgnoredGoFiles = append(p.IgnoredGoFiles, fname)
|
||||
continue NextFile
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if testFile {
|
||||
p.TestGoFiles = append(p.TestGoFiles, fname)
|
||||
} else {
|
||||
p.GoFiles = append(p.GoFiles, fname)
|
||||
}
|
||||
for _, is := range pf.Imports {
|
||||
name, err := strconv.Unquote(is.Path.Value)
|
||||
if err != nil {
|
||||
return err // can't happen?
|
||||
}
|
||||
if testFile {
|
||||
testImports = append(testImports, name)
|
||||
} else {
|
||||
imports = append(imports, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
imports = uniq(imports)
|
||||
testImports = uniq(testImports)
|
||||
p.Imports = imports
|
||||
p.TestImports = testImports
|
||||
return nil
|
||||
}
|
||||
|
||||
// All of the following functions were vendored from go proper. Locations are noted in comments, but may change in future Go versions.
|
||||
|
||||
// importPaths returns the import paths to use for the given command line.
|
||||
// $GOROOT/src/cmd/main.go:366
|
||||
func importPaths(args []string) []string {
|
||||
debugf("importPathsNoDotExpansion(%q) == ", args)
|
||||
args = importPathsNoDotExpansion(args)
|
||||
debugf("%q\n", args)
|
||||
var out []string
|
||||
for _, a := range args {
|
||||
if strings.Contains(a, "...") {
|
||||
if build.IsLocalImport(a) {
|
||||
debugf("build.IsLocalImport(%q) == true\n", a)
|
||||
pkgs := allPackagesInFS(a)
|
||||
debugf("allPackagesInFS(%q) == %q\n", a, pkgs)
|
||||
out = append(out, pkgs...)
|
||||
} else {
|
||||
debugf("build.IsLocalImport(%q) == false\n", a)
|
||||
pkgs := allPackages(a)
|
||||
debugf("allPackages(%q) == %q\n", a, pkgs)
|
||||
out = append(out, allPackages(a)...)
|
||||
}
|
||||
continue
|
||||
}
|
||||
out = append(out, a)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// importPathsNoDotExpansion returns the import paths to use for the given
|
||||
// command line, but it does no ... expansion.
|
||||
// $GOROOT/src/cmd/main.go:332
|
||||
func importPathsNoDotExpansion(args []string) []string {
|
||||
if len(args) == 0 {
|
||||
return []string{"."}
|
||||
}
|
||||
var out []string
|
||||
for _, a := range args {
|
||||
// Arguments are supposed to be import paths, but
|
||||
// as a courtesy to Windows developers, rewrite \ to /
|
||||
// in command-line arguments. Handles .\... and so on.
|
||||
if filepath.Separator == '\\' {
|
||||
a = strings.Replace(a, `\`, `/`, -1)
|
||||
}
|
||||
|
||||
// Put argument in canonical form, but preserve leading ./.
|
||||
if strings.HasPrefix(a, "./") {
|
||||
a = "./" + pathpkg.Clean(a)
|
||||
if a == "./." {
|
||||
a = "."
|
||||
}
|
||||
} else {
|
||||
a = pathpkg.Clean(a)
|
||||
}
|
||||
if a == "all" || a == "std" || a == "cmd" {
|
||||
out = append(out, allPackages(a)...)
|
||||
continue
|
||||
}
|
||||
out = append(out, a)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// allPackagesInFS is like allPackages but is passed a pattern
|
||||
// beginning ./ or ../, meaning it should scan the tree rooted
|
||||
// at the given directory. There are ... in the pattern too.
|
||||
// $GOROOT/src/cmd/main.go:620
|
||||
func allPackagesInFS(pattern string) []string {
|
||||
pkgs := matchPackagesInFS(pattern)
|
||||
if len(pkgs) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
|
||||
}
|
||||
return pkgs
|
||||
}
|
||||
|
||||
// allPackages returns all the packages that can be found
|
||||
// under the $GOPATH directories and $GOROOT matching pattern.
|
||||
// The pattern is either "all" (all packages), "std" (standard packages),
|
||||
// "cmd" (standard commands), or a path including "...".
|
||||
// $GOROOT/src/cmd/main.go:542
|
||||
func allPackages(pattern string) []string {
|
||||
pkgs := matchPackages(pattern)
|
||||
if len(pkgs) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
|
||||
}
|
||||
return pkgs
|
||||
}
|
||||
|
||||
// $GOROOT/src/cmd/main.go:554
|
||||
// This has been changed to not use build.ImportDir
|
||||
func matchPackages(pattern string) []string {
|
||||
match := func(string) bool { return true }
|
||||
treeCanMatch := func(string) bool { return true }
|
||||
if pattern != "all" && pattern != "std" && pattern != "cmd" {
|
||||
match = matchPattern(pattern)
|
||||
treeCanMatch = treeCanMatchPattern(pattern)
|
||||
}
|
||||
|
||||
have := map[string]bool{
|
||||
"builtin": true, // ignore pseudo-package that exists only for documentation
|
||||
}
|
||||
if !build.Default.CgoEnabled {
|
||||
have["runtime/cgo"] = true // ignore during walk
|
||||
}
|
||||
var pkgs []string
|
||||
|
||||
for _, src := range build.Default.SrcDirs() {
|
||||
if (pattern == "std" || pattern == "cmd") && src != gorootSrc {
|
||||
continue
|
||||
}
|
||||
src = filepath.Clean(src) + string(filepath.Separator)
|
||||
root := src
|
||||
if pattern == "cmd" {
|
||||
root += "cmd" + string(filepath.Separator)
|
||||
}
|
||||
filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil || !fi.IsDir() || path == src {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Avoid .foo, _foo, and testdata directory trees.
|
||||
_, elem := filepath.Split(path)
|
||||
if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
name := filepath.ToSlash(path[len(src):])
|
||||
if pattern == "std" && (strings.Contains(name, ".") || name == "cmd") {
|
||||
// The name "std" is only the standard library.
|
||||
// If the name has a dot, assume it's a domain name for go get,
|
||||
// and if the name is cmd, it's the root of the command tree.
|
||||
return filepath.SkipDir
|
||||
}
|
||||
if !treeCanMatch(name) {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
if have[name] {
|
||||
return nil
|
||||
}
|
||||
have[name] = true
|
||||
if !match(name) {
|
||||
return nil
|
||||
}
|
||||
|
||||
ap, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if _, err = fullPackageInDir(ap); err != nil {
|
||||
debugf("matchPackage(%q) ap=%q Error: %q\n", ap, pattern, err)
|
||||
if _, noGo := err.(*build.NoGoError); noGo {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
pkgs = append(pkgs, name)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
return pkgs
|
||||
}
|
||||
|
||||
// treeCanMatchPattern(pattern)(name) reports whether
|
||||
// name or children of name can possibly match pattern.
|
||||
// Pattern is the same limited glob accepted by matchPattern.
|
||||
// $GOROOT/src/cmd/main.go:527
|
||||
func treeCanMatchPattern(pattern string) func(name string) bool {
|
||||
wildCard := false
|
||||
if i := strings.Index(pattern, "..."); i >= 0 {
|
||||
wildCard = true
|
||||
pattern = pattern[:i]
|
||||
}
|
||||
return func(name string) bool {
|
||||
return len(name) <= len(pattern) && hasPathPrefix(pattern, name) ||
|
||||
wildCard && strings.HasPrefix(name, pattern)
|
||||
}
|
||||
}
|
||||
|
||||
// hasPathPrefix reports whether the path s begins with the
|
||||
// elements in prefix.
|
||||
// $GOROOT/src/cmd/main.go:489
|
||||
func hasPathPrefix(s, prefix string) bool {
|
||||
switch {
|
||||
default:
|
||||
return false
|
||||
case len(s) == len(prefix):
|
||||
return s == prefix
|
||||
case len(s) > len(prefix):
|
||||
if prefix != "" && prefix[len(prefix)-1] == '/' {
|
||||
return strings.HasPrefix(s, prefix)
|
||||
}
|
||||
return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
|
||||
}
|
||||
}
|
||||
|
||||
// $GOROOT/src/cmd/go/main.go:631
|
||||
// This has been changed to not use build.ImportDir
|
||||
func matchPackagesInFS(pattern string) []string {
|
||||
// Find directory to begin the scan.
|
||||
// Could be smarter but this one optimization
|
||||
// is enough for now, since ... is usually at the
|
||||
// end of a path.
|
||||
i := strings.Index(pattern, "...")
|
||||
dir, _ := pathpkg.Split(pattern[:i])
|
||||
|
||||
// pattern begins with ./ or ../.
|
||||
// path.Clean will discard the ./ but not the ../.
|
||||
// We need to preserve the ./ for pattern matching
|
||||
// and in the returned import paths.
|
||||
prefix := ""
|
||||
if strings.HasPrefix(pattern, "./") {
|
||||
prefix = "./"
|
||||
}
|
||||
match := matchPattern(pattern)
|
||||
|
||||
var pkgs []string
|
||||
filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil || !fi.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if path == dir {
|
||||
// filepath.Walk starts at dir and recurses. For the recursive case,
|
||||
// the path is the result of filepath.Join, which calls filepath.Clean.
|
||||
// The initial case is not Cleaned, though, so we do this explicitly.
|
||||
//
|
||||
// This converts a path like "./io/" to "io". Without this step, running
|
||||
// "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io
|
||||
// package, because prepending the prefix "./" to the unclean path would
|
||||
// result in "././io", and match("././io") returns false.
|
||||
path = filepath.Clean(path)
|
||||
}
|
||||
|
||||
// Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..".
|
||||
_, elem := filepath.Split(path)
|
||||
dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".."
|
||||
if dot || strings.HasPrefix(elem, "_") || elem == "testdata" {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
name := prefix + filepath.ToSlash(path)
|
||||
if !match(name) {
|
||||
return nil
|
||||
}
|
||||
ap, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if _, err = fullPackageInDir(ap); err != nil {
|
||||
debugf("matchPackageInFS(%q) ap=%q Error: %q\n", ap, pattern, err)
|
||||
if _, noGo := err.(*build.NoGoError); !noGo {
|
||||
log.Print(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
pkgs = append(pkgs, name)
|
||||
return nil
|
||||
})
|
||||
return pkgs
|
||||
}
|
|
@ -1,255 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime/pprof"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var (
|
||||
cpuprofile string
|
||||
verbose bool // Verbose flag for commands that support it
|
||||
debug bool // Debug flag for commands that support it
|
||||
majorGoVersion string
|
||||
VendorExperiment bool
|
||||
sep string
|
||||
)
|
||||
|
||||
// Command is an implementation of a godep command
|
||||
// like godep save or godep go.
|
||||
type Command struct {
|
||||
// Run runs the command.
|
||||
// The args are the arguments after the command name.
|
||||
Run func(cmd *Command, args []string)
|
||||
|
||||
// Name of the command
|
||||
Name string
|
||||
|
||||
// Args the command would expect
|
||||
Args string
|
||||
|
||||
// Short is the short description shown in the 'godep help' output.
|
||||
Short string
|
||||
|
||||
// Long is the long message shown in the
|
||||
// 'godep help <this-command>' output.
|
||||
Long string
|
||||
|
||||
// Flag is a set of flags specific to this command.
|
||||
Flag flag.FlagSet
|
||||
|
||||
// OnlyInGOPATH limits this command to being run only while inside of a GOPATH
|
||||
OnlyInGOPATH bool
|
||||
}
|
||||
|
||||
// UsageExit prints usage information and exits.
|
||||
func (c *Command) UsageExit() {
|
||||
fmt.Fprintf(os.Stderr, "Args: godep %s [-v] [-d] %s\n\n", c.Name, c.Args)
|
||||
fmt.Fprintf(os.Stderr, "Run 'godep help %s' for help.\n", c.Name)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
// Commands lists the available commands and help topics.
|
||||
// The order here is the order in which they are printed
|
||||
// by 'godep help'.
|
||||
var commands = []*Command{
|
||||
cmdSave,
|
||||
cmdGo,
|
||||
cmdGet,
|
||||
cmdPath,
|
||||
cmdRestore,
|
||||
cmdUpdate,
|
||||
cmdDiff,
|
||||
cmdVersion,
|
||||
}
|
||||
|
||||
// VendorExperiment is the Go 1.5 vendor directory experiment flag, see
|
||||
// https://github.com/golang/go/commit/183cc0cd41f06f83cb7a2490a499e3f9101befff
|
||||
// Honor the env var unless the project already has an old school godep workspace
|
||||
func determineVendor(v string) bool {
|
||||
go15ve := os.Getenv("GO15VENDOREXPERIMENT")
|
||||
var ev bool
|
||||
switch v {
|
||||
case "go1", "go1.1", "go1.2", "go1.3", "go1.4":
|
||||
ev = false
|
||||
case "go1.5":
|
||||
ev = go15ve == "1"
|
||||
case "go1.6":
|
||||
ev = go15ve != "0"
|
||||
default: //go1.7+, devel*
|
||||
ev = true
|
||||
}
|
||||
|
||||
ws := filepath.Join("Godeps", "_workspace")
|
||||
s, err := os.Stat(ws)
|
||||
if err == nil && s.IsDir() {
|
||||
log.Printf("WARNING: Godep workspaces (./Godeps/_workspace) are deprecated and support for them will be removed when go1.8 is released.")
|
||||
if ev {
|
||||
log.Printf("WARNING: Go version (%s) & $GO15VENDOREXPERIMENT=%s wants to enable the vendor experiment, but disabling because a Godep workspace (%s) exists\n", v, go15ve, ws)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return ev
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix("godep: ")
|
||||
log.SetOutput(os.Stderr)
|
||||
|
||||
flag.Usage = usageExit
|
||||
flag.Parse()
|
||||
args := flag.Args()
|
||||
if len(args) < 1 {
|
||||
usageExit()
|
||||
}
|
||||
|
||||
if args[0] == "help" {
|
||||
help(args[1:])
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
majorGoVersion, err = goVersion()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, cmd := range commands {
|
||||
if cmd.Name == args[0] {
|
||||
if cmd.OnlyInGOPATH {
|
||||
checkInGOPATH()
|
||||
}
|
||||
|
||||
VendorExperiment = determineVendor(majorGoVersion)
|
||||
// sep is the signature set of path elements that
|
||||
// precede the original path of an imported package.
|
||||
sep = defaultSep(VendorExperiment)
|
||||
|
||||
cmd.Flag.BoolVar(&verbose, "v", false, "enable verbose output")
|
||||
cmd.Flag.BoolVar(&debug, "d", false, "enable debug output")
|
||||
cmd.Flag.StringVar(&cpuprofile, "cpuprofile", "", "Write cpu profile to this file")
|
||||
cmd.Flag.Usage = func() { cmd.UsageExit() }
|
||||
cmd.Flag.Parse(args[1:])
|
||||
|
||||
debugln("versionString()", versionString())
|
||||
debugln("majorGoVersion", majorGoVersion)
|
||||
debugln("VendorExperiment", VendorExperiment)
|
||||
debugln("sep", sep)
|
||||
|
||||
if cpuprofile != "" {
|
||||
f, err := os.Create(cpuprofile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
pprof.StartCPUProfile(f)
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
cmd.Run(cmd, cmd.Flag.Args())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "godep: unknown command %q\n", args[0])
|
||||
fmt.Fprintf(os.Stderr, "Run 'godep help' for usage.\n")
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
func subPath(sub, path string) bool {
|
||||
ls := strings.ToLower(sub)
|
||||
lp := strings.ToLower(path)
|
||||
if ls == lp {
|
||||
return false
|
||||
}
|
||||
return strings.HasPrefix(ls, lp)
|
||||
}
|
||||
|
||||
func checkInGOPATH() {
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatal("Unable to determine current working directory", err)
|
||||
}
|
||||
dirs := build.Default.SrcDirs()
|
||||
for _, p := range dirs {
|
||||
if ok := subPath(pwd, p); ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("[WARNING]: godep should only be used inside a valid go package directory and")
|
||||
log.Println("[WARNING]: may not function correctly. You are probably outside of your $GOPATH.")
|
||||
log.Printf("[WARNING]:\tCurrent Directory: %s\n", pwd)
|
||||
log.Printf("[WARNING]:\t$GOPATH: %s\n", os.Getenv("GOPATH"))
|
||||
}
|
||||
|
||||
var usageTemplate = `
|
||||
Godep is a tool for managing Go package dependencies.
|
||||
|
||||
Usage:
|
||||
|
||||
godep command [arguments]
|
||||
|
||||
The commands are:
|
||||
{{range .}}
|
||||
{{.Name | printf "%-8s"}} {{.Short}}{{end}}
|
||||
|
||||
Use "godep help [command]" for more information about a command.
|
||||
`
|
||||
|
||||
var helpTemplate = `
|
||||
Args: godep {{.Name}} [-v] [-d] {{.Args}}
|
||||
|
||||
{{.Long | trim}}
|
||||
|
||||
If -v is given, verbose output is enabled.
|
||||
|
||||
If -d is given, debug output is enabled (you probably don't want this, see -v).
|
||||
|
||||
`
|
||||
|
||||
func help(args []string) {
|
||||
if len(args) == 0 {
|
||||
printUsage(os.Stdout)
|
||||
return
|
||||
}
|
||||
if len(args) != 1 {
|
||||
fmt.Fprintf(os.Stderr, "usage: godep help command\n\n")
|
||||
fmt.Fprintf(os.Stderr, "Too many arguments given.\n")
|
||||
os.Exit(2)
|
||||
}
|
||||
for _, cmd := range commands {
|
||||
if cmd.Name == args[0] {
|
||||
tmpl(os.Stdout, helpTemplate, cmd)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func usageExit() {
|
||||
printUsage(os.Stderr)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
func printUsage(w io.Writer) {
|
||||
tmpl(w, usageTemplate, commands)
|
||||
}
|
||||
|
||||
// tmpl executes the given template text on data, writing the result to w.
|
||||
func tmpl(w io.Writer, text string, data interface{}) {
|
||||
t := template.New("top")
|
||||
t.Funcs(template.FuncMap{
|
||||
"trim": strings.TrimSpace,
|
||||
})
|
||||
template.Must(t.Parse(strings.TrimSpace(text) + "\n\n"))
|
||||
if err := t.Execute(w, data); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/kr/pretty"
|
||||
)
|
||||
|
||||
func debugln(a ...interface{}) (int, error) {
|
||||
if debug {
|
||||
return fmt.Println(a...)
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func verboseln(a ...interface{}) {
|
||||
if verbose {
|
||||
log.Println(a...)
|
||||
}
|
||||
}
|
||||
|
||||
func debugf(format string, a ...interface{}) (int, error) {
|
||||
if debug {
|
||||
return fmt.Printf(format, a...)
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func ppln(a ...interface{}) (int, error) {
|
||||
if debug {
|
||||
return pretty.Println(a...)
|
||||
}
|
||||
return 0, nil
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var cmdPath = &Command{
|
||||
Name: "path",
|
||||
Short: "print GOPATH for dependency code",
|
||||
Long: `
|
||||
Command path prints a path for use in env var GOPATH
|
||||
that makes available the specified version of each dependency.
|
||||
|
||||
The printed path does not include any GOPATH value from
|
||||
the environment.
|
||||
|
||||
For more about how GOPATH works, see 'go help gopath'.
|
||||
`,
|
||||
Run: runPath,
|
||||
OnlyInGOPATH: true,
|
||||
}
|
||||
|
||||
// Print the gopath that points to
|
||||
// the included dependency code.
|
||||
func runPath(cmd *Command, args []string) {
|
||||
if len(args) != 0 {
|
||||
cmd.UsageExit()
|
||||
}
|
||||
if VendorExperiment {
|
||||
fmt.Fprintln(os.Stderr, "Error: GO15VENDOREXPERIMENT is enabled and the vendor/ directory is not a valid Go workspace.")
|
||||
os.Exit(1)
|
||||
}
|
||||
gopath := prepareGopath()
|
||||
fmt.Println(gopath)
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/build"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Package represents a Go package.
|
||||
type Package struct {
|
||||
Dir string
|
||||
Root string
|
||||
ImportPath string
|
||||
Deps []string
|
||||
Standard bool
|
||||
Processed bool
|
||||
|
||||
GoFiles []string
|
||||
CgoFiles []string
|
||||
IgnoredGoFiles []string
|
||||
|
||||
TestGoFiles []string
|
||||
TestImports []string
|
||||
XTestGoFiles []string
|
||||
XTestImports []string
|
||||
|
||||
Error struct {
|
||||
Err string
|
||||
}
|
||||
|
||||
// --- New stuff for now
|
||||
Imports []string
|
||||
Dependencies []build.Package
|
||||
}
|
||||
|
||||
// LoadPackages loads the named packages
|
||||
// Unlike the go tool, an empty argument list is treated as an empty list; "."
|
||||
// must be given explicitly if desired.
|
||||
// IgnoredGoFiles will be processed and their dependencies resolved recursively
|
||||
func LoadPackages(names ...string) (a []*Package, err error) {
|
||||
debugln("LoadPackages", names)
|
||||
if len(names) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
pkgs := strings.Split(ignorePackages, ",")
|
||||
sort.Strings(pkgs)
|
||||
for _, i := range importPaths(names) {
|
||||
p, err := listPackage(i)
|
||||
if err != nil {
|
||||
if len(pkgs) > 0 {
|
||||
idx := sort.SearchStrings(pkgs, i)
|
||||
if idx < len(pkgs) && pkgs[idx] == i {
|
||||
fmt.Fprintf(os.Stderr, "warning: ignoring package %q \n", i)
|
||||
continue
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
a = append(a, p)
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (p *Package) allGoFiles() []string {
|
||||
var a []string
|
||||
a = append(a, p.GoFiles...)
|
||||
a = append(a, p.CgoFiles...)
|
||||
a = append(a, p.TestGoFiles...)
|
||||
a = append(a, p.XTestGoFiles...)
|
||||
a = append(a, p.IgnoredGoFiles...)
|
||||
return a
|
||||
}
|
||||
|
||||
// matchPattern(pattern)(name) reports whether
|
||||
// name matches pattern. Pattern is a limited glob
|
||||
// pattern in which '...' means 'any string' and there
|
||||
// is no other special syntax.
|
||||
// Taken from $GOROOT/src/cmd/go/main.go.
|
||||
func matchPattern(pattern string) func(name string) bool {
|
||||
re := regexp.QuoteMeta(pattern)
|
||||
re = strings.Replace(re, `\.\.\.`, `.*`, -1)
|
||||
// Special case: foo/... matches foo too.
|
||||
if strings.HasSuffix(re, `/.*`) {
|
||||
re = re[:len(re)-len(`/.*`)] + `(/.*)?`
|
||||
}
|
||||
reg := regexp.MustCompile(`^` + re + `$`)
|
||||
return func(name string) bool {
|
||||
return reg.MatchString(name)
|
||||
}
|
||||
}
|
|
@ -1,195 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"go/build"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/tools/go/vcs"
|
||||
)
|
||||
|
||||
var cmdRestore = &Command{
|
||||
Name: "restore",
|
||||
Short: "check out listed dependency versions in GOPATH",
|
||||
Long: `
|
||||
Restore checks out the Godeps-specified version of each package in GOPATH.
|
||||
|
||||
NOTE: restore leaves git repositories in a detached state. go1.6+ no longer
|
||||
checks out the master branch when doing a "go get", see:
|
||||
https://github.com/golang/go/commit/42206598671a44111c8f726ad33dc7b265bdf669.
|
||||
|
||||
`,
|
||||
Run: runRestore,
|
||||
OnlyInGOPATH: true,
|
||||
}
|
||||
|
||||
// Three phases:
|
||||
// 1. Download all deps
|
||||
// 2. Restore all deps (checkout the recorded rev)
|
||||
// 3. Attempt to load all deps as a simple consistency check
|
||||
func runRestore(cmd *Command, args []string) {
|
||||
if len(build.Default.GOPATH) == 0 {
|
||||
log.Println("Error restore requires GOPATH but it is empty.")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var hadError bool
|
||||
checkErr := func(s string) {
|
||||
if hadError {
|
||||
log.Println(s)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
g, err := loadDefaultGodepsFile()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
for i, dep := range g.Deps {
|
||||
verboseln("Downloading dependency (if needed):", dep.ImportPath)
|
||||
err := download(&dep)
|
||||
if err != nil {
|
||||
log.Printf("error downloading dep (%s): %s\n", dep.ImportPath, err)
|
||||
hadError = true
|
||||
}
|
||||
g.Deps[i] = dep
|
||||
}
|
||||
checkErr("Error downloading some deps. Aborting restore and check.")
|
||||
for _, dep := range g.Deps {
|
||||
verboseln("Restoring dependency (if needed):", dep.ImportPath)
|
||||
err := restore(dep)
|
||||
if err != nil {
|
||||
log.Printf("error restoring dep (%s): %s\n", dep.ImportPath, err)
|
||||
hadError = true
|
||||
}
|
||||
}
|
||||
checkErr("Error restoring some deps. Aborting check.")
|
||||
for _, dep := range g.Deps {
|
||||
verboseln("Checking dependency:", dep.ImportPath)
|
||||
_, err := LoadPackages(dep.ImportPath)
|
||||
if err != nil {
|
||||
log.Printf("Dep (%s) restored, but was unable to load it with error:\n\t%s\n", dep.ImportPath, err)
|
||||
if me, ok := err.(errorMissingDep); ok {
|
||||
log.Println("\tThis may be because the dependencies were saved with an older version of godep (< v33).")
|
||||
log.Printf("\tTry `go get %s`. Then `godep save` to update deps.\n", me.i)
|
||||
}
|
||||
hadError = true
|
||||
}
|
||||
}
|
||||
checkErr("Error checking some deps.")
|
||||
}
|
||||
|
||||
var downloaded = make(map[string]bool)
|
||||
|
||||
// download the given dependency.
|
||||
// 2 Passes: 1) go get -d <pkg>, 2) git pull (if necessary)
|
||||
func download(dep *Dependency) error {
|
||||
|
||||
rr, err := vcs.RepoRootForImportPath(dep.ImportPath, debug)
|
||||
if err != nil {
|
||||
debugln("Error determining repo root for", dep.ImportPath)
|
||||
return err
|
||||
}
|
||||
ppln("rr", rr)
|
||||
|
||||
dep.vcs = cmd[rr.VCS]
|
||||
|
||||
// try to find an existing directory in the GOPATHs
|
||||
for _, gp := range filepath.SplitList(build.Default.GOPATH) {
|
||||
t := filepath.Join(gp, "src", rr.Root)
|
||||
fi, err := os.Stat(t)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if fi.IsDir() {
|
||||
dep.root = t
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If none found, just pick the first GOPATH entry (AFAICT that's what go get does)
|
||||
if dep.root == "" {
|
||||
dep.root = filepath.Join(filepath.SplitList(build.Default.GOPATH)[0], "src", rr.Root)
|
||||
}
|
||||
ppln("dep", dep)
|
||||
|
||||
if downloaded[rr.Repo] {
|
||||
verboseln("Skipping already downloaded repo", rr.Repo)
|
||||
return nil
|
||||
}
|
||||
|
||||
fi, err := os.Stat(dep.root)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if err := os.MkdirAll(filepath.Dir(dep.root), os.ModePerm); err != nil {
|
||||
debugln("Error creating base dir of", dep.root)
|
||||
return err
|
||||
}
|
||||
err := rr.VCS.CreateAtRev(dep.root, rr.Repo, dep.Rev)
|
||||
debugln("CreatedAtRev", dep.root, rr.Repo, dep.Rev)
|
||||
if err != nil {
|
||||
debugln("CreateAtRev error", err)
|
||||
return err
|
||||
}
|
||||
downloaded[rr.Repo] = true
|
||||
return nil
|
||||
}
|
||||
debugln("Error checking repo root for", dep.ImportPath, "at", dep.root, ":", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if !fi.IsDir() {
|
||||
return errors.New("repo root src dir exists, but isn't a directory for " + dep.ImportPath + " at " + dep.root)
|
||||
}
|
||||
|
||||
if !dep.vcs.exists(dep.root, dep.Rev) {
|
||||
debugln("Updating existing", dep.root)
|
||||
if dep.vcs == vcsGit {
|
||||
detached, err := gitDetached(dep.root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if detached {
|
||||
db, err := gitDefaultBranch(dep.root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gitCheckout(dep.root, db); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dep.vcs.vcs.Download(dep.root)
|
||||
downloaded[rr.Repo] = true
|
||||
}
|
||||
|
||||
debugln("Nothing to download")
|
||||
return nil
|
||||
}
|
||||
|
||||
var restored = make(map[string]string) // dep.root -> dep.Rev
|
||||
|
||||
// restore checks out the given revision.
|
||||
func restore(dep Dependency) error {
|
||||
rev, ok := restored[dep.root]
|
||||
debugln(rev)
|
||||
debugln(ok)
|
||||
debugln(dep.root)
|
||||
if ok {
|
||||
if rev != dep.Rev {
|
||||
return errors.New("Wanted to restore rev " + dep.Rev + ", already restored rev " + rev + " for another package in the repo")
|
||||
}
|
||||
verboseln("Skipping already restored repo")
|
||||
return nil
|
||||
}
|
||||
|
||||
debugln("Restoring:", dep.ImportPath, dep.Rev)
|
||||
err := dep.vcs.RevSync(dep.root, dep.Rev)
|
||||
if err == nil {
|
||||
restored[dep.root] = dep.Rev
|
||||
}
|
||||
return err
|
||||
}
|
|
@ -1,171 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
|
||||
"github.com/kr/fs"
|
||||
)
|
||||
|
||||
// rewrite visits the go files in pkgs, plus all go files
|
||||
// in the directory tree Godeps, rewriting import statements
|
||||
// according to the rules for func qualify.
|
||||
func rewrite(pkgs []*Package, qual string, paths []string) error {
|
||||
for _, path := range pkgFiles(pkgs) {
|
||||
debugln("rewrite", path)
|
||||
err := rewriteTree(path, qual, paths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return rewriteTree("Godeps", qual, paths)
|
||||
}
|
||||
|
||||
// pkgFiles returns the full filesystem path to all go files in pkgs.
|
||||
func pkgFiles(pkgs []*Package) []string {
|
||||
var a []string
|
||||
for _, pkg := range pkgs {
|
||||
for _, s := range pkg.allGoFiles() {
|
||||
a = append(a, filepath.Join(pkg.Dir, s))
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// rewriteTree recursively visits the go files in path, rewriting
|
||||
// import statements according to the rules for func qualify.
|
||||
// This function ignores the 'testdata' directory.
|
||||
func rewriteTree(path, qual string, paths []string) error {
|
||||
w := fs.Walk(path)
|
||||
for w.Step() {
|
||||
if w.Err() != nil {
|
||||
log.Println("rewrite:", w.Err())
|
||||
continue
|
||||
}
|
||||
s := w.Stat()
|
||||
if s.IsDir() && s.Name() == "testdata" {
|
||||
w.SkipDir()
|
||||
continue
|
||||
}
|
||||
if strings.HasSuffix(w.Path(), ".go") {
|
||||
err := rewriteGoFile(w.Path(), qual, paths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// rewriteGoFile rewrites import statements in the named file
|
||||
// according to the rules for func qualify.
|
||||
func rewriteGoFile(name, qual string, paths []string) error {
|
||||
debugln("rewriteGoFile", name, ",", qual, ",", paths)
|
||||
printerConfig := &printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8}
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, name, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var changed bool
|
||||
for _, s := range f.Imports {
|
||||
name, err := strconv.Unquote(s.Path.Value)
|
||||
if err != nil {
|
||||
return err // can't happen
|
||||
}
|
||||
q := qualify(unqualify(name), qual, paths)
|
||||
if q != name {
|
||||
s.Path.Value = strconv.Quote(q)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if !changed {
|
||||
return nil
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
if err = printerConfig.Fprint(&buffer, fset, f); err != nil {
|
||||
return err
|
||||
}
|
||||
fset = token.NewFileSet()
|
||||
f, err = parser.ParseFile(fset, name, &buffer, parser.ParseComments)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ast.SortImports(fset, f)
|
||||
tpath := name + ".temp"
|
||||
t, err := os.Create(tpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = printerConfig.Fprint(t, fset, f); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = t.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
// This is required before the rename on windows.
|
||||
if err = os.Remove(name); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Rename(tpath, name)
|
||||
}
|
||||
|
||||
func defaultSep(experiment bool) string {
|
||||
if experiment {
|
||||
return "/vendor/"
|
||||
}
|
||||
return "/Godeps/_workspace/src/"
|
||||
}
|
||||
|
||||
func relativeVendorTarget(experiment bool) string {
|
||||
full := defaultSep(experiment)
|
||||
if full[0] == '/' {
|
||||
full = full[1:]
|
||||
}
|
||||
return filepath.FromSlash(full)
|
||||
}
|
||||
|
||||
// unqualify returns the part of importPath after the last
|
||||
// occurrence of the signature path elements
|
||||
// (Godeps/_workspace/src) that always precede imported
|
||||
// packages in rewritten import paths.
|
||||
//
|
||||
// For example,
|
||||
// unqualify(C) = C
|
||||
// unqualify(D/Godeps/_workspace/src/C) = C
|
||||
func unqualify(importPath string) string {
|
||||
if i := strings.LastIndex(importPath, sep); i != -1 {
|
||||
importPath = importPath[i+len(sep):]
|
||||
}
|
||||
return importPath
|
||||
}
|
||||
|
||||
// qualify qualifies importPath with its corresponding import
|
||||
// path in the Godeps src copy of package pkg. If importPath
|
||||
// is a directory lexically contained in a path in paths,
|
||||
// it will be qualified with package pkg; otherwise, it will
|
||||
// be returned unchanged.
|
||||
//
|
||||
// For example, given paths {D, T} and pkg C,
|
||||
// importPath returns
|
||||
// C C
|
||||
// fmt fmt
|
||||
// D C/Godeps/_workspace/src/D
|
||||
// D/P C/Godeps/_workspace/src/D/P
|
||||
// T C/Godeps/_workspace/src/T
|
||||
func qualify(importPath, pkg string, paths []string) string {
|
||||
if containsPathPrefix(paths, importPath) {
|
||||
return pkg + sep + importPath
|
||||
}
|
||||
return importPath
|
||||
}
|
|
@ -1,613 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/kr/fs"
|
||||
)
|
||||
|
||||
var cmdSave = &Command{
|
||||
Name: "save",
|
||||
Args: "[-r] [-t] [packages]",
|
||||
Short: "list and copy dependencies into Godeps",
|
||||
Long: `
|
||||
|
||||
Save writes a list of the named packages and their dependencies along
|
||||
with the exact source control revision of each package, and copies
|
||||
their source code into a subdirectory. Packages inside "." are excluded
|
||||
from the list to be copied.
|
||||
|
||||
The list is written to Godeps/Godeps.json, and source code for all
|
||||
dependencies is copied into either Godeps/_workspace or, if the vendor
|
||||
experiment is turned on, vendor/.
|
||||
|
||||
The dependency list is a JSON document with the following structure:
|
||||
|
||||
type Godeps struct {
|
||||
ImportPath string
|
||||
GoVersion string // Abridged output of 'go version'.
|
||||
Packages []string // Arguments to godep save, if any.
|
||||
Deps []struct {
|
||||
ImportPath string
|
||||
Comment string // Tag or description of commit.
|
||||
Rev string // VCS-specific commit ID.
|
||||
}
|
||||
}
|
||||
|
||||
Any packages already present in the list will be left unchanged.
|
||||
To update a dependency to a newer revision, use 'godep update'.
|
||||
|
||||
If -r is given, import statements will be rewritten to refer directly
|
||||
to the copied source code. This is not compatible with the vendor
|
||||
experiment. Note that this will not rewrite the statements in the
|
||||
files outside the project.
|
||||
|
||||
If -t is given, test files (*_test.go files + testdata directories) are
|
||||
also saved.
|
||||
|
||||
For more about specifying packages, see 'go help packages'.
|
||||
`,
|
||||
Run: runSave,
|
||||
OnlyInGOPATH: true,
|
||||
}
|
||||
|
||||
var (
|
||||
saveR, saveT bool
|
||||
ignorePackages string
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmdSave.Flag.BoolVar(&saveR, "r", false, "rewrite import paths")
|
||||
cmdSave.Flag.BoolVar(&saveT, "t", false, "save test files")
|
||||
cmdSave.Flag.StringVar(&ignorePackages, "i", "", "list of packages to ignore separated by commas")
|
||||
}
|
||||
|
||||
func runSave(cmd *Command, args []string) {
|
||||
if VendorExperiment && saveR {
|
||||
log.Println("flag -r is incompatible with the vendoring experiment")
|
||||
cmd.UsageExit()
|
||||
}
|
||||
err := save(args)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
|
||||
func dotPackage() (*build.Package, error) {
|
||||
dir, err := filepath.Abs(".")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return build.ImportDir(dir, build.FindOnly)
|
||||
}
|
||||
|
||||
func projectPackages(dDir string, a []*Package) []*Package {
|
||||
var projPkgs []*Package
|
||||
dotDir := fmt.Sprintf("%s%c", dDir, filepath.Separator)
|
||||
for _, p := range a {
|
||||
pkgDir := fmt.Sprintf("%s%c", p.Dir, filepath.Separator)
|
||||
if strings.HasPrefix(pkgDir, dotDir) {
|
||||
projPkgs = append(projPkgs, p)
|
||||
}
|
||||
}
|
||||
return projPkgs
|
||||
}
|
||||
|
||||
func save(pkgs []string) error {
|
||||
var err error
|
||||
dp, err := dotPackage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
debugln("dotPackageImportPath:", dp.ImportPath)
|
||||
debugln("dotPackageDir:", dp.Dir)
|
||||
|
||||
cv, err := goVersion()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
verboseln("Go Version:", cv)
|
||||
|
||||
gold, err := loadDefaultGodepsFile()
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
verboseln("No old Godeps.json found.")
|
||||
gold.GoVersion = cv
|
||||
}
|
||||
|
||||
printVersionWarnings(gold.GoVersion)
|
||||
if len(gold.GoVersion) == 0 {
|
||||
gold.GoVersion = majorGoVersion
|
||||
} else {
|
||||
majorGoVersion, err = trimGoVersion(gold.GoVersion)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to determine go major version from value specified in %s: %s\n", gold.file(), gold.GoVersion)
|
||||
}
|
||||
}
|
||||
|
||||
gnew := &Godeps{
|
||||
ImportPath: dp.ImportPath,
|
||||
GoVersion: gold.GoVersion,
|
||||
}
|
||||
|
||||
switch len(pkgs) {
|
||||
case 0:
|
||||
pkgs = []string{"."}
|
||||
default:
|
||||
gnew.Packages = pkgs
|
||||
}
|
||||
|
||||
verboseln("Finding dependencies for", pkgs)
|
||||
a, err := LoadPackages(pkgs...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, p := range a {
|
||||
verboseln("Found package:", p.ImportPath)
|
||||
verboseln("\tDeps:", strings.Join(p.Deps, " "))
|
||||
}
|
||||
ppln(a)
|
||||
|
||||
projA := projectPackages(dp.Dir, a)
|
||||
debugln("Filtered projectPackages")
|
||||
ppln(projA)
|
||||
|
||||
verboseln("Computing new Godeps.json file")
|
||||
err = gnew.fill(a, dp.ImportPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
debugln("New Godeps Filled")
|
||||
ppln(gnew)
|
||||
|
||||
if gnew.Deps == nil {
|
||||
gnew.Deps = make([]Dependency, 0) // produce json [], not null
|
||||
}
|
||||
gdisk := gnew.copy()
|
||||
err = carryVersions(&gold, gnew)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if gold.isOldFile {
|
||||
// If we are migrating from an old format file,
|
||||
// we require that the listed version of every
|
||||
// dependency must be installed in GOPATH, so it's
|
||||
// available to copy.
|
||||
if !eqDeps(gnew.Deps, gdisk.Deps) {
|
||||
return errors.New(strings.TrimSpace(needRestore))
|
||||
}
|
||||
gold = Godeps{}
|
||||
}
|
||||
os.Remove("Godeps") // remove regular file if present; ignore error
|
||||
readme := filepath.Join("Godeps", "Readme")
|
||||
err = writeFile(readme, strings.TrimSpace(Readme)+"\n")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
_, err = gnew.save()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
verboseln("Computing diff between old and new deps")
|
||||
// We use a name starting with "_" so the go tool
|
||||
// ignores this directory when traversing packages
|
||||
// starting at the project's root. For example,
|
||||
// godep go list ./...
|
||||
srcdir := filepath.FromSlash(strings.Trim(sep, "/"))
|
||||
rem := subDeps(gold.Deps, gnew.Deps)
|
||||
ppln(rem)
|
||||
add := subDeps(gnew.Deps, gold.Deps)
|
||||
ppln(add)
|
||||
if len(rem) > 0 {
|
||||
verboseln("Deps to remove:")
|
||||
for _, r := range rem {
|
||||
verboseln("\t", r.ImportPath)
|
||||
}
|
||||
verboseln("Removing unused dependencies")
|
||||
err = removeSrc(srcdir, rem)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(add) > 0 {
|
||||
verboseln("Deps to add:")
|
||||
for _, a := range add {
|
||||
verboseln("\t", a.ImportPath)
|
||||
}
|
||||
verboseln("Adding new dependencies")
|
||||
err = copySrc(srcdir, add)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !VendorExperiment {
|
||||
f, _ := filepath.Split(srcdir)
|
||||
writeVCSIgnore(f)
|
||||
}
|
||||
var rewritePaths []string
|
||||
if saveR {
|
||||
for _, dep := range gnew.Deps {
|
||||
rewritePaths = append(rewritePaths, dep.ImportPath)
|
||||
}
|
||||
}
|
||||
verboseln("Rewriting paths (if necessary)")
|
||||
ppln(rewritePaths)
|
||||
return rewrite(projA, dp.ImportPath, rewritePaths)
|
||||
}
|
||||
|
||||
func printVersionWarnings(ov string) {
|
||||
var warning bool
|
||||
cv, err := goVersion()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Trim the old version because we may have saved it w/o trimming it
|
||||
// cv is already trimmed by goVersion()
|
||||
tov, err := trimGoVersion(ov)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if tov != ov {
|
||||
log.Printf("WARNING: Recorded go version (%s) with minor version string found.\n", ov)
|
||||
warning = true
|
||||
}
|
||||
if cv != tov {
|
||||
log.Printf("WARNING: Recorded major go version (%s) and in-use major go version (%s) differ.\n", tov, cv)
|
||||
warning = true
|
||||
}
|
||||
if warning {
|
||||
log.Println("To record current major go version run `godep update -goversion`.")
|
||||
}
|
||||
}
|
||||
|
||||
type revError struct {
|
||||
ImportPath string
|
||||
WantRev string
|
||||
HavePath string
|
||||
HaveRev string
|
||||
}
|
||||
|
||||
func (v *revError) Error() string {
|
||||
return fmt.Sprintf("cannot save %s at revision %s: already have %s at revision %s.\n"+
|
||||
"Run `godep update %s' first.", v.ImportPath, v.WantRev, v.HavePath, v.HaveRev, v.HavePath)
|
||||
}
|
||||
|
||||
// carryVersions copies Rev and Comment from a to b for
|
||||
// each dependency with an identical ImportPath. For any
|
||||
// dependency in b that appears to be from the same repo
|
||||
// as one in a (for example, a parent or child directory),
|
||||
// the Rev must already match - otherwise it is an error.
|
||||
func carryVersions(a, b *Godeps) error {
|
||||
for i := range b.Deps {
|
||||
err := carryVersion(a, &b.Deps[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func carryVersion(a *Godeps, db *Dependency) error {
|
||||
// First see if this exact package is already in the list.
|
||||
for _, da := range a.Deps {
|
||||
if db.ImportPath == da.ImportPath {
|
||||
db.Rev = da.Rev
|
||||
db.Comment = da.Comment
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// No exact match, check for child or sibling package.
|
||||
// We can't handle mismatched versions for packages in
|
||||
// the same repo, so report that as an error.
|
||||
for _, da := range a.Deps {
|
||||
if strings.HasPrefix(db.ImportPath, da.ImportPath+"/") ||
|
||||
strings.HasPrefix(da.ImportPath, db.root+"/") {
|
||||
if da.Rev != db.Rev {
|
||||
return &revError{
|
||||
ImportPath: db.ImportPath,
|
||||
WantRev: db.Rev,
|
||||
HavePath: da.ImportPath,
|
||||
HaveRev: da.Rev,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// No related package in the list, must be a new repo.
|
||||
return nil
|
||||
}
|
||||
|
||||
// subDeps returns a - b, using ImportPath for equality.
|
||||
func subDeps(a, b []Dependency) (diff []Dependency) {
|
||||
Diff:
|
||||
for _, da := range a {
|
||||
for _, db := range b {
|
||||
if da.ImportPath == db.ImportPath {
|
||||
continue Diff
|
||||
}
|
||||
}
|
||||
diff = append(diff, da)
|
||||
}
|
||||
return diff
|
||||
}
|
||||
|
||||
func removeSrc(srcdir string, deps []Dependency) error {
|
||||
for _, dep := range deps {
|
||||
path := filepath.FromSlash(dep.ImportPath)
|
||||
err := os.RemoveAll(filepath.Join(srcdir, path))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func copySrc(dir string, deps []Dependency) error {
|
||||
// mapping to see if we visited a parent directory already
|
||||
visited := make(map[string]bool)
|
||||
ok := true
|
||||
for _, dep := range deps {
|
||||
debugln("copySrc for", dep.ImportPath)
|
||||
srcdir := filepath.Join(dep.ws, "src")
|
||||
rel, err := filepath.Rel(srcdir, dep.dir)
|
||||
debugln("srcdir", srcdir)
|
||||
debugln("rel", rel)
|
||||
debugln("err", err)
|
||||
if err != nil { // this should never happen
|
||||
return err
|
||||
}
|
||||
dstpkgroot := filepath.Join(dir, rel)
|
||||
err = os.RemoveAll(dstpkgroot)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
ok = false
|
||||
}
|
||||
|
||||
// copy actual dependency
|
||||
vf := dep.vcs.listFiles(dep.dir)
|
||||
debugln("vf", vf)
|
||||
w := fs.Walk(dep.dir)
|
||||
for w.Step() {
|
||||
err = copyPkgFile(vf, dir, srcdir, w)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
|
||||
// Look for legal files in root
|
||||
// some packages are imports as a sub-package but license info
|
||||
// is at root: exampleorg/common has license file in exampleorg
|
||||
//
|
||||
if dep.ImportPath == dep.root {
|
||||
// we are already at root
|
||||
continue
|
||||
}
|
||||
|
||||
// prevent copying twice This could happen if we have
|
||||
// two subpackages listed someorg/common and
|
||||
// someorg/anotherpack which has their license in
|
||||
// the parent dir of someorg
|
||||
rootdir := filepath.Join(srcdir, filepath.FromSlash(dep.root))
|
||||
if visited[rootdir] {
|
||||
continue
|
||||
}
|
||||
visited[rootdir] = true
|
||||
vf = dep.vcs.listFiles(rootdir)
|
||||
w = fs.Walk(rootdir)
|
||||
for w.Step() {
|
||||
fname := filepath.Base(w.Path())
|
||||
if IsLegalFile(fname) && !strings.Contains(w.Path(), sep) {
|
||||
err = copyPkgFile(vf, dir, srcdir, w)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return errorCopyingSourceCode
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyPkgFile(vf vcsFiles, dstroot, srcroot string, w *fs.Walker) error {
|
||||
if w.Err() != nil {
|
||||
return w.Err()
|
||||
}
|
||||
name := w.Stat().Name()
|
||||
if w.Stat().IsDir() {
|
||||
if name[0] == '.' || name[0] == '_' || (!saveT && name == "testdata") {
|
||||
// Skip directories starting with '.' or '_' or
|
||||
// 'testdata' (last is only skipped if saveT is false)
|
||||
w.SkipDir()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
rel, err := filepath.Rel(srcroot, w.Path())
|
||||
if err != nil { // this should never happen
|
||||
return err
|
||||
}
|
||||
if !saveT && strings.HasSuffix(name, "_test.go") {
|
||||
if verbose {
|
||||
log.Printf("save: skipping test file: %s", w.Path())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if !vf.Contains(w.Path()) {
|
||||
if verbose {
|
||||
log.Printf("save: skipping untracked file: %s", w.Path())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return copyFile(filepath.Join(dstroot, rel), w.Path())
|
||||
}
|
||||
|
||||
// copyFile copies a regular file from src to dst.
|
||||
// dst is opened with os.Create.
|
||||
// If the file name ends with .go,
|
||||
// copyFile strips canonical import path annotations.
|
||||
// These are comments of the form:
|
||||
// package foo // import "bar/foo"
|
||||
// package foo /* import "bar/foo" */
|
||||
func copyFile(dst, src string) error {
|
||||
err := os.MkdirAll(filepath.Dir(dst), 0777)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
linkDst, err := os.Readlink(src)
|
||||
if err == nil {
|
||||
return os.Symlink(linkDst, dst)
|
||||
}
|
||||
|
||||
si, err := stat(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
w, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Chmod(dst, si.Mode()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if strings.HasSuffix(dst, ".go") {
|
||||
debugln("Copy Without Import Comment", w, r)
|
||||
err = copyWithoutImportComment(w, r)
|
||||
} else {
|
||||
debugln("Copy (plain)", w, r)
|
||||
_, err = io.Copy(w, r)
|
||||
}
|
||||
err1 := w.Close()
|
||||
if err == nil {
|
||||
err = err1
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func copyWithoutImportComment(w io.Writer, r io.Reader) error {
|
||||
b := bufio.NewReader(r)
|
||||
for {
|
||||
l, err := b.ReadBytes('\n')
|
||||
eof := err == io.EOF
|
||||
if err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
// If we have data then write it out...
|
||||
if len(l) > 0 {
|
||||
// Strip off \n if it exists because stripImportComment
|
||||
_, err := w.Write(append(stripImportComment(bytes.TrimRight(l, "\n")), '\n'))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if eof {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
importAnnotation = `import\s+(?:"[^"]*"|` + "`[^`]*`" + `)`
|
||||
importComment = `(?://\s*` + importAnnotation + `\s*$|/\*\s*` + importAnnotation + `\s*\*/)`
|
||||
)
|
||||
|
||||
var (
|
||||
importCommentRE = regexp.MustCompile(`^\s*(package\s+\w+)\s+` + importComment + `(.*)`)
|
||||
pkgPrefix = []byte("package ")
|
||||
)
|
||||
|
||||
// stripImportComment returns line with its import comment removed.
|
||||
// If s is not a package statement containing an import comment,
|
||||
// it is returned unaltered.
|
||||
// FIXME: expects lines w/o a \n at the end
|
||||
// See also http://golang.org/s/go14customimport.
|
||||
func stripImportComment(line []byte) []byte {
|
||||
if !bytes.HasPrefix(line, pkgPrefix) {
|
||||
// Fast path; this will skip all but one line in the file.
|
||||
// This assumes there is no whitespace before the keyword.
|
||||
return line
|
||||
}
|
||||
if m := importCommentRE.FindSubmatch(line); m != nil {
|
||||
return append(m[1], m[2]...)
|
||||
}
|
||||
return line
|
||||
}
|
||||
|
||||
// Func writeVCSIgnore writes "ignore" files inside dir for known VCSs,
|
||||
// so that dir/pkg and dir/bin don't accidentally get committed.
|
||||
// It logs any errors it encounters.
|
||||
func writeVCSIgnore(dir string) {
|
||||
// Currently git is the only VCS for which we know how to do this.
|
||||
// Mercurial and Bazaar have similar mechanisms, but they apparently
|
||||
// require writing files outside of dir.
|
||||
const ignore = "/pkg\n/bin\n"
|
||||
name := filepath.Join(dir, ".gitignore")
|
||||
err := writeFile(name, ignore)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
// writeFile is like ioutil.WriteFile but it creates
|
||||
// intermediate directories with os.MkdirAll.
|
||||
func writeFile(name, body string) error {
|
||||
err := os.MkdirAll(filepath.Dir(name), 0777)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(name, []byte(body), 0666)
|
||||
}
|
||||
|
||||
const (
|
||||
// Readme contains the README text.
|
||||
Readme = `
|
||||
This directory tree is generated automatically by godep.
|
||||
|
||||
Please do not edit.
|
||||
|
||||
See https://github.com/tools/godep for more information.
|
||||
`
|
||||
needRestore = `
|
||||
mismatched versions while migrating
|
||||
|
||||
It looks like you are switching from the old Godeps format
|
||||
(from flag -copy=false). The old format is just a file; it
|
||||
doesn't contain source code. For this migration, godep needs
|
||||
the appropriate version of each dependency to be installed in
|
||||
GOPATH, so that the source code is available to copy.
|
||||
|
||||
To fix this, run 'godep restore'.
|
||||
`
|
||||
)
|
|
@ -1,292 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var cmdUpdate = &Command{
|
||||
Name: "update",
|
||||
Args: "[-goversion] [packages]",
|
||||
Short: "update selected packages or the go version",
|
||||
Long: `
|
||||
Update changes the named dependency packages to use the
|
||||
revision of each currently installed in GOPATH. New code will
|
||||
be copied into the Godeps workspace or vendor folder and the
|
||||
new revision will be written to the manifest.
|
||||
|
||||
If -goversion is specified, update the recorded go version.
|
||||
|
||||
For more about specifying packages, see 'go help packages'.
|
||||
`,
|
||||
Run: runUpdate,
|
||||
OnlyInGOPATH: true,
|
||||
}
|
||||
|
||||
var (
|
||||
updateGoVer bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmdUpdate.Flag.BoolVar(&saveT, "t", false, "save test files during update")
|
||||
cmdUpdate.Flag.BoolVar(&updateGoVer, "goversion", false, "update the recorded go version")
|
||||
}
|
||||
|
||||
func runUpdate(cmd *Command, args []string) {
|
||||
if updateGoVer {
|
||||
err := updateGoVersion()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
if len(args) > 0 {
|
||||
err := update(args)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateGoVersion() error {
|
||||
gold, err := loadDefaultGodepsFile()
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
cv, err := goVersion()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gv := gold.GoVersion
|
||||
gold.GoVersion = cv
|
||||
_, err = gold.save()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if gv != cv {
|
||||
log.Println("Updated major go version to", cv)
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func update(args []string) error {
|
||||
if len(args) == 0 {
|
||||
args = []string{"."}
|
||||
}
|
||||
g, err := loadDefaultGodepsFile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, arg := range args {
|
||||
arg := path.Clean(arg)
|
||||
any := markMatches(arg, g.Deps)
|
||||
if !any {
|
||||
log.Println("not in manifest:", arg)
|
||||
}
|
||||
}
|
||||
deps, rdeps, err := LoadVCSAndUpdate(g.Deps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(deps) == 0 {
|
||||
return errorNoPackagesUpdatable
|
||||
}
|
||||
g.addOrUpdateDeps(deps)
|
||||
g.removeDeps(rdeps)
|
||||
if _, err = g.save(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
srcdir := relativeVendorTarget(VendorExperiment)
|
||||
if err := removeSrc(filepath.FromSlash(strings.Trim(sep, "/")), rdeps); err != nil {
|
||||
return err
|
||||
}
|
||||
copySrc(srcdir, deps)
|
||||
|
||||
ok, err := needRewrite(g.Packages)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var rewritePaths []string
|
||||
if ok {
|
||||
for _, dep := range g.Deps {
|
||||
rewritePaths = append(rewritePaths, dep.ImportPath)
|
||||
}
|
||||
}
|
||||
return rewrite(nil, g.ImportPath, rewritePaths)
|
||||
}
|
||||
|
||||
func needRewrite(importPaths []string) (bool, error) {
|
||||
if len(importPaths) == 0 {
|
||||
importPaths = []string{"."}
|
||||
}
|
||||
a, err := LoadPackages(importPaths...)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, p := range a {
|
||||
for _, name := range p.allGoFiles() {
|
||||
path := filepath.Join(p.Dir, name)
|
||||
hasSep, err := hasRewrittenImportStatement(path)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if hasSep {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func hasRewrittenImportStatement(path string) (bool, error) {
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, path, nil, 0)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, s := range f.Imports {
|
||||
name, _ := strconv.Unquote(s.Path.Value)
|
||||
if strings.Contains(name, sep) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// markMatches marks each entry in deps with an import path that
|
||||
// matches pat. It returns whether any matches occurred.
|
||||
func markMatches(pat string, deps []Dependency) (matched bool) {
|
||||
f := matchPattern(pat)
|
||||
for i, dep := range deps {
|
||||
if f(dep.ImportPath) {
|
||||
deps[i].matched = true
|
||||
matched = true
|
||||
}
|
||||
}
|
||||
return matched
|
||||
}
|
||||
|
||||
func fillDeps(deps []Dependency) ([]Dependency, error) {
|
||||
for i := range deps {
|
||||
if deps[i].pkg != nil {
|
||||
continue
|
||||
}
|
||||
ps, err := LoadPackages(deps[i].ImportPath)
|
||||
if err != nil {
|
||||
if _, ok := err.(errPackageNotFound); ok {
|
||||
deps[i].missing = true
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if len(ps) > 1 {
|
||||
panic("More than one package found for " + deps[i].ImportPath)
|
||||
}
|
||||
p := ps[0]
|
||||
deps[i].pkg = p
|
||||
deps[i].dir = p.Dir
|
||||
deps[i].ws = p.Root
|
||||
|
||||
vcs, reporoot, err := VCSFromDir(p.Dir, filepath.Join(p.Root, "src"))
|
||||
if err != nil {
|
||||
return nil, errorLoadingDeps
|
||||
}
|
||||
deps[i].root = filepath.ToSlash(reporoot)
|
||||
deps[i].vcs = vcs
|
||||
}
|
||||
|
||||
return deps, nil
|
||||
}
|
||||
|
||||
// LoadVCSAndUpdate loads and updates a set of dependencies.
|
||||
func LoadVCSAndUpdate(deps []Dependency) ([]Dependency, []Dependency, error) {
|
||||
var err1 error
|
||||
|
||||
deps, err := fillDeps(deps)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
repoMask := make(map[string]bool)
|
||||
for i := range deps {
|
||||
if !deps[i].matched {
|
||||
repoMask[deps[i].root] = true
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if we need any new packages because of new transitive imports
|
||||
for _, dep := range deps {
|
||||
if !dep.matched || dep.missing {
|
||||
continue
|
||||
}
|
||||
for _, dp := range dep.pkg.Dependencies {
|
||||
if dp.Goroot {
|
||||
continue
|
||||
}
|
||||
var have bool
|
||||
for _, d := range deps {
|
||||
if d.ImportPath == dp.ImportPath {
|
||||
have = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !have {
|
||||
deps = append(deps, Dependency{ImportPath: dp.ImportPath, matched: true})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deps, err = fillDeps(deps)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var toUpdate, toRemove []Dependency
|
||||
for _, d := range deps {
|
||||
if !d.matched || repoMask[d.root] {
|
||||
continue
|
||||
}
|
||||
if d.missing {
|
||||
toRemove = append(toRemove, d)
|
||||
continue
|
||||
}
|
||||
toUpdate = append(toUpdate, d)
|
||||
}
|
||||
|
||||
debugln("toUpdate")
|
||||
ppln(toUpdate)
|
||||
|
||||
var toCopy []Dependency
|
||||
for _, d := range toUpdate {
|
||||
id, err := d.vcs.identify(d.dir)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
err1 = errorLoadingDeps
|
||||
continue
|
||||
}
|
||||
if d.vcs.isDirty(d.dir, id) {
|
||||
log.Println("dirty working tree (please commit changes):", d.dir)
|
||||
}
|
||||
d.Rev = id
|
||||
d.Comment = d.vcs.describe(d.dir, id)
|
||||
toCopy = append(toCopy, d)
|
||||
}
|
||||
debugln("toCopy")
|
||||
ppln(toCopy)
|
||||
|
||||
if err1 != nil {
|
||||
return nil, nil, err1
|
||||
}
|
||||
return toCopy, toRemove, nil
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// driveLetterToUpper converts Windows path's drive letters to uppercase. This
|
||||
// is needed when comparing 2 paths with different drive letter case.
|
||||
func driveLetterToUpper(path string) string {
|
||||
if runtime.GOOS != "windows" || path == "" {
|
||||
return path
|
||||
}
|
||||
|
||||
p := path
|
||||
|
||||
// If path's drive letter is lowercase, change it to uppercase.
|
||||
if len(p) >= 2 && p[1] == ':' && 'a' <= p[0] && p[0] <= 'z' {
|
||||
p = string(p[0]+'A'-'a') + p[1:]
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
// clean the path and ensure that a drive letter is upper case (if it exists).
|
||||
func cleanPath(path string) string {
|
||||
return driveLetterToUpper(filepath.Clean(path))
|
||||
}
|
||||
|
||||
// deal with case insensitive filesystems and other weirdness
|
||||
func pathEqual(a, b string) bool {
|
||||
a = cleanPath(a)
|
||||
b = cleanPath(b)
|
||||
return strings.EqualFold(a, b)
|
||||
}
|
|
@ -1,293 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/vcs"
|
||||
)
|
||||
|
||||
// VCS represents a version control system.
|
||||
type VCS struct {
|
||||
vcs *vcs.Cmd
|
||||
|
||||
IdentifyCmd string
|
||||
DescribeCmd string
|
||||
DiffCmd string
|
||||
ListCmd string
|
||||
RootCmd string
|
||||
|
||||
// run in sandbox repos
|
||||
ExistsCmd string
|
||||
}
|
||||
|
||||
var vcsBzr = &VCS{
|
||||
vcs: vcs.ByCmd("bzr"),
|
||||
|
||||
IdentifyCmd: "version-info --custom --template {revision_id}",
|
||||
DescribeCmd: "revno", // TODO(kr): find tag names if possible
|
||||
DiffCmd: "diff -r {rev}",
|
||||
ListCmd: "ls --from-root -R",
|
||||
RootCmd: "root",
|
||||
}
|
||||
|
||||
var vcsGit = &VCS{
|
||||
vcs: vcs.ByCmd("git"),
|
||||
|
||||
IdentifyCmd: "rev-parse HEAD",
|
||||
DescribeCmd: "describe --tags --abbrev=14",
|
||||
DiffCmd: "diff {rev}",
|
||||
ListCmd: "ls-files --full-name",
|
||||
RootCmd: "rev-parse --show-cdup",
|
||||
|
||||
ExistsCmd: "cat-file -e {rev}",
|
||||
}
|
||||
|
||||
var vcsHg = &VCS{
|
||||
vcs: vcs.ByCmd("hg"),
|
||||
|
||||
IdentifyCmd: "parents --template {node}",
|
||||
DescribeCmd: "log -r . --template {latesttag}-{latesttagdistance}",
|
||||
DiffCmd: "diff -r {rev}",
|
||||
ListCmd: "status --all --no-status",
|
||||
RootCmd: "root",
|
||||
|
||||
ExistsCmd: "cat -r {rev} .",
|
||||
}
|
||||
|
||||
var cmd = map[*vcs.Cmd]*VCS{
|
||||
vcsBzr.vcs: vcsBzr,
|
||||
vcsGit.vcs: vcsGit,
|
||||
vcsHg.vcs: vcsHg,
|
||||
}
|
||||
|
||||
// VCSFromDir returns a VCS value from a directory.
|
||||
func VCSFromDir(dir, srcRoot string) (*VCS, string, error) {
|
||||
vcscmd, reporoot, err := vcs.FromDir(dir, srcRoot)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("error while inspecting %q: %v", dir, err)
|
||||
}
|
||||
vcsext := cmd[vcscmd]
|
||||
if vcsext == nil {
|
||||
return nil, "", fmt.Errorf("%s is unsupported: %s", vcscmd.Name, dir)
|
||||
}
|
||||
return vcsext, reporoot, nil
|
||||
}
|
||||
|
||||
func (v *VCS) identify(dir string) (string, error) {
|
||||
out, err := v.runOutput(dir, v.IdentifyCmd)
|
||||
return string(bytes.TrimSpace(out)), err
|
||||
}
|
||||
|
||||
func absRoot(dir, out string) string {
|
||||
if filepath.IsAbs(out) {
|
||||
return filepath.Clean(out)
|
||||
}
|
||||
return filepath.Join(dir, out)
|
||||
}
|
||||
|
||||
func (v *VCS) root(dir string) (string, error) {
|
||||
out, err := v.runOutput(dir, v.RootCmd)
|
||||
return absRoot(dir, string(bytes.TrimSpace(out))), err
|
||||
}
|
||||
|
||||
func (v *VCS) describe(dir, rev string) string {
|
||||
out, err := v.runOutputVerboseOnly(dir, v.DescribeCmd, "rev", rev)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(bytes.TrimSpace(out))
|
||||
}
|
||||
|
||||
func (v *VCS) isDirty(dir, rev string) bool {
|
||||
out, err := v.runOutput(dir, v.DiffCmd, "rev", rev)
|
||||
return err != nil || len(out) != 0
|
||||
}
|
||||
|
||||
type vcsFiles map[string]bool
|
||||
|
||||
func (vf vcsFiles) Contains(path string) bool {
|
||||
// Fast path, we have the path
|
||||
if vf[path] {
|
||||
return true
|
||||
}
|
||||
|
||||
// Slow path for case insensitive filesystems
|
||||
// See #310
|
||||
for f := range vf {
|
||||
if pathEqual(f, path) {
|
||||
return true
|
||||
}
|
||||
// git's root command (maybe other vcs as well) resolve symlinks, so try that too
|
||||
// FIXME: rev-parse --show-cdup + extra logic will fix this for git but also need to validate the other vcs commands. This is maybe temporary.
|
||||
p, err := filepath.EvalSymlinks(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if pathEqual(f, p) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// No matches by either method
|
||||
return false
|
||||
}
|
||||
|
||||
// listFiles tracked by the VCS in the repo that contains dir, converted to absolute path.
|
||||
func (v *VCS) listFiles(dir string) vcsFiles {
|
||||
root, err := v.root(dir)
|
||||
debugln("vcs dir", dir)
|
||||
debugln("vcs root", root)
|
||||
ppln(v)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
out, err := v.runOutput(dir, v.ListCmd)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
files := make(vcsFiles)
|
||||
for _, file := range bytes.Split(out, []byte{'\n'}) {
|
||||
if len(file) > 0 {
|
||||
path, err := filepath.Abs(filepath.Join(root, string(file)))
|
||||
if err != nil {
|
||||
panic(err) // this should not happen
|
||||
}
|
||||
|
||||
if pathEqual(filepath.Dir(path), dir) {
|
||||
files[path] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
func (v *VCS) exists(dir, rev string) bool {
|
||||
err := v.runVerboseOnly(dir, v.ExistsCmd, "rev", rev)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// RevSync checks out the revision given by rev in dir.
|
||||
// The dir must exist and rev must be a valid revision.
|
||||
func (v *VCS) RevSync(dir, rev string) error {
|
||||
return v.run(dir, v.vcs.TagSyncCmd, "tag", rev)
|
||||
}
|
||||
|
||||
// run runs the command line cmd in the given directory.
|
||||
// keyval is a list of key, value pairs. run expands
|
||||
// instances of {key} in cmd into value, but only after
|
||||
// splitting cmd into individual arguments.
|
||||
// If an error occurs, run prints the command line and the
|
||||
// command's combined stdout+stderr to standard error.
|
||||
// Otherwise run discards the command's output.
|
||||
func (v *VCS) run(dir string, cmdline string, kv ...string) error {
|
||||
_, err := v.run1(dir, cmdline, kv, true)
|
||||
return err
|
||||
}
|
||||
|
||||
// runVerboseOnly is like run but only generates error output to standard error in verbose mode.
|
||||
func (v *VCS) runVerboseOnly(dir string, cmdline string, kv ...string) error {
|
||||
_, err := v.run1(dir, cmdline, kv, false)
|
||||
return err
|
||||
}
|
||||
|
||||
// runOutput is like run but returns the output of the command.
|
||||
func (v *VCS) runOutput(dir string, cmdline string, kv ...string) ([]byte, error) {
|
||||
return v.run1(dir, cmdline, kv, true)
|
||||
}
|
||||
|
||||
// runOutputVerboseOnly is like runOutput but only generates error output to standard error in verbose mode.
|
||||
func (v *VCS) runOutputVerboseOnly(dir string, cmdline string, kv ...string) ([]byte, error) {
|
||||
return v.run1(dir, cmdline, kv, false)
|
||||
}
|
||||
|
||||
// run1 is the generalized implementation of run and runOutput.
|
||||
func (v *VCS) run1(dir string, cmdline string, kv []string, verbose bool) ([]byte, error) {
|
||||
m := make(map[string]string)
|
||||
for i := 0; i < len(kv); i += 2 {
|
||||
m[kv[i]] = kv[i+1]
|
||||
}
|
||||
args := strings.Fields(cmdline)
|
||||
for i, arg := range args {
|
||||
args[i] = expand(m, arg)
|
||||
}
|
||||
|
||||
_, err := exec.LookPath(v.vcs.Cmd)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "godep: missing %s command.\n", v.vcs.Name)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd := exec.Command(v.vcs.Cmd, args...)
|
||||
cmd.Dir = dir
|
||||
var buf bytes.Buffer
|
||||
cmd.Stdout = &buf
|
||||
cmd.Stderr = &buf
|
||||
err = cmd.Run()
|
||||
out := buf.Bytes()
|
||||
if err != nil {
|
||||
if verbose {
|
||||
fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.vcs.Cmd, strings.Join(args, " "))
|
||||
os.Stderr.Write(out)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func expand(m map[string]string, s string) string {
|
||||
for k, v := range m {
|
||||
s = strings.Replace(s, "{"+k+"}", v, -1)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func gitDetached(r string) (bool, error) {
|
||||
o, err := vcsGit.runOutput(r, "status")
|
||||
if err != nil {
|
||||
return false, errors.New("unable to determine git status " + err.Error())
|
||||
}
|
||||
return bytes.Contains(o, []byte("HEAD detached at")), nil
|
||||
}
|
||||
|
||||
func gitDefaultBranch(r string) (string, error) {
|
||||
o, err := vcsGit.runOutput(r, "remote show origin")
|
||||
if err != nil {
|
||||
return "", errors.New("Running git remote show origin errored with: " + err.Error())
|
||||
}
|
||||
return gitDetermineDefaultBranch(r, string(o))
|
||||
}
|
||||
|
||||
func gitDetermineDefaultBranch(r, o string) (string, error) {
|
||||
e := "Unable to determine HEAD branch: "
|
||||
hb := "HEAD branch:"
|
||||
lbcfgp := "Local branch configured for 'git pull':"
|
||||
s := strings.Index(o, hb)
|
||||
if s < 0 {
|
||||
b := strings.Index(o, lbcfgp)
|
||||
if b < 0 {
|
||||
return "", errors.New(e + "Remote HEAD is ambiguous. Before godep can pull new commits you will need to:" + `
|
||||
cd ` + r + `
|
||||
git checkout <a HEAD branch>
|
||||
Here is what was reported:
|
||||
` + o)
|
||||
}
|
||||
s = b + len(lbcfgp)
|
||||
} else {
|
||||
s += len(hb)
|
||||
}
|
||||
f := strings.Fields(o[s:])
|
||||
if len(f) < 3 {
|
||||
return "", errors.New(e + "git output too short")
|
||||
}
|
||||
return f[0], nil
|
||||
}
|
||||
|
||||
func gitCheckout(r, b string) error {
|
||||
return vcsGit.run(r, "checkout "+b)
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const version = "80-k8s-r1"
|
||||
|
||||
var cmdVersion = &Command{
|
||||
Name: "version",
|
||||
Short: "show version info",
|
||||
Long: `
|
||||
|
||||
Displays the version of godep as well as the target OS, architecture and go runtime version.
|
||||
`,
|
||||
Run: runVersion,
|
||||
}
|
||||
|
||||
func versionString() string {
|
||||
return fmt.Sprintf("godep v%s (%s/%s/%s)", version, runtime.GOOS, runtime.GOARCH, runtime.Version())
|
||||
}
|
||||
|
||||
func runVersion(cmd *Command, args []string) {
|
||||
fmt.Printf("%s\n", versionString())
|
||||
}
|
||||
|
||||
func GoVersionFields(c rune) bool {
|
||||
return c == 'g' || c == 'o' || c == '.'
|
||||
}
|
||||
|
||||
// isSameOrNewer go version (goA.B)
|
||||
// go1.6 >= go1.6 == true
|
||||
// go1.5 >= go1.6 == false
|
||||
func isSameOrNewer(base, check string) bool {
|
||||
if base == check {
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(check, "devel-") {
|
||||
return true
|
||||
}
|
||||
bp := strings.FieldsFunc(base, GoVersionFields)
|
||||
cp := strings.FieldsFunc(check, GoVersionFields)
|
||||
if len(bp) < 2 || len(cp) < 2 {
|
||||
log.Fatalf("Error comparing %s to %s\n", base, check)
|
||||
}
|
||||
if bp[0] == cp[0] { // We only have go version 1 right now
|
||||
bm, err := strconv.Atoi(bp[1])
|
||||
// These errors are unlikely and there is nothing nice to do here anyway
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
cm, err := strconv.Atoi(cp[1])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return cm >= bm
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,22 +0,0 @@
|
|||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
|
@ -1,37 +0,0 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"deep_equal.go",
|
||||
"type.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/third_party/forked/golang/reflect",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["deep_equal_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
|
@ -1,388 +0,0 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package reflect is a fork of go's standard library reflection package, which
|
||||
// allows for deep equal with equality functions defined.
|
||||
package reflect
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Equalities is a map from type to a function comparing two values of
|
||||
// that type.
|
||||
type Equalities map[reflect.Type]reflect.Value
|
||||
|
||||
// For convenience, panics on errrors
|
||||
func EqualitiesOrDie(funcs ...interface{}) Equalities {
|
||||
e := Equalities{}
|
||||
if err := e.AddFuncs(funcs...); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// AddFuncs is a shortcut for multiple calls to AddFunc.
|
||||
func (e Equalities) AddFuncs(funcs ...interface{}) error {
|
||||
for _, f := range funcs {
|
||||
if err := e.AddFunc(f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddFunc uses func as an equality function: it must take
|
||||
// two parameters of the same type, and return a boolean.
|
||||
func (e Equalities) AddFunc(eqFunc interface{}) error {
|
||||
fv := reflect.ValueOf(eqFunc)
|
||||
ft := fv.Type()
|
||||
if ft.Kind() != reflect.Func {
|
||||
return fmt.Errorf("expected func, got: %v", ft)
|
||||
}
|
||||
if ft.NumIn() != 2 {
|
||||
return fmt.Errorf("expected two 'in' params, got: %v", ft)
|
||||
}
|
||||
if ft.NumOut() != 1 {
|
||||
return fmt.Errorf("expected one 'out' param, got: %v", ft)
|
||||
}
|
||||
if ft.In(0) != ft.In(1) {
|
||||
return fmt.Errorf("expected arg 1 and 2 to have same type, but got %v", ft)
|
||||
}
|
||||
var forReturnType bool
|
||||
boolType := reflect.TypeOf(forReturnType)
|
||||
if ft.Out(0) != boolType {
|
||||
return fmt.Errorf("expected bool return, got: %v", ft)
|
||||
}
|
||||
e[ft.In(0)] = fv
|
||||
return nil
|
||||
}
|
||||
|
||||
// Below here is forked from go's reflect/deepequal.go
|
||||
|
||||
// During deepValueEqual, must keep track of checks that are
|
||||
// in progress. The comparison algorithm assumes that all
|
||||
// checks in progress are true when it reencounters them.
|
||||
// Visited comparisons are stored in a map indexed by visit.
|
||||
type visit struct {
|
||||
a1 uintptr
|
||||
a2 uintptr
|
||||
typ reflect.Type
|
||||
}
|
||||
|
||||
// unexportedTypePanic is thrown when you use this DeepEqual on something that has an
|
||||
// unexported type. It indicates a programmer error, so should not occur at runtime,
|
||||
// which is why it's not public and thus impossible to catch.
|
||||
type unexportedTypePanic []reflect.Type
|
||||
|
||||
func (u unexportedTypePanic) Error() string { return u.String() }
|
||||
func (u unexportedTypePanic) String() string {
|
||||
strs := make([]string, len(u))
|
||||
for i, t := range u {
|
||||
strs[i] = fmt.Sprintf("%v", t)
|
||||
}
|
||||
return "an unexported field was encountered, nested like this: " + strings.Join(strs, " -> ")
|
||||
}
|
||||
|
||||
func makeUsefulPanic(v reflect.Value) {
|
||||
if x := recover(); x != nil {
|
||||
if u, ok := x.(unexportedTypePanic); ok {
|
||||
u = append(unexportedTypePanic{v.Type()}, u...)
|
||||
x = u
|
||||
}
|
||||
panic(x)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests for deep equality using reflected types. The map argument tracks
|
||||
// comparisons that have already been seen, which allows short circuiting on
|
||||
// recursive types.
|
||||
func (e Equalities) deepValueEqual(v1, v2 reflect.Value, visited map[visit]bool, depth int) bool {
|
||||
defer makeUsefulPanic(v1)
|
||||
|
||||
if !v1.IsValid() || !v2.IsValid() {
|
||||
return v1.IsValid() == v2.IsValid()
|
||||
}
|
||||
if v1.Type() != v2.Type() {
|
||||
return false
|
||||
}
|
||||
if fv, ok := e[v1.Type()]; ok {
|
||||
return fv.Call([]reflect.Value{v1, v2})[0].Bool()
|
||||
}
|
||||
|
||||
hard := func(k reflect.Kind) bool {
|
||||
switch k {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.Struct:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if v1.CanAddr() && v2.CanAddr() && hard(v1.Kind()) {
|
||||
addr1 := v1.UnsafeAddr()
|
||||
addr2 := v2.UnsafeAddr()
|
||||
if addr1 > addr2 {
|
||||
// Canonicalize order to reduce number of entries in visited.
|
||||
addr1, addr2 = addr2, addr1
|
||||
}
|
||||
|
||||
// Short circuit if references are identical ...
|
||||
if addr1 == addr2 {
|
||||
return true
|
||||
}
|
||||
|
||||
// ... or already seen
|
||||
typ := v1.Type()
|
||||
v := visit{addr1, addr2, typ}
|
||||
if visited[v] {
|
||||
return true
|
||||
}
|
||||
|
||||
// Remember for later.
|
||||
visited[v] = true
|
||||
}
|
||||
|
||||
switch v1.Kind() {
|
||||
case reflect.Array:
|
||||
// We don't need to check length here because length is part of
|
||||
// an array's type, which has already been filtered for.
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
if !e.deepValueEqual(v1.Index(i), v2.Index(i), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Slice:
|
||||
if (v1.IsNil() || v1.Len() == 0) != (v2.IsNil() || v2.Len() == 0) {
|
||||
return false
|
||||
}
|
||||
if v1.IsNil() || v1.Len() == 0 {
|
||||
return true
|
||||
}
|
||||
if v1.Len() != v2.Len() {
|
||||
return false
|
||||
}
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
return true
|
||||
}
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
if !e.deepValueEqual(v1.Index(i), v2.Index(i), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Interface:
|
||||
if v1.IsNil() || v2.IsNil() {
|
||||
return v1.IsNil() == v2.IsNil()
|
||||
}
|
||||
return e.deepValueEqual(v1.Elem(), v2.Elem(), visited, depth+1)
|
||||
case reflect.Ptr:
|
||||
return e.deepValueEqual(v1.Elem(), v2.Elem(), visited, depth+1)
|
||||
case reflect.Struct:
|
||||
for i, n := 0, v1.NumField(); i < n; i++ {
|
||||
if !e.deepValueEqual(v1.Field(i), v2.Field(i), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Map:
|
||||
if (v1.IsNil() || v1.Len() == 0) != (v2.IsNil() || v2.Len() == 0) {
|
||||
return false
|
||||
}
|
||||
if v1.IsNil() || v1.Len() == 0 {
|
||||
return true
|
||||
}
|
||||
if v1.Len() != v2.Len() {
|
||||
return false
|
||||
}
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
return true
|
||||
}
|
||||
for _, k := range v1.MapKeys() {
|
||||
if !e.deepValueEqual(v1.MapIndex(k), v2.MapIndex(k), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Func:
|
||||
if v1.IsNil() && v2.IsNil() {
|
||||
return true
|
||||
}
|
||||
// Can't do better than this:
|
||||
return false
|
||||
default:
|
||||
// Normal equality suffices
|
||||
if !v1.CanInterface() || !v2.CanInterface() {
|
||||
panic(unexportedTypePanic{})
|
||||
}
|
||||
return v1.Interface() == v2.Interface()
|
||||
}
|
||||
}
|
||||
|
||||
// DeepEqual is like reflect.DeepEqual, but focused on semantic equality
|
||||
// instead of memory equality.
|
||||
//
|
||||
// It will use e's equality functions if it finds types that match.
|
||||
//
|
||||
// An empty slice *is* equal to a nil slice for our purposes; same for maps.
|
||||
//
|
||||
// Unexported field members cannot be compared and will cause an imformative panic; you must add an Equality
|
||||
// function for these types.
|
||||
func (e Equalities) DeepEqual(a1, a2 interface{}) bool {
|
||||
if a1 == nil || a2 == nil {
|
||||
return a1 == a2
|
||||
}
|
||||
v1 := reflect.ValueOf(a1)
|
||||
v2 := reflect.ValueOf(a2)
|
||||
if v1.Type() != v2.Type() {
|
||||
return false
|
||||
}
|
||||
return e.deepValueEqual(v1, v2, make(map[visit]bool), 0)
|
||||
}
|
||||
|
||||
func (e Equalities) deepValueDerive(v1, v2 reflect.Value, visited map[visit]bool, depth int) bool {
|
||||
defer makeUsefulPanic(v1)
|
||||
|
||||
if !v1.IsValid() || !v2.IsValid() {
|
||||
return v1.IsValid() == v2.IsValid()
|
||||
}
|
||||
if v1.Type() != v2.Type() {
|
||||
return false
|
||||
}
|
||||
if fv, ok := e[v1.Type()]; ok {
|
||||
return fv.Call([]reflect.Value{v1, v2})[0].Bool()
|
||||
}
|
||||
|
||||
hard := func(k reflect.Kind) bool {
|
||||
switch k {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.Struct:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if v1.CanAddr() && v2.CanAddr() && hard(v1.Kind()) {
|
||||
addr1 := v1.UnsafeAddr()
|
||||
addr2 := v2.UnsafeAddr()
|
||||
if addr1 > addr2 {
|
||||
// Canonicalize order to reduce number of entries in visited.
|
||||
addr1, addr2 = addr2, addr1
|
||||
}
|
||||
|
||||
// Short circuit if references are identical ...
|
||||
if addr1 == addr2 {
|
||||
return true
|
||||
}
|
||||
|
||||
// ... or already seen
|
||||
typ := v1.Type()
|
||||
v := visit{addr1, addr2, typ}
|
||||
if visited[v] {
|
||||
return true
|
||||
}
|
||||
|
||||
// Remember for later.
|
||||
visited[v] = true
|
||||
}
|
||||
|
||||
switch v1.Kind() {
|
||||
case reflect.Array:
|
||||
// We don't need to check length here because length is part of
|
||||
// an array's type, which has already been filtered for.
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
if !e.deepValueDerive(v1.Index(i), v2.Index(i), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Slice:
|
||||
if v1.IsNil() || v1.Len() == 0 {
|
||||
return true
|
||||
}
|
||||
if v1.Len() > v2.Len() {
|
||||
return false
|
||||
}
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
return true
|
||||
}
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
if !e.deepValueDerive(v1.Index(i), v2.Index(i), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.String:
|
||||
if v1.Len() == 0 {
|
||||
return true
|
||||
}
|
||||
if v1.Len() > v2.Len() {
|
||||
return false
|
||||
}
|
||||
return v1.String() == v2.String()
|
||||
case reflect.Interface:
|
||||
if v1.IsNil() {
|
||||
return true
|
||||
}
|
||||
return e.deepValueDerive(v1.Elem(), v2.Elem(), visited, depth+1)
|
||||
case reflect.Ptr:
|
||||
if v1.IsNil() {
|
||||
return true
|
||||
}
|
||||
return e.deepValueDerive(v1.Elem(), v2.Elem(), visited, depth+1)
|
||||
case reflect.Struct:
|
||||
for i, n := 0, v1.NumField(); i < n; i++ {
|
||||
if !e.deepValueDerive(v1.Field(i), v2.Field(i), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Map:
|
||||
if v1.IsNil() || v1.Len() == 0 {
|
||||
return true
|
||||
}
|
||||
if v1.Len() > v2.Len() {
|
||||
return false
|
||||
}
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
return true
|
||||
}
|
||||
for _, k := range v1.MapKeys() {
|
||||
if !e.deepValueDerive(v1.MapIndex(k), v2.MapIndex(k), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Func:
|
||||
if v1.IsNil() && v2.IsNil() {
|
||||
return true
|
||||
}
|
||||
// Can't do better than this:
|
||||
return false
|
||||
default:
|
||||
// Normal equality suffices
|
||||
if !v1.CanInterface() || !v2.CanInterface() {
|
||||
panic(unexportedTypePanic{})
|
||||
}
|
||||
return v1.Interface() == v2.Interface()
|
||||
}
|
||||
}
|
||||
|
||||
// DeepDerivative is similar to DeepEqual except that unset fields in a1 are
|
||||
// ignored (not compared). This allows us to focus on the fields that matter to
|
||||
// the semantic comparison.
|
||||
//
|
||||
// The unset fields include a nil pointer and an empty string.
|
||||
func (e Equalities) DeepDerivative(a1, a2 interface{}) bool {
|
||||
if a1 == nil {
|
||||
return true
|
||||
}
|
||||
v1 := reflect.ValueOf(a1)
|
||||
v2 := reflect.ValueOf(a2)
|
||||
if v1.Type() != v2.Type() {
|
||||
return false
|
||||
}
|
||||
return e.deepValueDerive(v1, v2, make(map[visit]bool), 0)
|
||||
}
|
|
@ -1,137 +0,0 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package reflect
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEqualities(t *testing.T) {
|
||||
e := Equalities{}
|
||||
type Bar struct {
|
||||
X int
|
||||
}
|
||||
type Baz struct {
|
||||
Y Bar
|
||||
}
|
||||
err := e.AddFuncs(
|
||||
func(a, b int) bool {
|
||||
return a+1 == b
|
||||
},
|
||||
func(a, b Bar) bool {
|
||||
return a.X*10 == b.X
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected: %v", err)
|
||||
}
|
||||
|
||||
type Foo struct {
|
||||
X int
|
||||
}
|
||||
|
||||
table := []struct {
|
||||
a, b interface{}
|
||||
equal bool
|
||||
}{
|
||||
{1, 2, true},
|
||||
{2, 1, false},
|
||||
{"foo", "fo", false},
|
||||
{"foo", "foo", true},
|
||||
{"foo", "foobar", false},
|
||||
{Foo{1}, Foo{2}, true},
|
||||
{Foo{2}, Foo{1}, false},
|
||||
{Bar{1}, Bar{10}, true},
|
||||
{&Bar{1}, &Bar{10}, true},
|
||||
{Baz{Bar{1}}, Baz{Bar{10}}, true},
|
||||
{[...]string{}, [...]string{"1", "2", "3"}, false},
|
||||
{[...]string{"1"}, [...]string{"1", "2", "3"}, false},
|
||||
{[...]string{"1", "2", "3"}, [...]string{}, false},
|
||||
{[...]string{"1", "2", "3"}, [...]string{"1", "2", "3"}, true},
|
||||
{map[string]int{"foo": 1}, map[string]int{}, false},
|
||||
{map[string]int{"foo": 1}, map[string]int{"foo": 2}, true},
|
||||
{map[string]int{"foo": 2}, map[string]int{"foo": 1}, false},
|
||||
{map[string]int{"foo": 1}, map[string]int{"foo": 2, "bar": 6}, false},
|
||||
{map[string]int{"foo": 1, "bar": 6}, map[string]int{"foo": 2}, false},
|
||||
{map[string]int{}, map[string]int(nil), true},
|
||||
{[]string(nil), []string(nil), true},
|
||||
{[]string{}, []string(nil), true},
|
||||
{[]string(nil), []string{}, true},
|
||||
{[]string{"1"}, []string(nil), false},
|
||||
{[]string{}, []string{"1", "2", "3"}, false},
|
||||
{[]string{"1"}, []string{"1", "2", "3"}, false},
|
||||
{[]string{"1", "2", "3"}, []string{}, false},
|
||||
}
|
||||
|
||||
for _, item := range table {
|
||||
if e, a := item.equal, e.DeepEqual(item.a, item.b); e != a {
|
||||
t.Errorf("Expected (%+v == %+v) == %v, but got %v", item.a, item.b, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDerivates(t *testing.T) {
|
||||
e := Equalities{}
|
||||
type Bar struct {
|
||||
X int
|
||||
}
|
||||
type Baz struct {
|
||||
Y Bar
|
||||
}
|
||||
err := e.AddFuncs(
|
||||
func(a, b int) bool {
|
||||
return a+1 == b
|
||||
},
|
||||
func(a, b Bar) bool {
|
||||
return a.X*10 == b.X
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected: %v", err)
|
||||
}
|
||||
|
||||
type Foo struct {
|
||||
X int
|
||||
}
|
||||
|
||||
table := []struct {
|
||||
a, b interface{}
|
||||
equal bool
|
||||
}{
|
||||
{1, 2, true},
|
||||
{2, 1, false},
|
||||
{"foo", "fo", false},
|
||||
{"foo", "foo", true},
|
||||
{"foo", "foobar", false},
|
||||
{Foo{1}, Foo{2}, true},
|
||||
{Foo{2}, Foo{1}, false},
|
||||
{Bar{1}, Bar{10}, true},
|
||||
{&Bar{1}, &Bar{10}, true},
|
||||
{Baz{Bar{1}}, Baz{Bar{10}}, true},
|
||||
{[...]string{}, [...]string{"1", "2", "3"}, false},
|
||||
{[...]string{"1"}, [...]string{"1", "2", "3"}, false},
|
||||
{[...]string{"1", "2", "3"}, [...]string{}, false},
|
||||
{[...]string{"1", "2", "3"}, [...]string{"1", "2", "3"}, true},
|
||||
{map[string]int{"foo": 1}, map[string]int{}, false},
|
||||
{map[string]int{"foo": 1}, map[string]int{"foo": 2}, true},
|
||||
{map[string]int{"foo": 2}, map[string]int{"foo": 1}, false},
|
||||
{map[string]int{"foo": 1}, map[string]int{"foo": 2, "bar": 6}, true},
|
||||
{map[string]int{"foo": 1, "bar": 6}, map[string]int{"foo": 2}, false},
|
||||
{map[string]int{}, map[string]int(nil), true},
|
||||
{[]string(nil), []string(nil), true},
|
||||
{[]string{}, []string(nil), true},
|
||||
{[]string(nil), []string{}, true},
|
||||
{[]string{"1"}, []string(nil), false},
|
||||
{[]string{}, []string{"1", "2", "3"}, true},
|
||||
{[]string{"1"}, []string{"1", "2", "3"}, true},
|
||||
{[]string{"1", "2", "3"}, []string{}, false},
|
||||
}
|
||||
|
||||
for _, item := range table {
|
||||
if e, a := item.equal, e.DeepDerivative(item.a, item.b); e != a {
|
||||
t.Errorf("Expected (%+v ~ %+v) == %v, but got %v", item.a, item.b, e, a)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
//This package is copied from Go library reflect/type.go.
|
||||
//The struct tag library provides no way to extract the list of struct tags, only
|
||||
//a specific tag
|
||||
package reflect
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type StructTag struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
func (t StructTag) String() string {
|
||||
return fmt.Sprintf("%s:%q", t.Name, t.Value)
|
||||
}
|
||||
|
||||
type StructTags []StructTag
|
||||
|
||||
func (tags StructTags) String() string {
|
||||
s := make([]string, 0, len(tags))
|
||||
for _, tag := range tags {
|
||||
s = append(s, tag.String())
|
||||
}
|
||||
return "`" + strings.Join(s, " ") + "`"
|
||||
}
|
||||
|
||||
func (tags StructTags) Has(name string) bool {
|
||||
for i := range tags {
|
||||
if tags[i].Name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ParseStructTags returns the full set of fields in a struct tag in the order they appear in
|
||||
// the struct tag.
|
||||
func ParseStructTags(tag string) (StructTags, error) {
|
||||
tags := StructTags{}
|
||||
for tag != "" {
|
||||
// Skip leading space.
|
||||
i := 0
|
||||
for i < len(tag) && tag[i] == ' ' {
|
||||
i++
|
||||
}
|
||||
tag = tag[i:]
|
||||
if tag == "" {
|
||||
break
|
||||
}
|
||||
|
||||
// Scan to colon. A space, a quote or a control character is a syntax error.
|
||||
// Strictly speaking, control chars include the range [0x7f, 0x9f], not just
|
||||
// [0x00, 0x1f], but in practice, we ignore the multi-byte control characters
|
||||
// as it is simpler to inspect the tag's bytes than the tag's runes.
|
||||
i = 0
|
||||
for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f {
|
||||
i++
|
||||
}
|
||||
if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
|
||||
break
|
||||
}
|
||||
name := string(tag[:i])
|
||||
tag = tag[i+1:]
|
||||
|
||||
// Scan quoted string to find value.
|
||||
i = 1
|
||||
for i < len(tag) && tag[i] != '"' {
|
||||
if tag[i] == '\\' {
|
||||
i++
|
||||
}
|
||||
i++
|
||||
}
|
||||
if i >= len(tag) {
|
||||
break
|
||||
}
|
||||
qvalue := string(tag[:i+1])
|
||||
tag = tag[i+1:]
|
||||
|
||||
value, err := strconv.Unquote(qvalue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tags = append(tags, StructTag{Name: name, Value: value})
|
||||
}
|
||||
return tags, nil
|
||||
}
|
|
@ -1,30 +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 = [
|
||||
"exec.go",
|
||||
"funcs.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/third_party/forked/golang/template",
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
|
@ -1,94 +0,0 @@
|
|||
//This package is copied from Go library text/template.
|
||||
//The original private functions indirect and printableValue
|
||||
//are exported as public functions.
|
||||
package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var Indirect = indirect
|
||||
var PrintableValue = printableValue
|
||||
|
||||
var (
|
||||
errorType = reflect.TypeOf((*error)(nil)).Elem()
|
||||
fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
|
||||
)
|
||||
|
||||
// indirect returns the item at the end of indirection, and a bool to indicate if it's nil.
|
||||
// We indirect through pointers and empty interfaces (only) because
|
||||
// non-empty interfaces have methods we might need.
|
||||
func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
|
||||
for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() {
|
||||
if v.IsNil() {
|
||||
return v, true
|
||||
}
|
||||
if v.Kind() == reflect.Interface && v.NumMethod() > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return v, false
|
||||
}
|
||||
|
||||
// printableValue returns the, possibly indirected, interface value inside v that
|
||||
// is best for a call to formatted printer.
|
||||
func printableValue(v reflect.Value) (interface{}, bool) {
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v, _ = indirect(v) // fmt.Fprint handles nil.
|
||||
}
|
||||
if !v.IsValid() {
|
||||
return "<no value>", true
|
||||
}
|
||||
|
||||
if !v.Type().Implements(errorType) && !v.Type().Implements(fmtStringerType) {
|
||||
if v.CanAddr() && (reflect.PtrTo(v.Type()).Implements(errorType) || reflect.PtrTo(v.Type()).Implements(fmtStringerType)) {
|
||||
v = v.Addr()
|
||||
} else {
|
||||
switch v.Kind() {
|
||||
case reflect.Chan, reflect.Func:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
}
|
||||
return v.Interface(), true
|
||||
}
|
||||
|
||||
// canBeNil reports whether an untyped nil can be assigned to the type. See reflect.Zero.
|
||||
func canBeNil(typ reflect.Type) bool {
|
||||
switch typ.Kind() {
|
||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isTrue reports whether the value is 'true', in the sense of not the zero of its type,
|
||||
// and whether the value has a meaningful truth value.
|
||||
func isTrue(val reflect.Value) (truth, ok bool) {
|
||||
if !val.IsValid() {
|
||||
// Something like var x interface{}, never set. It's a form of nil.
|
||||
return false, true
|
||||
}
|
||||
switch val.Kind() {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||
truth = val.Len() > 0
|
||||
case reflect.Bool:
|
||||
truth = val.Bool()
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
truth = val.Complex() != 0
|
||||
case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Interface:
|
||||
truth = !val.IsNil()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
truth = val.Int() != 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
truth = val.Float() != 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
truth = val.Uint() != 0
|
||||
case reflect.Struct:
|
||||
truth = true // Struct values are always true.
|
||||
default:
|
||||
return
|
||||
}
|
||||
return truth, true
|
||||
}
|
|
@ -1,599 +0,0 @@
|
|||
//This package is copied from Go library text/template.
|
||||
//The original private functions eq, ge, gt, le, lt, and ne
|
||||
//are exported as public functions.
|
||||
package template
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var Equal = eq
|
||||
var GreaterEqual = ge
|
||||
var Greater = gt
|
||||
var LessEqual = le
|
||||
var Less = lt
|
||||
var NotEqual = ne
|
||||
|
||||
// FuncMap is the type of the map defining the mapping from names to functions.
|
||||
// Each function must have either a single return value, or two return values of
|
||||
// which the second has type error. In that case, if the second (error)
|
||||
// return value evaluates to non-nil during execution, execution terminates and
|
||||
// Execute returns that error.
|
||||
type FuncMap map[string]interface{}
|
||||
|
||||
var builtins = FuncMap{
|
||||
"and": and,
|
||||
"call": call,
|
||||
"html": HTMLEscaper,
|
||||
"index": index,
|
||||
"js": JSEscaper,
|
||||
"len": length,
|
||||
"not": not,
|
||||
"or": or,
|
||||
"print": fmt.Sprint,
|
||||
"printf": fmt.Sprintf,
|
||||
"println": fmt.Sprintln,
|
||||
"urlquery": URLQueryEscaper,
|
||||
|
||||
// Comparisons
|
||||
"eq": eq, // ==
|
||||
"ge": ge, // >=
|
||||
"gt": gt, // >
|
||||
"le": le, // <=
|
||||
"lt": lt, // <
|
||||
"ne": ne, // !=
|
||||
}
|
||||
|
||||
var builtinFuncs = createValueFuncs(builtins)
|
||||
|
||||
// createValueFuncs turns a FuncMap into a map[string]reflect.Value
|
||||
func createValueFuncs(funcMap FuncMap) map[string]reflect.Value {
|
||||
m := make(map[string]reflect.Value)
|
||||
addValueFuncs(m, funcMap)
|
||||
return m
|
||||
}
|
||||
|
||||
// addValueFuncs adds to values the functions in funcs, converting them to reflect.Values.
|
||||
func addValueFuncs(out map[string]reflect.Value, in FuncMap) {
|
||||
for name, fn := range in {
|
||||
v := reflect.ValueOf(fn)
|
||||
if v.Kind() != reflect.Func {
|
||||
panic("value for " + name + " not a function")
|
||||
}
|
||||
if !goodFunc(v.Type()) {
|
||||
panic(fmt.Errorf("can't install method/function %q with %d results", name, v.Type().NumOut()))
|
||||
}
|
||||
out[name] = v
|
||||
}
|
||||
}
|
||||
|
||||
// AddFuncs adds to values the functions in funcs. It does no checking of the input -
|
||||
// call addValueFuncs first.
|
||||
func addFuncs(out, in FuncMap) {
|
||||
for name, fn := range in {
|
||||
out[name] = fn
|
||||
}
|
||||
}
|
||||
|
||||
// goodFunc checks that the function or method has the right result signature.
|
||||
func goodFunc(typ reflect.Type) bool {
|
||||
// We allow functions with 1 result or 2 results where the second is an error.
|
||||
switch {
|
||||
case typ.NumOut() == 1:
|
||||
return true
|
||||
case typ.NumOut() == 2 && typ.Out(1) == errorType:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// findFunction looks for a function in the template, and global map.
|
||||
func findFunction(name string) (reflect.Value, bool) {
|
||||
if fn := builtinFuncs[name]; fn.IsValid() {
|
||||
return fn, true
|
||||
}
|
||||
return reflect.Value{}, false
|
||||
}
|
||||
|
||||
// Indexing.
|
||||
|
||||
// index returns the result of indexing its first argument by the following
|
||||
// arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each
|
||||
// indexed item must be a map, slice, or array.
|
||||
func index(item interface{}, indices ...interface{}) (interface{}, error) {
|
||||
v := reflect.ValueOf(item)
|
||||
for _, i := range indices {
|
||||
index := reflect.ValueOf(i)
|
||||
var isNil bool
|
||||
if v, isNil = indirect(v); isNil {
|
||||
return nil, fmt.Errorf("index of nil pointer")
|
||||
}
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Slice, reflect.String:
|
||||
var x int64
|
||||
switch index.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
x = index.Int()
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
x = int64(index.Uint())
|
||||
default:
|
||||
return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type())
|
||||
}
|
||||
if x < 0 || x >= int64(v.Len()) {
|
||||
return nil, fmt.Errorf("index out of range: %d", x)
|
||||
}
|
||||
v = v.Index(int(x))
|
||||
case reflect.Map:
|
||||
if !index.IsValid() {
|
||||
index = reflect.Zero(v.Type().Key())
|
||||
}
|
||||
if !index.Type().AssignableTo(v.Type().Key()) {
|
||||
return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type())
|
||||
}
|
||||
if x := v.MapIndex(index); x.IsValid() {
|
||||
v = x
|
||||
} else {
|
||||
v = reflect.Zero(v.Type().Elem())
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("can't index item of type %s", v.Type())
|
||||
}
|
||||
}
|
||||
return v.Interface(), nil
|
||||
}
|
||||
|
||||
// Length
|
||||
|
||||
// length returns the length of the item, with an error if it has no defined length.
|
||||
func length(item interface{}) (int, error) {
|
||||
v, isNil := indirect(reflect.ValueOf(item))
|
||||
if isNil {
|
||||
return 0, fmt.Errorf("len of nil pointer")
|
||||
}
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
|
||||
return v.Len(), nil
|
||||
}
|
||||
return 0, fmt.Errorf("len of type %s", v.Type())
|
||||
}
|
||||
|
||||
// Function invocation
|
||||
|
||||
// call returns the result of evaluating the first argument as a function.
|
||||
// The function must return 1 result, or 2 results, the second of which is an error.
|
||||
func call(fn interface{}, args ...interface{}) (interface{}, error) {
|
||||
v := reflect.ValueOf(fn)
|
||||
typ := v.Type()
|
||||
if typ.Kind() != reflect.Func {
|
||||
return nil, fmt.Errorf("non-function of type %s", typ)
|
||||
}
|
||||
if !goodFunc(typ) {
|
||||
return nil, fmt.Errorf("function called with %d args; should be 1 or 2", typ.NumOut())
|
||||
}
|
||||
numIn := typ.NumIn()
|
||||
var dddType reflect.Type
|
||||
if typ.IsVariadic() {
|
||||
if len(args) < numIn-1 {
|
||||
return nil, fmt.Errorf("wrong number of args: got %d want at least %d", len(args), numIn-1)
|
||||
}
|
||||
dddType = typ.In(numIn - 1).Elem()
|
||||
} else {
|
||||
if len(args) != numIn {
|
||||
return nil, fmt.Errorf("wrong number of args: got %d want %d", len(args), numIn)
|
||||
}
|
||||
}
|
||||
argv := make([]reflect.Value, len(args))
|
||||
for i, arg := range args {
|
||||
value := reflect.ValueOf(arg)
|
||||
// Compute the expected type. Clumsy because of variadics.
|
||||
var argType reflect.Type
|
||||
if !typ.IsVariadic() || i < numIn-1 {
|
||||
argType = typ.In(i)
|
||||
} else {
|
||||
argType = dddType
|
||||
}
|
||||
if !value.IsValid() && canBeNil(argType) {
|
||||
value = reflect.Zero(argType)
|
||||
}
|
||||
if !value.Type().AssignableTo(argType) {
|
||||
return nil, fmt.Errorf("arg %d has type %s; should be %s", i, value.Type(), argType)
|
||||
}
|
||||
argv[i] = value
|
||||
}
|
||||
result := v.Call(argv)
|
||||
if len(result) == 2 && !result[1].IsNil() {
|
||||
return result[0].Interface(), result[1].Interface().(error)
|
||||
}
|
||||
return result[0].Interface(), nil
|
||||
}
|
||||
|
||||
// Boolean logic.
|
||||
|
||||
func truth(a interface{}) bool {
|
||||
t, _ := isTrue(reflect.ValueOf(a))
|
||||
return t
|
||||
}
|
||||
|
||||
// and computes the Boolean AND of its arguments, returning
|
||||
// the first false argument it encounters, or the last argument.
|
||||
func and(arg0 interface{}, args ...interface{}) interface{} {
|
||||
if !truth(arg0) {
|
||||
return arg0
|
||||
}
|
||||
for i := range args {
|
||||
arg0 = args[i]
|
||||
if !truth(arg0) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return arg0
|
||||
}
|
||||
|
||||
// or computes the Boolean OR of its arguments, returning
|
||||
// the first true argument it encounters, or the last argument.
|
||||
func or(arg0 interface{}, args ...interface{}) interface{} {
|
||||
if truth(arg0) {
|
||||
return arg0
|
||||
}
|
||||
for i := range args {
|
||||
arg0 = args[i]
|
||||
if truth(arg0) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return arg0
|
||||
}
|
||||
|
||||
// not returns the Boolean negation of its argument.
|
||||
func not(arg interface{}) (truth bool) {
|
||||
truth, _ = isTrue(reflect.ValueOf(arg))
|
||||
return !truth
|
||||
}
|
||||
|
||||
// Comparison.
|
||||
|
||||
// TODO: Perhaps allow comparison between signed and unsigned integers.
|
||||
|
||||
var (
|
||||
errBadComparisonType = errors.New("invalid type for comparison")
|
||||
errBadComparison = errors.New("incompatible types for comparison")
|
||||
errNoComparison = errors.New("missing argument for comparison")
|
||||
)
|
||||
|
||||
type kind int
|
||||
|
||||
const (
|
||||
invalidKind kind = iota
|
||||
boolKind
|
||||
complexKind
|
||||
intKind
|
||||
floatKind
|
||||
integerKind
|
||||
stringKind
|
||||
uintKind
|
||||
)
|
||||
|
||||
func basicKind(v reflect.Value) (kind, error) {
|
||||
switch v.Kind() {
|
||||
case reflect.Bool:
|
||||
return boolKind, nil
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return intKind, nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return uintKind, nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return floatKind, nil
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
return complexKind, nil
|
||||
case reflect.String:
|
||||
return stringKind, nil
|
||||
}
|
||||
return invalidKind, errBadComparisonType
|
||||
}
|
||||
|
||||
// eq evaluates the comparison a == b || a == c || ...
|
||||
func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) {
|
||||
v1 := reflect.ValueOf(arg1)
|
||||
k1, err := basicKind(v1)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(arg2) == 0 {
|
||||
return false, errNoComparison
|
||||
}
|
||||
for _, arg := range arg2 {
|
||||
v2 := reflect.ValueOf(arg)
|
||||
k2, err := basicKind(v2)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
truth := false
|
||||
if k1 != k2 {
|
||||
// Special case: Can compare integer values regardless of type's sign.
|
||||
switch {
|
||||
case k1 == intKind && k2 == uintKind:
|
||||
truth = v1.Int() >= 0 && uint64(v1.Int()) == v2.Uint()
|
||||
case k1 == uintKind && k2 == intKind:
|
||||
truth = v2.Int() >= 0 && v1.Uint() == uint64(v2.Int())
|
||||
default:
|
||||
return false, errBadComparison
|
||||
}
|
||||
} else {
|
||||
switch k1 {
|
||||
case boolKind:
|
||||
truth = v1.Bool() == v2.Bool()
|
||||
case complexKind:
|
||||
truth = v1.Complex() == v2.Complex()
|
||||
case floatKind:
|
||||
truth = v1.Float() == v2.Float()
|
||||
case intKind:
|
||||
truth = v1.Int() == v2.Int()
|
||||
case stringKind:
|
||||
truth = v1.String() == v2.String()
|
||||
case uintKind:
|
||||
truth = v1.Uint() == v2.Uint()
|
||||
default:
|
||||
panic("invalid kind")
|
||||
}
|
||||
}
|
||||
if truth {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// ne evaluates the comparison a != b.
|
||||
func ne(arg1, arg2 interface{}) (bool, error) {
|
||||
// != is the inverse of ==.
|
||||
equal, err := eq(arg1, arg2)
|
||||
return !equal, err
|
||||
}
|
||||
|
||||
// lt evaluates the comparison a < b.
|
||||
func lt(arg1, arg2 interface{}) (bool, error) {
|
||||
v1 := reflect.ValueOf(arg1)
|
||||
k1, err := basicKind(v1)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
v2 := reflect.ValueOf(arg2)
|
||||
k2, err := basicKind(v2)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
truth := false
|
||||
if k1 != k2 {
|
||||
// Special case: Can compare integer values regardless of type's sign.
|
||||
switch {
|
||||
case k1 == intKind && k2 == uintKind:
|
||||
truth = v1.Int() < 0 || uint64(v1.Int()) < v2.Uint()
|
||||
case k1 == uintKind && k2 == intKind:
|
||||
truth = v2.Int() >= 0 && v1.Uint() < uint64(v2.Int())
|
||||
default:
|
||||
return false, errBadComparison
|
||||
}
|
||||
} else {
|
||||
switch k1 {
|
||||
case boolKind, complexKind:
|
||||
return false, errBadComparisonType
|
||||
case floatKind:
|
||||
truth = v1.Float() < v2.Float()
|
||||
case intKind:
|
||||
truth = v1.Int() < v2.Int()
|
||||
case stringKind:
|
||||
truth = v1.String() < v2.String()
|
||||
case uintKind:
|
||||
truth = v1.Uint() < v2.Uint()
|
||||
default:
|
||||
panic("invalid kind")
|
||||
}
|
||||
}
|
||||
return truth, nil
|
||||
}
|
||||
|
||||
// le evaluates the comparison <= b.
|
||||
func le(arg1, arg2 interface{}) (bool, error) {
|
||||
// <= is < or ==.
|
||||
lessThan, err := lt(arg1, arg2)
|
||||
if lessThan || err != nil {
|
||||
return lessThan, err
|
||||
}
|
||||
return eq(arg1, arg2)
|
||||
}
|
||||
|
||||
// gt evaluates the comparison a > b.
|
||||
func gt(arg1, arg2 interface{}) (bool, error) {
|
||||
// > is the inverse of <=.
|
||||
lessOrEqual, err := le(arg1, arg2)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return !lessOrEqual, nil
|
||||
}
|
||||
|
||||
// ge evaluates the comparison a >= b.
|
||||
func ge(arg1, arg2 interface{}) (bool, error) {
|
||||
// >= is the inverse of <.
|
||||
lessThan, err := lt(arg1, arg2)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return !lessThan, nil
|
||||
}
|
||||
|
||||
// HTML escaping.
|
||||
|
||||
var (
|
||||
htmlQuot = []byte(""") // shorter than """
|
||||
htmlApos = []byte("'") // shorter than "'" and apos was not in HTML until HTML5
|
||||
htmlAmp = []byte("&")
|
||||
htmlLt = []byte("<")
|
||||
htmlGt = []byte(">")
|
||||
)
|
||||
|
||||
// HTMLEscape writes to w the escaped HTML equivalent of the plain text data b.
|
||||
func HTMLEscape(w io.Writer, b []byte) {
|
||||
last := 0
|
||||
for i, c := range b {
|
||||
var html []byte
|
||||
switch c {
|
||||
case '"':
|
||||
html = htmlQuot
|
||||
case '\'':
|
||||
html = htmlApos
|
||||
case '&':
|
||||
html = htmlAmp
|
||||
case '<':
|
||||
html = htmlLt
|
||||
case '>':
|
||||
html = htmlGt
|
||||
default:
|
||||
continue
|
||||
}
|
||||
w.Write(b[last:i])
|
||||
w.Write(html)
|
||||
last = i + 1
|
||||
}
|
||||
w.Write(b[last:])
|
||||
}
|
||||
|
||||
// HTMLEscapeString returns the escaped HTML equivalent of the plain text data s.
|
||||
func HTMLEscapeString(s string) string {
|
||||
// Avoid allocation if we can.
|
||||
if strings.IndexAny(s, `'"&<>`) < 0 {
|
||||
return s
|
||||
}
|
||||
var b bytes.Buffer
|
||||
HTMLEscape(&b, []byte(s))
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// HTMLEscaper returns the escaped HTML equivalent of the textual
|
||||
// representation of its arguments.
|
||||
func HTMLEscaper(args ...interface{}) string {
|
||||
return HTMLEscapeString(evalArgs(args))
|
||||
}
|
||||
|
||||
// JavaScript escaping.
|
||||
|
||||
var (
|
||||
jsLowUni = []byte(`\u00`)
|
||||
hex = []byte("0123456789ABCDEF")
|
||||
|
||||
jsBackslash = []byte(`\\`)
|
||||
jsApos = []byte(`\'`)
|
||||
jsQuot = []byte(`\"`)
|
||||
jsLt = []byte(`\x3C`)
|
||||
jsGt = []byte(`\x3E`)
|
||||
)
|
||||
|
||||
// JSEscape writes to w the escaped JavaScript equivalent of the plain text data b.
|
||||
func JSEscape(w io.Writer, b []byte) {
|
||||
last := 0
|
||||
for i := 0; i < len(b); i++ {
|
||||
c := b[i]
|
||||
|
||||
if !jsIsSpecial(rune(c)) {
|
||||
// fast path: nothing to do
|
||||
continue
|
||||
}
|
||||
w.Write(b[last:i])
|
||||
|
||||
if c < utf8.RuneSelf {
|
||||
// Quotes, slashes and angle brackets get quoted.
|
||||
// Control characters get written as \u00XX.
|
||||
switch c {
|
||||
case '\\':
|
||||
w.Write(jsBackslash)
|
||||
case '\'':
|
||||
w.Write(jsApos)
|
||||
case '"':
|
||||
w.Write(jsQuot)
|
||||
case '<':
|
||||
w.Write(jsLt)
|
||||
case '>':
|
||||
w.Write(jsGt)
|
||||
default:
|
||||
w.Write(jsLowUni)
|
||||
t, b := c>>4, c&0x0f
|
||||
w.Write(hex[t : t+1])
|
||||
w.Write(hex[b : b+1])
|
||||
}
|
||||
} else {
|
||||
// Unicode rune.
|
||||
r, size := utf8.DecodeRune(b[i:])
|
||||
if unicode.IsPrint(r) {
|
||||
w.Write(b[i : i+size])
|
||||
} else {
|
||||
fmt.Fprintf(w, "\\u%04X", r)
|
||||
}
|
||||
i += size - 1
|
||||
}
|
||||
last = i + 1
|
||||
}
|
||||
w.Write(b[last:])
|
||||
}
|
||||
|
||||
// JSEscapeString returns the escaped JavaScript equivalent of the plain text data s.
|
||||
func JSEscapeString(s string) string {
|
||||
// Avoid allocation if we can.
|
||||
if strings.IndexFunc(s, jsIsSpecial) < 0 {
|
||||
return s
|
||||
}
|
||||
var b bytes.Buffer
|
||||
JSEscape(&b, []byte(s))
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func jsIsSpecial(r rune) bool {
|
||||
switch r {
|
||||
case '\\', '\'', '"', '<', '>':
|
||||
return true
|
||||
}
|
||||
return r < ' ' || utf8.RuneSelf <= r
|
||||
}
|
||||
|
||||
// JSEscaper returns the escaped JavaScript equivalent of the textual
|
||||
// representation of its arguments.
|
||||
func JSEscaper(args ...interface{}) string {
|
||||
return JSEscapeString(evalArgs(args))
|
||||
}
|
||||
|
||||
// URLQueryEscaper returns the escaped value of the textual representation of
|
||||
// its arguments in a form suitable for embedding in a URL query.
|
||||
func URLQueryEscaper(args ...interface{}) string {
|
||||
return url.QueryEscape(evalArgs(args))
|
||||
}
|
||||
|
||||
// evalArgs formats the list of arguments into a string. It is therefore equivalent to
|
||||
// fmt.Sprint(args...)
|
||||
// except that each argument is indirected (if a pointer), as required,
|
||||
// using the same rules as the default string evaluation during template
|
||||
// execution.
|
||||
func evalArgs(args []interface{}) string {
|
||||
ok := false
|
||||
var s string
|
||||
// Fast path for simple common case.
|
||||
if len(args) == 1 {
|
||||
s, ok = args[0].(string)
|
||||
}
|
||||
if !ok {
|
||||
for i, arg := range args {
|
||||
a, ok := printableValue(reflect.ValueOf(arg))
|
||||
if ok {
|
||||
args[i] = a
|
||||
} // else left fmt do its thing
|
||||
}
|
||||
s = fmt.Sprint(args...)
|
||||
}
|
||||
return s
|
||||
}
|
|
@ -1,172 +0,0 @@
|
|||
Shell2Junit License Information
|
||||
|
||||
Feb, 2010
|
||||
|
||||
shell2junit library and sample code is licensed under Apache License, v.2.0.
|
||||
|
||||
(c) 2009 Manolo Carrasco (Manuel Carrasco Moñino)
|
||||
|
||||
=====
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and
|
||||
distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the
|
||||
copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other
|
||||
entities that control, are controlled by, or are under common control with
|
||||
that entity. For the purposes of this definition, "control" means (i) the
|
||||
power, direct or indirect, to cause the direction or management of such
|
||||
entity, whether by contract or otherwise, or (ii) ownership of fifty percent
|
||||
(50%) or more of the outstanding shares, or (iii) beneficial ownership of
|
||||
such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
||||
permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation source, and
|
||||
configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation
|
||||
or translation of a Source form, including but not limited to compiled
|
||||
object code, generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form,
|
||||
made available under the License, as indicated by a copyright notice that is
|
||||
included in or attached to the work (an example is provided in the Appendix
|
||||
below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form,
|
||||
that is based on (or derived from) the Work and for which the editorial
|
||||
revisions, annotations, elaborations, or other modifications represent, as a
|
||||
whole, an original work of authorship. For the purposes of this License,
|
||||
Derivative Works shall not include works that remain separable from, or
|
||||
merely link (or bind by name) to the interfaces of, the Work and Derivative
|
||||
Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original
|
||||
version of the Work and any modifications or additions to that Work or
|
||||
Derivative Works thereof, that is intentionally submitted to Licensor for
|
||||
inclusion in the Work by the copyright owner or by an individual or Legal
|
||||
Entity authorized to submit on behalf of the copyright owner. For the
|
||||
purposes of this definition, "submitted" means any form of electronic,
|
||||
verbal, or written communication sent to the Licensor or its
|
||||
representatives, including but not limited to communication on electronic
|
||||
mailing lists, source code control systems, and issue tracking systems that
|
||||
are managed by, or on behalf of, the Licensor for the purpose of discussing
|
||||
and improving the Work, but excluding communication that is conspicuously
|
||||
marked or otherwise designated in writing by the copyright owner as "Not a
|
||||
Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on
|
||||
behalf of whom a Contribution has been received by Licensor and subsequently
|
||||
incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of this
|
||||
License, each Contributor hereby grants to You a perpetual, worldwide,
|
||||
non-exclusive, no-charge, royalty-free, irrevocable copyright license to
|
||||
reproduce, prepare Derivative Works of, publicly display, publicly perform,
|
||||
sublicense, and distribute the Work and such Derivative Works in Source or
|
||||
Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of this
|
||||
License, each Contributor hereby grants to You a perpetual, worldwide,
|
||||
non-exclusive, no-charge, royalty-free, irrevocable (except as stated in
|
||||
this section) patent license to make, have made, use, offer to sell, sell,
|
||||
import, and otherwise transfer the Work, where such license applies only to
|
||||
those patent claims licensable by such Contributor that are necessarily
|
||||
infringed by their Contribution(s) alone or by combination of their
|
||||
Contribution(s) with the Work to which such Contribution(s) was submitted.
|
||||
If You institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||
Contribution incorporated within the Work constitutes direct or contributory
|
||||
patent infringement, then any patent licenses granted to You under this
|
||||
License for that Work shall terminate as of the date such litigation is
|
||||
filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the Work or
|
||||
Derivative Works thereof in any medium, with or without modifications, and
|
||||
in Source or Object form, provided that You meet the following conditions:
|
||||
|
||||
a. You must give any other recipients of the Work or Derivative Works a copy
|
||||
of this License; and
|
||||
|
||||
b. You must cause any modified files to carry prominent notices stating that
|
||||
You changed the files; and
|
||||
|
||||
c. You must retain, in the Source form of any Derivative Works that You
|
||||
distribute, all copyright, patent, trademark, and attribution notices from
|
||||
the Source form of the Work, excluding those notices that do not pertain to
|
||||
any part of the Derivative Works; and
|
||||
|
||||
d. If the Work includes a "NOTICE" text file as part of its distribution,
|
||||
then any Derivative Works that You distribute must include a readable copy
|
||||
of the attribution notices contained within such NOTICE file, excluding
|
||||
those notices that do not pertain to any part of the Derivative Works, in at
|
||||
least one of the following places: within a NOTICE text file distributed as
|
||||
part of the Derivative Works; within the Source form or documentation, if
|
||||
provided along with the Derivative Works; or, within a display generated by
|
||||
the Derivative Works, if and wherever such third-party notices normally
|
||||
appear. The contents of the NOTICE file are for informational purposes only
|
||||
and do not modify the License. You may add Your own attribution notices
|
||||
within Derivative Works that You distribute, alongside or as an addendum to
|
||||
the NOTICE text from the Work, provided that such additional attribution
|
||||
notices cannot be construed as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and may
|
||||
provide additional or different license terms and conditions for use,
|
||||
reproduction, or distribution of Your modifications, or for any such
|
||||
Derivative Works as a whole, provided Your use, reproduction, and
|
||||
distribution of the Work otherwise complies with the conditions stated in
|
||||
this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise, any
|
||||
Contribution intentionally submitted for inclusion in the Work by You to the
|
||||
Licensor shall be under the terms and conditions of this License, without
|
||||
any additional terms or conditions. Notwithstanding the above, nothing
|
||||
herein shall supersede or modify the terms of any separate license agreement
|
||||
you may have executed with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor, except
|
||||
as required for reasonable and customary use in describing the origin of the
|
||||
Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in
|
||||
writing, Licensor provides the Work (and each Contributor provides its
|
||||
Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied, including, without limitation, any
|
||||
warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining
|
||||
the appropriateness of using or redistributing the Work and assume any risks
|
||||
associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory, whether
|
||||
in tort (including negligence), contract, or otherwise, unless required by
|
||||
applicable law (such as deliberate and grossly negligent acts) or agreed to
|
||||
in writing, shall any Contributor be liable to You for damages, including
|
||||
any direct, indirect, special, incidental, or consequential damages of any
|
||||
character arising as a result of this License or out of the use or inability
|
||||
to use the Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all other
|
||||
commercial damages or losses), even if such Contributor has been advised of
|
||||
the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing the Work
|
||||
or Derivative Works thereof, You may choose to offer, and charge a fee for,
|
||||
acceptance of support, warranty, indemnity, or other liability obligations
|
||||
and/or rights consistent with this License. However, in accepting such
|
||||
obligations, You may act only on Your own behalf and on Your sole
|
||||
responsibility, not on behalf of any other Contributor, and only if You
|
||||
agree to indemnify, defend, and hold each Contributor harmless for any
|
||||
liability incurred by, or claims asserted against, such Contributor by
|
||||
reason of your accepting any such warranty or additional liability.
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
# See the OWNERS docs at https://go.k8s.io/owners
|
||||
|
||||
reviewers:
|
||||
- cblecker
|
||||
- ixdy
|
||||
- mengqiy
|
||||
- pwittrock
|
||||
approvers:
|
||||
- cblecker
|
||||
- ixdy
|
||||
- mengqiy
|
||||
- pwittrock
|
|
@ -1,179 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
### Copyright 2010 Manuel Carrasco Moñino. (manolo at apache.org)
|
||||
###
|
||||
### Licensed under the Apache License, Version 2.0.
|
||||
### You may obtain a copy of it at
|
||||
### http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
###
|
||||
### A library for shell scripts which creates reports in jUnit format.
|
||||
### These reports can be used in Jenkins, or any other CI.
|
||||
###
|
||||
### Usage:
|
||||
### - Include this file in your shell script
|
||||
### - Use juLog to call your command any time you want to produce a new report
|
||||
### Usage: juLog <options> command arguments
|
||||
### options:
|
||||
### -class="MyClass" : a class name which will be shown in the junit report
|
||||
### -name="TestName" : the test name which will be shown in the junit report
|
||||
### -error="RegExp" : a regexp which sets the test as failure when the output matches it
|
||||
### -ierror="RegExp" : same as -error but case insensitive
|
||||
### -output="Path" : path to output directory, defaults to "./results"
|
||||
### - Junit reports are left in the folder 'result' under the directory where the script is executed.
|
||||
### - Configure Jenkins to parse junit files from the generated folder
|
||||
###
|
||||
|
||||
asserts=00; errors=0; total=0; content=""
|
||||
date=`which gdate 2>/dev/null || which date`
|
||||
|
||||
# default output folder
|
||||
juDIR=`pwd`/results
|
||||
|
||||
# The name of the suite is calculated based in your script name
|
||||
suite=""
|
||||
|
||||
if LANG=C sed --help 2>&1 | grep -q GNU; then
|
||||
SED="sed"
|
||||
elif which gsed &>/dev/null; then
|
||||
SED="gsed"
|
||||
else
|
||||
echo "Failed to find GNU sed as sed or gsed. If you are on Mac: brew install gnu-sed." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# A wrapper for the eval method witch allows catching seg-faults and use tee
|
||||
errfile=/tmp/evErr.$$.log
|
||||
function eVal() {
|
||||
(eval "$1")
|
||||
# stdout and stderr may currently be inverted (see below) so echo may write to stderr
|
||||
echo "$?" 2>&1 | tr -d "\n" > "${errfile}"
|
||||
}
|
||||
|
||||
# Method to clean old tests
|
||||
function juLogClean() {
|
||||
echo "+++ Removing old junit reports from: ${juDIR} "
|
||||
rm -f "${juDIR}"/junit-*
|
||||
}
|
||||
|
||||
# Execute a command and record its results
|
||||
function juLog() {
|
||||
suite="";
|
||||
errfile=/tmp/evErr.$$.log
|
||||
date=`which gdate 2>/dev/null || which date`
|
||||
asserts=00; errors=0; total=0; content=""
|
||||
|
||||
# parse arguments
|
||||
ya=""; icase=""
|
||||
while [[ -z "$ya" ]]; do
|
||||
case "$1" in
|
||||
-name=*) name=`echo "$1" | ${SED} -e 's/-name=//'`; shift;;
|
||||
-class=*) class=`echo "$1" | ${SED} -e 's/-class=//'`; shift;;
|
||||
-ierror=*) ereg=`echo "$1" | ${SED} -e 's/-ierror=//'`; icase="-i"; shift;;
|
||||
-error=*) ereg=`echo "$1" | ${SED} -e 's/-error=//'`; shift;;
|
||||
-output=*) juDIR=`echo "$1" | ${SED} -e 's/-output=//'`; shift;;
|
||||
*) ya=1;;
|
||||
esac
|
||||
done
|
||||
|
||||
# create output directory
|
||||
mkdir -p "${juDIR}" || exit
|
||||
# use first arg as name if it was not given
|
||||
if [[ -z "${name}" ]]; then
|
||||
name="${asserts}-$1"
|
||||
shift
|
||||
fi
|
||||
|
||||
if [[ "${class}" = "" ]]; then
|
||||
class="default"
|
||||
fi
|
||||
|
||||
suite=${class}
|
||||
|
||||
# calculate command to eval
|
||||
[[ -z "$1" ]] && return
|
||||
cmd="$1"; shift
|
||||
while [[ -n "${1:-}" ]]
|
||||
do
|
||||
cmd="${cmd} \"$1\""
|
||||
shift
|
||||
done
|
||||
|
||||
# eval the command sending output to a file
|
||||
outf=/var/tmp/ju$$.txt
|
||||
errf=/var/tmp/ju$$-err.txt
|
||||
>${outf}
|
||||
echo "" | tee -a ${outf}
|
||||
echo "+++ Running case: ${class}.${name} " | tee -a ${outf}
|
||||
echo "+++ working dir: "`pwd` | tee -a ${outf}
|
||||
echo "+++ command: ${cmd}" | tee -a ${outf}
|
||||
ini=`${date} +%s.%N`
|
||||
# execute the command, temporarily swapping stderr and stdout so they can be tee'd to separate files,
|
||||
# then swapping them back again so that the streams are written correctly for the invoking process
|
||||
( (eVal "${cmd}" | tee -a ${outf}) 3>&1 1>&2 2>&3 | tee ${errf}) 3>&1 1>&2 2>&3
|
||||
evErr=`cat ${errfile}`
|
||||
rm -f ${errfile}
|
||||
end=`${date} +%s.%N`
|
||||
echo "+++ exit code: ${evErr}" | tee -a ${outf}
|
||||
|
||||
# set the appropriate error, based in the exit code and the regex
|
||||
[[ ${evErr} != 0 ]] && err=1 || err=0
|
||||
out=`cat $outf | ${SED} -e 's/^\([^+]\)/| \1/g'`
|
||||
if [ ${err} = 0 -a -n "${ereg:-}" ]; then
|
||||
H=`echo "${out}" | egrep ${icase} "${ereg}"`
|
||||
[[ -n "${H}" ]] && err=1
|
||||
fi
|
||||
[[ ${err} != 0 ]] && echo "+++ error: ${err}" | tee -a ${outf}
|
||||
rm -f ${outf}
|
||||
|
||||
errMsg=`cat ${errf}`
|
||||
rm -f ${errf}
|
||||
# calculate vars
|
||||
asserts=$(($asserts+1))
|
||||
errors=$(($errors+$err))
|
||||
time=`echo "${end} ${ini}" | awk '{print $1 - $2}'`
|
||||
total=`echo "${total} ${time}" | awk '{print $1 + $2}'`
|
||||
|
||||
# write the junit xml report
|
||||
## failure tag
|
||||
[[ ${err} = 0 ]] && failure="" || failure="
|
||||
<failure type=\"ScriptError\" message=\"Script Error\"><![CDATA[${errMsg}]]></failure>
|
||||
"
|
||||
## testcase tag
|
||||
content="${content}
|
||||
<testcase assertions=\"1\" name=\"${name}\" time=\"${time}\" classname=\"${class}\">
|
||||
${failure}
|
||||
<system-err><![CDATA[${errMsg}]]></system-err>
|
||||
</testcase>
|
||||
"
|
||||
## testsuite block
|
||||
|
||||
if [[ -e "${juDIR}/junit_${suite}.xml" ]]; then
|
||||
# file exists. first update the failures count
|
||||
failCount=`${SED} -n "s/.*testsuite.*failures=\"\([0-9]*\)\".*/\1/p" "${juDIR}/junit_${suite}.xml"`
|
||||
errors=$(($failCount+$errors))
|
||||
${SED} -i "0,/failures=\"${failCount}\"/ s/failures=\"${failCount}\"/failures=\"${errors}\"/" "${juDIR}/junit_${suite}.xml"
|
||||
${SED} -i "0,/errors=\"${failCount}\"/ s/errors=\"${failCount}\"/errors=\"${errors}\"/" "${juDIR}/junit_${suite}.xml"
|
||||
|
||||
# file exists. Need to append to it. If we remove the testsuite end tag, we can just add it in after.
|
||||
${SED} -i "s^</testsuite>^^g" ${juDIR}/junit_${suite}.xml ## remove testSuite so we can add it later
|
||||
${SED} -i "s^</testsuites>^^g" ${juDIR}/junit_${suite}.xml
|
||||
cat <<EOF >> "$juDIR/junit_$suite.xml"
|
||||
${content:-}
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
EOF
|
||||
|
||||
else
|
||||
# no file exists. Adding a new file
|
||||
cat <<EOF > "${juDIR}/junit_${suite}.xml"
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<testsuites>
|
||||
<testsuite failures="${errors}" assertions="${assertions:-}" name="${suite}" tests="1" errors="${errors}" time="${total}">
|
||||
${content:-}
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
EOF
|
||||
fi
|
||||
|
||||
return ${err}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue