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