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  }