Removed panic and added logging
This commit is contained in:
parent
43bad54292
commit
448e9dc3ce
|
|
@ -18,10 +18,10 @@ package snapshot
|
||||||
|
|
||||||
type LayeredMap struct {
|
type LayeredMap struct {
|
||||||
layers []map[string]string
|
layers []map[string]string
|
||||||
hasher func(string) string
|
hasher func(string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLayeredMap(h func(string) string) *LayeredMap {
|
func NewLayeredMap(h func(string) (string, error)) *LayeredMap {
|
||||||
l := LayeredMap{
|
l := LayeredMap{
|
||||||
hasher: h,
|
hasher: h,
|
||||||
}
|
}
|
||||||
|
|
@ -42,12 +42,15 @@ func (l *LayeredMap) Get(s string) (string, bool) {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LayeredMap) MaybeAdd(s string) bool {
|
func (l *LayeredMap) MaybeAdd(s string) (bool, error) {
|
||||||
oldV, ok := l.Get(s)
|
oldV, ok := l.Get(s)
|
||||||
newV := l.hasher(s)
|
newV, err := l.hasher(s)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
if ok && newV == oldV {
|
if ok && newV == oldV {
|
||||||
return false
|
return false, nil
|
||||||
}
|
}
|
||||||
l.layers[len(l.layers)-1][s] = newV
|
l.layers[len(l.layers)-1][s] = newV
|
||||||
return true
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,12 +32,11 @@ import (
|
||||||
type Snapshotter struct {
|
type Snapshotter struct {
|
||||||
l *LayeredMap
|
l *LayeredMap
|
||||||
directory string
|
directory string
|
||||||
snapshots []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSnapshotter creates a new snapshotter rooted at d
|
// NewSnapshotter creates a new snapshotter rooted at d
|
||||||
func NewSnapshotter(l *LayeredMap, d string) *Snapshotter {
|
func NewSnapshotter(l *LayeredMap, d string) *Snapshotter {
|
||||||
return &Snapshotter{l: l, directory: d, snapshots: []string{}}
|
return &Snapshotter{l: l, directory: d}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes a new snapshotter
|
// Init initializes a new snapshotter
|
||||||
|
|
@ -48,51 +47,76 @@ func (s *Snapshotter) Init() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TakeSnapshot takes a snapshot of the filesystem, avoiding directories in the whitelist
|
// TakeSnapshot takes a snapshot of the filesystem, avoiding directories in the whitelist, and creates
|
||||||
// It stores changed files in a tar, and returns the contents of this tar at the end
|
// a tarball of the changed files. Return contents of the tarball, and whether or not any files were changed
|
||||||
func (s *Snapshotter) TakeSnapshot() ([]byte, error) {
|
func (s *Snapshotter) TakeSnapshot() ([]byte, bool, error) {
|
||||||
|
|
||||||
buf := bytes.NewBuffer([]byte{})
|
buf := bytes.NewBuffer([]byte{})
|
||||||
added, err := s.snapShotFS(buf)
|
filesAdded, err := s.snapShotFS(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, filesAdded, err
|
||||||
}
|
}
|
||||||
if !added {
|
contents, err := ioutil.ReadAll(buf)
|
||||||
logrus.Infof("No files were changed in this command, this layer will not be appended.")
|
if err != nil {
|
||||||
|
return nil, filesAdded, err
|
||||||
|
}
|
||||||
|
return contents, filesAdded, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TakeSnapshotOfFiles takes a snapshot of specific files
|
||||||
|
// Used for ADD/COPY commands, when we know which files have changed
|
||||||
|
func (s *Snapshotter) TakeSnapshotOfFiles(files []string) ([]byte, error) {
|
||||||
|
logrus.Infof("Taking snapshot of files %s", files)
|
||||||
|
s.l.Snapshot()
|
||||||
|
if len(files) == 0 {
|
||||||
|
logrus.Info("No files changed in this command, skipping snapshotting.")
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
buf := bytes.NewBuffer([]byte{})
|
||||||
return nil, err
|
w := tar.NewWriter(buf)
|
||||||
}
|
defer w.Close()
|
||||||
// Add buffer contents until buffer is empty
|
for _, file := range files {
|
||||||
var contents []byte
|
info, err := os.Stat(file)
|
||||||
for {
|
if err != nil {
|
||||||
next := buf.Next(buf.Len())
|
return nil, err
|
||||||
if len(next) == 0 {
|
}
|
||||||
break
|
if util.PathInWhitelist(file, s.directory) {
|
||||||
|
logrus.Debugf("Not adding %s to layer, as it is whitelisted", file)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Only add to the tar if we add it to the layeredmap.
|
||||||
|
maybeAdd, err := s.l.MaybeAdd(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if maybeAdd {
|
||||||
|
util.AddToTar(file, info, w)
|
||||||
}
|
}
|
||||||
contents = append(contents, next...)
|
|
||||||
}
|
}
|
||||||
return contents, nil
|
return ioutil.ReadAll(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Snapshotter) snapShotFS(f io.Writer) (bool, error) {
|
func (s *Snapshotter) snapShotFS(f io.Writer) (bool, error) {
|
||||||
s.l.Snapshot()
|
s.l.Snapshot()
|
||||||
added := false
|
filesAdded := false
|
||||||
w := tar.NewWriter(f)
|
w := tar.NewWriter(f)
|
||||||
defer w.Close()
|
defer w.Close()
|
||||||
|
|
||||||
err := filepath.Walk(s.directory, func(path string, info os.FileInfo, err error) error {
|
err := filepath.Walk(s.directory, func(path string, info os.FileInfo, err error) error {
|
||||||
if util.PathInWhitelist(path, s.directory) {
|
if util.PathInWhitelist(path, s.directory) {
|
||||||
|
logrus.Debugf("Not adding %s to layer, as it's whitelisted", path)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only add to the tar if we add it to the layeredmap.
|
// Only add to the tar if we add it to the layeredmap.
|
||||||
if s.l.MaybeAdd(path) {
|
maybeAdd, err := s.l.MaybeAdd(path)
|
||||||
added = true
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if maybeAdd {
|
||||||
|
filesAdded = true
|
||||||
return util.AddToTar(path, info, w)
|
return util.AddToTar(path, info, w)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
return added, err
|
return filesAdded, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,28 +38,36 @@ func TestSnapshotFileChange(t *testing.T) {
|
||||||
// Make some changes to the filesystem
|
// Make some changes to the filesystem
|
||||||
newFiles := map[string]string{
|
newFiles := map[string]string{
|
||||||
"foo": "newbaz1",
|
"foo": "newbaz1",
|
||||||
|
"bar/bat": "baz",
|
||||||
"workspace/bat": "bat",
|
"workspace/bat": "bat",
|
||||||
}
|
}
|
||||||
if err := testutil.SetupFiles(testDir, newFiles); err != nil {
|
if err := testutil.SetupFiles(testDir, newFiles); err != nil {
|
||||||
t.Fatalf("Error setting up fs: %s", err)
|
t.Fatalf("Error setting up fs: %s", err)
|
||||||
}
|
}
|
||||||
// Take another snapshot
|
// Take another snapshot
|
||||||
contents, err := snapshotter.TakeSnapshot()
|
contents, filesAdded, err := snapshotter.TakeSnapshot()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error taking snapshot of fs: %s", err)
|
t.Fatalf("Error taking snapshot of fs: %s", err)
|
||||||
}
|
}
|
||||||
|
if !filesAdded {
|
||||||
|
t.Fatal("No files added to snapshot.")
|
||||||
|
}
|
||||||
// Check contents of the snapshot, make sure contents is equivalent to snapshotFiles
|
// Check contents of the snapshot, make sure contents is equivalent to snapshotFiles
|
||||||
reader := bytes.NewReader(contents)
|
reader := bytes.NewReader(contents)
|
||||||
tr := tar.NewReader(reader)
|
tr := tar.NewReader(reader)
|
||||||
fooPath := filepath.Join(testDir, "foo")
|
fooPath := filepath.Join(testDir, "foo")
|
||||||
|
batPath := filepath.Join(testDir, "bar/bat")
|
||||||
snapshotFiles := map[string]string{
|
snapshotFiles := map[string]string{
|
||||||
fooPath: "newbaz1",
|
fooPath: "newbaz1",
|
||||||
|
batPath: "baz",
|
||||||
}
|
}
|
||||||
|
numFiles := 0
|
||||||
for {
|
for {
|
||||||
hdr, err := tr.Next()
|
hdr, err := tr.Next()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
numFiles++
|
||||||
if _, isFile := snapshotFiles[hdr.Name]; !isFile {
|
if _, isFile := snapshotFiles[hdr.Name]; !isFile {
|
||||||
t.Fatalf("File %s unexpectedly in tar", hdr.Name)
|
t.Fatalf("File %s unexpectedly in tar", hdr.Name)
|
||||||
}
|
}
|
||||||
|
|
@ -68,6 +76,9 @@ func TestSnapshotFileChange(t *testing.T) {
|
||||||
t.Fatalf("Contents of %s incorrect, expected: %s, actual: %s", hdr.Name, snapshotFiles[hdr.Name], string(contents))
|
t.Fatalf("Contents of %s incorrect, expected: %s, actual: %s", hdr.Name, snapshotFiles[hdr.Name], string(contents))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if numFiles != 2 {
|
||||||
|
t.Fatalf("Incorrect number of files were added, expected: 2, actual: %v", numFiles)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSnapshotChangePermissions(t *testing.T) {
|
func TestSnapshotChangePermissions(t *testing.T) {
|
||||||
|
|
@ -82,21 +93,26 @@ func TestSnapshotChangePermissions(t *testing.T) {
|
||||||
t.Fatalf("Error changing permissions on %s: %v", batPath, err)
|
t.Fatalf("Error changing permissions on %s: %v", batPath, err)
|
||||||
}
|
}
|
||||||
// Take another snapshot
|
// Take another snapshot
|
||||||
contents, err := snapshotter.TakeSnapshot()
|
contents, filesAdded, err := snapshotter.TakeSnapshot()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error taking snapshot of fs: %s", err)
|
t.Fatalf("Error taking snapshot of fs: %s", err)
|
||||||
}
|
}
|
||||||
|
if !filesAdded {
|
||||||
|
t.Fatal("No files added to snapshot.")
|
||||||
|
}
|
||||||
// Check contents of the snapshot, make sure contents is equivalent to snapshotFiles
|
// Check contents of the snapshot, make sure contents is equivalent to snapshotFiles
|
||||||
reader := bytes.NewReader(contents)
|
reader := bytes.NewReader(contents)
|
||||||
tr := tar.NewReader(reader)
|
tr := tar.NewReader(reader)
|
||||||
snapshotFiles := map[string]string{
|
snapshotFiles := map[string]string{
|
||||||
batPath: "baz2",
|
batPath: "baz2",
|
||||||
}
|
}
|
||||||
|
numFiles := 0
|
||||||
for {
|
for {
|
||||||
hdr, err := tr.Next()
|
hdr, err := tr.Next()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
numFiles++
|
||||||
if _, isFile := snapshotFiles[hdr.Name]; !isFile {
|
if _, isFile := snapshotFiles[hdr.Name]; !isFile {
|
||||||
t.Fatalf("File %s unexpectedly in tar", hdr.Name)
|
t.Fatalf("File %s unexpectedly in tar", hdr.Name)
|
||||||
}
|
}
|
||||||
|
|
@ -105,6 +121,57 @@ func TestSnapshotChangePermissions(t *testing.T) {
|
||||||
t.Fatalf("Contents of %s incorrect, expected: %s, actual: %s", hdr.Name, snapshotFiles[hdr.Name], string(contents))
|
t.Fatalf("Contents of %s incorrect, expected: %s, actual: %s", hdr.Name, snapshotFiles[hdr.Name], string(contents))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if numFiles != 1 {
|
||||||
|
t.Fatalf("Incorrect number of files were added, expected: 1, got: %v", numFiles)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSnapshotFiles(t *testing.T) {
|
||||||
|
testDir, snapshotter, err := setUpTestDir()
|
||||||
|
defer os.RemoveAll(testDir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// Make some changes to the filesystem
|
||||||
|
newFiles := map[string]string{
|
||||||
|
"foo": "newbaz1",
|
||||||
|
"workspace/file": "bat",
|
||||||
|
}
|
||||||
|
if err := testutil.SetupFiles(testDir, newFiles); err != nil {
|
||||||
|
t.Fatalf("Error setting up fs: %s", err)
|
||||||
|
}
|
||||||
|
filesToSnapshot := []string{
|
||||||
|
filepath.Join(testDir, "foo"),
|
||||||
|
filepath.Join(testDir, "workspace/file"),
|
||||||
|
}
|
||||||
|
contents, err := snapshotter.TakeSnapshotOfFiles(filesToSnapshot)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
expectedContents := map[string]string{
|
||||||
|
filepath.Join(testDir, "foo"): "newbaz1",
|
||||||
|
}
|
||||||
|
// Check contents of the snapshot, make sure contents is equivalent to snapshotFiles
|
||||||
|
reader := bytes.NewReader(contents)
|
||||||
|
tr := tar.NewReader(reader)
|
||||||
|
numFiles := 0
|
||||||
|
for {
|
||||||
|
hdr, err := tr.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
numFiles = numFiles + 1
|
||||||
|
if _, isFile := expectedContents[hdr.Name]; !isFile {
|
||||||
|
t.Fatalf("File %s unexpectedly in tar", hdr.Name)
|
||||||
|
}
|
||||||
|
contents, _ := ioutil.ReadAll(tr)
|
||||||
|
if string(contents) != expectedContents[hdr.Name] {
|
||||||
|
t.Fatalf("Contents of %s incorrect, expected: %s, actual: %s", hdr.Name, expectedContents[hdr.Name], string(contents))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if numFiles != 1 {
|
||||||
|
t.Fatalf("%s was not added.", filepath.Join(testDir, "foo"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEmptySnapshot(t *testing.T) {
|
func TestEmptySnapshot(t *testing.T) {
|
||||||
|
|
@ -114,13 +181,13 @@ func TestEmptySnapshot(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// Take snapshot with no changes
|
// Take snapshot with no changes
|
||||||
contents, err := snapshotter.TakeSnapshot()
|
_, filesAdded, err := snapshotter.TakeSnapshot()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error taking snapshot of fs: %s", err)
|
t.Fatalf("Error taking snapshot of fs: %s", err)
|
||||||
}
|
}
|
||||||
// Since we took a snapshot with no changes, contents should be nil
|
// Since we took a snapshot with no changes, contents should be nil
|
||||||
if contents != nil {
|
if filesAdded {
|
||||||
t.Fatal("Contents should be nil, since no changes to the filesystem were made.")
|
t.Fatal("Files added even though no changes to file system were made.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,12 +36,12 @@ func SetLogLevel(logLevel string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hasher returns a hash function, used in snapshotting to determine if a file has changed
|
// Hasher returns a hash function, used in snapshotting to determine if a file has changed
|
||||||
func Hasher() func(string) string {
|
func Hasher() func(string) (string, error) {
|
||||||
hasher := func(p string) string {
|
hasher := func(p string) (string, error) {
|
||||||
h := md5.New()
|
h := md5.New()
|
||||||
fi, err := os.Lstat(p)
|
fi, err := os.Lstat(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return "", err
|
||||||
}
|
}
|
||||||
h.Write([]byte(fi.Mode().String()))
|
h.Write([]byte(fi.Mode().String()))
|
||||||
h.Write([]byte(fi.ModTime().String()))
|
h.Write([]byte(fi.ModTime().String()))
|
||||||
|
|
@ -49,15 +49,15 @@ func Hasher() func(string) string {
|
||||||
if fi.Mode().IsRegular() {
|
if fi.Mode().IsRegular() {
|
||||||
f, err := os.Open(p)
|
f, err := os.Open(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return "", err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
if _, err := io.Copy(h, f); err != nil {
|
if _, err := io.Copy(h, f); err != nil {
|
||||||
panic(err)
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return hex.EncodeToString(h.Sum(nil))
|
return hex.EncodeToString(h.Sum(nil)), nil
|
||||||
}
|
}
|
||||||
return hasher
|
return hasher
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue