Skip to content

Commit b8a7309

Browse files
committed
feat: integrate fsnotify to watch local file changes
implements the helm/chartmuseum#545 Signed-off-by: scnace <scbizu@gmail.com>
1 parent 994ca76 commit b8a7309

4 files changed

Lines changed: 92 additions & 6 deletions

File tree

event.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package storage
2+
3+
// EventType represents the type of event kind
4+
type EventType uint8
5+
6+
const (
7+
EventPutObject EventType = iota + 1
8+
EventDeleteObject
9+
)

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/aliyun/aliyun-oss-go-sdk v2.2.0+incompatible
1010
github.com/aws/aws-sdk-go v1.43.16
1111
github.com/baidubce/bce-sdk-go v0.9.105
12+
github.com/fsnotify/fsnotify v1.5.1
1213
github.com/gophercloud/gophercloud v0.24.0
1314
github.com/oracle/oci-go-sdk v24.3.0+incompatible
1415
github.com/stretchr/testify v1.7.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m
137137
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
138138
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
139139
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
140+
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
141+
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
140142
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
141143
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
142144
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=

local.go

Lines changed: 80 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,93 @@ limitations under the License.
1717
package storage
1818

1919
import (
20+
"fmt"
2021
"io/ioutil"
2122
"os"
23+
"sync"
2224

2325
pathutil "path"
2426
"path/filepath"
27+
28+
"github.com/fsnotify/fsnotify"
2529
)
2630

2731
// LocalFilesystemBackend is a storage backend for local filesystem storage
2832
type LocalFilesystemBackend struct {
2933
RootDirectory string
3034
}
3135

36+
type NewLocalFilesystemBackendOption struct {
37+
notifier map[EventType]func()
38+
}
39+
40+
var watcherOnce sync.Once
41+
var globalWatcher *fsnotify.Watcher
42+
43+
func WithEventNotifier(e EventType, fn func()) func(*NewLocalFilesystemBackendOption) {
44+
return func(option *NewLocalFilesystemBackendOption) {
45+
if option.notifier == nil {
46+
option.notifier = make(map[EventType]func())
47+
}
48+
option.notifier[e] = fn
49+
}
50+
}
51+
3252
// NewLocalFilesystemBackend creates a new instance of LocalFilesystemBackend
33-
func NewLocalFilesystemBackend(rootDirectory string) *LocalFilesystemBackend {
53+
func NewLocalFilesystemBackend(rootDirectory string, opts ...func(*NewLocalFilesystemBackendOption)) *LocalFilesystemBackend {
54+
var option NewLocalFilesystemBackendOption
55+
for _, opt := range opts {
56+
opt(&option)
57+
}
3458
absPath, err := filepath.Abs(rootDirectory)
3559
if err != nil {
3660
panic(err)
3761
}
38-
b := &LocalFilesystemBackend{RootDirectory: absPath}
62+
b := &LocalFilesystemBackend{
63+
RootDirectory: absPath,
64+
}
65+
66+
watcherOnce.Do(func() {
67+
globalWatcher, err = fsnotify.NewWatcher()
68+
if err != nil {
69+
panic(err)
70+
}
71+
// since it is a longTerm watcher , we do not need to close it
72+
go func() {
73+
for {
74+
select {
75+
case event, ok := <-globalWatcher.Events:
76+
if !ok {
77+
continue
78+
}
79+
if event.Op&fsnotify.Write == fsnotify.Write {
80+
if option.notifier != nil {
81+
for _, op := range option.notifier {
82+
op()
83+
}
84+
}
85+
}
86+
case _, ok := <-globalWatcher.Errors:
87+
if !ok {
88+
continue
89+
}
90+
}
91+
}
92+
}()
93+
94+
if err := globalWatcher.Add(b.RootDirectory); err != nil {
95+
panic(err)
96+
}
97+
})
98+
3999
return b
40100
}
41101

102+
func (b LocalFilesystemBackend) AddWatherPath(path string) error {
103+
// TODO: to ensure that we should lock here ?
104+
return globalWatcher.Add(path)
105+
}
106+
42107
// ListObjects lists all objects in root directory (depth 1)
43108
func (b LocalFilesystemBackend) ListObjects(prefix string) ([]Object, error) {
44109
var objects []Object
@@ -84,6 +149,7 @@ func (b LocalFilesystemBackend) PutObject(path string, content []byte) error {
84149
_, err := os.Stat(folderPath)
85150
if err != nil {
86151
if os.IsNotExist(err) {
152+
// NOTE: works for dynamic depth tenant
87153
err := os.MkdirAll(folderPath, 0774)
88154
if err != nil {
89155
return err
@@ -95,17 +161,25 @@ func (b LocalFilesystemBackend) PutObject(path string, content []byte) error {
95161
if err != nil {
96162
return err
97163
}
164+
// also adds the fsnotify watcher path
165+
if err := b.AddWatherPath(folderPath); err != nil {
166+
return err
167+
}
98168
} else {
99169
return err
100170
}
101171
}
102-
err = ioutil.WriteFile(fullpath, content, 0644)
103-
return err
172+
if err = ioutil.WriteFile(fullpath, content, 0644); err != nil {
173+
return err
174+
}
175+
return nil
104176
}
105177

106178
// DeleteObject removes an object from root directory
107179
func (b LocalFilesystemBackend) DeleteObject(path string) error {
108180
fullpath := pathutil.Join(b.RootDirectory, path)
109-
err := os.Remove(fullpath)
110-
return err
181+
if err := os.Remove(fullpath); err != nil {
182+
return fmt.Errorf("failed to delete object %s: %w", path, err)
183+
}
184+
return nil
111185
}

0 commit comments

Comments
 (0)