-
Notifications
You must be signed in to change notification settings - Fork 208
/
fetcher_file.go
100 lines (95 loc) · 1.9 KB
/
fetcher_file.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package fetcher
import (
"errors"
"fmt"
"io"
"os"
"time"
)
// File checks the provided Path, at the provided
// Interval for new Go binaries. When a new binary
// is found it will replace the currently running
// binary.
type File struct {
Path string
Interval time.Duration
// hash is the file modify time and its size
hash string
delay bool
}
// Init sets the Path and Interval options
func (f *File) Init() error {
if f.Path == "" {
return fmt.Errorf("Path required")
}
if f.Interval < 1*time.Second {
f.Interval = 1 * time.Second
}
if err := f.updateHash(); err != nil {
return err
}
return nil
}
// Fetch file from the specified Path
func (f *File) Fetch() (io.Reader, error) {
//only delay after first fetch
if f.delay {
time.Sleep(f.Interval)
}
f.delay = true
lastHash := f.hash
if err := f.updateHash(); err != nil {
return nil, err
}
// no change
if lastHash == f.hash {
return nil, nil
}
// changed!
file, err := os.Open(f.Path)
if err != nil {
return nil, err
}
//check every 1/4s for 5s to
//ensure its not mid-copy
const rate = 250 * time.Millisecond
const total = int(5 * time.Second / rate)
attempt := 1
for {
if attempt == total {
file.Close()
return nil, errors.New("file is currently being changed")
}
attempt++
//sleep
time.Sleep(rate)
//check hash!
if err := f.updateHash(); err != nil {
file.Close()
return nil, err
}
//check until no longer changing
if lastHash == f.hash {
break
}
lastHash = f.hash
}
return file, nil
}
func (f *File) updateHash() error {
file, err := os.Open(f.Path)
if err != nil {
//binary does not exist, skip
if os.IsNotExist(err) {
return nil
}
return fmt.Errorf("Open file error: %s", err)
}
defer file.Close()
s, err := file.Stat()
if err != nil {
return fmt.Errorf("Get file stat error: %s", err)
}
f.hash = fmt.Sprintf("%d|%d", s.ModTime().UnixNano(), s.Size())
return nil
}