github.com/artpar/rclone@v1.67.3/cmd/serve/docker/driver.go (about) 1 package docker 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "math/rand" 8 "os" 9 "path/filepath" 10 "reflect" 11 "sort" 12 "sync" 13 "time" 14 15 "github.com/artpar/rclone/cmd/mountlib" 16 "github.com/artpar/rclone/fs" 17 "github.com/artpar/rclone/fs/config" 18 "github.com/artpar/rclone/lib/atexit" 19 "github.com/artpar/rclone/lib/file" 20 "github.com/artpar/rclone/vfs/vfscommon" 21 "github.com/artpar/rclone/vfs/vfsflags" 22 "github.com/coreos/go-systemd/v22/daemon" 23 ) 24 25 // Driver implements docker driver api 26 type Driver struct { 27 root string 28 volumes map[string]*Volume 29 statePath string 30 dummy bool // disables real mounting 31 mntOpt mountlib.Options 32 vfsOpt vfscommon.Options 33 mu sync.Mutex 34 exitOnce sync.Once 35 hupChan chan os.Signal 36 monChan chan bool // exit if true for exit, refresh if false 37 } 38 39 // NewDriver makes a new docker driver 40 func NewDriver(ctx context.Context, root string, mntOpt *mountlib.Options, vfsOpt *vfscommon.Options, dummy, forgetState bool) (*Driver, error) { 41 // setup directories 42 cacheDir := config.GetCacheDir() 43 err := file.MkdirAll(cacheDir, 0700) 44 if err != nil { 45 return nil, fmt.Errorf("failed to create cache directory: %s: %w", cacheDir, err) 46 } 47 48 //err = file.MkdirAll(root, 0755) 49 if err != nil { 50 return nil, fmt.Errorf("failed to create mount root: %s: %w", root, err) 51 } 52 53 // setup driver state 54 if mntOpt == nil { 55 mntOpt = &mountlib.Opt 56 } 57 if vfsOpt == nil { 58 vfsOpt = &vfsflags.Opt 59 } 60 drv := &Driver{ 61 root: root, 62 statePath: filepath.Join(cacheDir, stateFile), 63 volumes: map[string]*Volume{}, 64 mntOpt: *mntOpt, 65 vfsOpt: *vfsOpt, 66 dummy: dummy, 67 } 68 drv.mntOpt.Daemon = false 69 70 // restore from saved state 71 if !forgetState { 72 if err = drv.restoreState(ctx); err != nil { 73 return nil, fmt.Errorf("failed to restore state: %w", err) 74 } 75 } 76 77 // start mount monitoring 78 drv.hupChan = make(chan os.Signal, 1) 79 drv.monChan = make(chan bool, 1) 80 mountlib.NotifyOnSigHup(drv.hupChan) 81 go drv.monitor() 82 83 // unmount all volumes on exit 84 atexit.Register(func() { 85 drv.exitOnce.Do(drv.Exit) 86 }) 87 88 // notify systemd 89 if _, err := daemon.SdNotify(false, daemon.SdNotifyReady); err != nil { 90 return nil, fmt.Errorf("failed to notify systemd: %w", err) 91 } 92 93 return drv, nil 94 } 95 96 // Exit will unmount all currently mounted volumes 97 func (drv *Driver) Exit() { 98 fs.Debugf(nil, "Unmount all volumes") 99 drv.mu.Lock() 100 defer drv.mu.Unlock() 101 102 reportErr(func() error { 103 _, err := daemon.SdNotify(false, daemon.SdNotifyStopping) 104 return err 105 }()) 106 drv.monChan <- true // ask monitor to exit 107 for _, vol := range drv.volumes { 108 reportErr(vol.unmountAll()) 109 vol.Mounts = []string{} // never persist mounts at exit 110 } 111 reportErr(drv.saveState()) 112 drv.dummy = true // no more mounts 113 } 114 115 // monitor all mounts 116 func (drv *Driver) monitor() { 117 for { 118 // https://stackoverflow.com/questions/19992334/how-to-listen-to-n-channels-dynamic-select-statement 119 monChan := reflect.SelectCase{ 120 Dir: reflect.SelectRecv, 121 Chan: reflect.ValueOf(drv.monChan), 122 } 123 hupChan := reflect.SelectCase{ 124 Dir: reflect.SelectRecv, 125 Chan: reflect.ValueOf(drv.monChan), 126 } 127 sources := []reflect.SelectCase{monChan, hupChan} 128 volumes := []*Volume{nil, nil} 129 130 drv.mu.Lock() 131 for _, vol := range drv.volumes { 132 if vol.mnt.ErrChan != nil { 133 errSource := reflect.SelectCase{ 134 Dir: reflect.SelectRecv, 135 Chan: reflect.ValueOf(vol.mnt.ErrChan), 136 } 137 sources = append(sources, errSource) 138 volumes = append(volumes, vol) 139 } 140 } 141 drv.mu.Unlock() 142 143 fs.Debugf(nil, "Monitoring %d volumes", len(sources)-2) 144 idx, val, _ := reflect.Select(sources) 145 switch idx { 146 case 0: 147 if val.Bool() { 148 fs.Debugf(nil, "Monitoring stopped") 149 return 150 } 151 case 1: 152 // user sent SIGHUP to clear the cache 153 drv.clearCache() 154 default: 155 vol := volumes[idx] 156 if err := val.Interface(); err != nil { 157 fs.Logf(nil, "Volume %q unmounted externally: %v", vol.Name, err) 158 } else { 159 fs.Infof(nil, "Volume %q unmounted externally", vol.Name) 160 } 161 drv.mu.Lock() 162 reportErr(vol.unmountAll()) 163 drv.mu.Unlock() 164 } 165 } 166 } 167 168 // clearCache will clear cache of all volumes 169 func (drv *Driver) clearCache() { 170 fs.Debugf(nil, "Clear all caches") 171 drv.mu.Lock() 172 defer drv.mu.Unlock() 173 174 for _, vol := range drv.volumes { 175 reportErr(vol.clearCache()) 176 } 177 } 178 179 func reportErr(err error) { 180 if err != nil { 181 fs.Errorf("docker plugin", "%v", err) 182 } 183 } 184 185 // Create volume 186 // To use subpath we are limited to defining a new volume definition via alias 187 func (drv *Driver) Create(req *CreateRequest) error { 188 ctx := context.Background() 189 drv.mu.Lock() 190 defer drv.mu.Unlock() 191 192 name := req.Name 193 fs.Debugf(nil, "Create volume %q", name) 194 195 if vol, _ := drv.getVolume(name); vol != nil { 196 return ErrVolumeExists 197 } 198 199 vol, err := newVolume(ctx, name, req.Options, drv) 200 if err != nil { 201 return err 202 } 203 drv.volumes[name] = vol 204 return drv.saveState() 205 } 206 207 // Remove volume 208 func (drv *Driver) Remove(req *RemoveRequest) error { 209 ctx := context.Background() 210 drv.mu.Lock() 211 defer drv.mu.Unlock() 212 vol, err := drv.getVolume(req.Name) 213 if err != nil { 214 return err 215 } 216 if err = vol.remove(ctx); err != nil { 217 return err 218 } 219 delete(drv.volumes, vol.Name) 220 return drv.saveState() 221 } 222 223 // List volumes handled by the driver 224 func (drv *Driver) List() (*ListResponse, error) { 225 drv.mu.Lock() 226 defer drv.mu.Unlock() 227 228 volumeList := drv.listVolumes() 229 fs.Debugf(nil, "List: %v", volumeList) 230 231 res := &ListResponse{ 232 Volumes: []*VolInfo{}, 233 } 234 for _, name := range volumeList { 235 vol := drv.volumes[name] 236 res.Volumes = append(res.Volumes, vol.getInfo()) 237 } 238 return res, nil 239 } 240 241 // Get volume info 242 func (drv *Driver) Get(req *GetRequest) (*GetResponse, error) { 243 drv.mu.Lock() 244 defer drv.mu.Unlock() 245 vol, err := drv.getVolume(req.Name) 246 if err != nil { 247 return nil, err 248 } 249 return &GetResponse{Volume: vol.getInfo()}, nil 250 } 251 252 // Path returns path of the requested volume 253 func (drv *Driver) Path(req *PathRequest) (*PathResponse, error) { 254 drv.mu.Lock() 255 defer drv.mu.Unlock() 256 vol, err := drv.getVolume(req.Name) 257 if err != nil { 258 return nil, err 259 } 260 return &PathResponse{Mountpoint: vol.MountPoint}, nil 261 } 262 263 // Mount volume 264 func (drv *Driver) Mount(req *MountRequest) (*MountResponse, error) { 265 drv.mu.Lock() 266 defer drv.mu.Unlock() 267 vol, err := drv.getVolume(req.Name) 268 if err == nil { 269 err = vol.mount(req.ID) 270 } 271 if err == nil { 272 err = drv.saveState() 273 } 274 if err != nil { 275 return nil, err 276 } 277 return &MountResponse{Mountpoint: vol.MountPoint}, nil 278 } 279 280 // Unmount volume 281 func (drv *Driver) Unmount(req *UnmountRequest) error { 282 drv.mu.Lock() 283 defer drv.mu.Unlock() 284 vol, err := drv.getVolume(req.Name) 285 if err == nil { 286 err = vol.unmount(req.ID) 287 } 288 if err == nil { 289 err = drv.saveState() 290 } 291 return err 292 } 293 294 // getVolume returns volume by name 295 func (drv *Driver) getVolume(name string) (*Volume, error) { 296 vol := drv.volumes[name] 297 if vol == nil { 298 return nil, ErrVolumeNotFound 299 } 300 return vol, nil 301 } 302 303 // listVolumes returns list volume listVolumes 304 func (drv *Driver) listVolumes() []string { 305 names := []string{} 306 for key := range drv.volumes { 307 names = append(names, key) 308 } 309 sort.Strings(names) 310 return names 311 } 312 313 // saveState saves volumes handled by driver to persistent store 314 func (drv *Driver) saveState() error { 315 volumeList := drv.listVolumes() 316 fs.Debugf(nil, "Save state %v to %s", volumeList, drv.statePath) 317 318 state := []*Volume{} 319 for _, key := range volumeList { 320 vol := drv.volumes[key] 321 vol.prepareState() 322 state = append(state, vol) 323 } 324 325 data, err := json.Marshal(state) 326 if err != nil { 327 return fmt.Errorf("failed to marshal state: %w", err) 328 } 329 330 ctx := context.Background() 331 retries := fs.GetConfig(ctx).LowLevelRetries 332 for i := 0; i <= retries; i++ { 333 err = os.WriteFile(drv.statePath, data, 0600) 334 if err == nil { 335 return nil 336 } 337 time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 338 } 339 return fmt.Errorf("failed to save state: %w", err) 340 } 341 342 // restoreState recreates volumes from saved driver state 343 func (drv *Driver) restoreState(ctx context.Context) error { 344 fs.Debugf(nil, "Restore state from %s", drv.statePath) 345 346 data, err := os.ReadFile(drv.statePath) 347 if os.IsNotExist(err) { 348 return nil 349 } 350 351 var state []*Volume 352 if err == nil { 353 err = json.Unmarshal(data, &state) 354 } 355 if err != nil { 356 fs.Logf(nil, "Failed to restore plugin state: %v", err) 357 return nil 358 } 359 360 for _, vol := range state { 361 if err := vol.restoreState(ctx, drv); err != nil { 362 fs.Logf(nil, "Failed to restore volume %q: %v", vol.Name, err) 363 continue 364 } 365 drv.volumes[vol.Name] = vol 366 } 367 return nil 368 }