github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/test/testvol/main.go (about)

     1  package main
     2  
     3  import (
     4  	"io/ioutil"
     5  	"os"
     6  	"path/filepath"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/docker/go-plugins-helpers/volume"
    11  	"github.com/pkg/errors"
    12  	"github.com/sirupsen/logrus"
    13  	"github.com/spf13/cobra"
    14  )
    15  
    16  var rootCmd = &cobra.Command{
    17  	Use:   "testvol",
    18  	Short: "testvol - volume plugin for Podman",
    19  	Long:  `Creates simple directory volumes using the Volume Plugin API for testing volume plugin functionality`,
    20  	RunE: func(cmd *cobra.Command, args []string) error {
    21  		return startServer(config.sockName)
    22  	},
    23  	PersistentPreRunE: before,
    24  }
    25  
    26  // Configuration for the volume plugin
    27  type cliConfig struct {
    28  	logLevel string
    29  	sockName string
    30  	path     string
    31  }
    32  
    33  // Default configuration is stored here. Will be overwritten by flags.
    34  var config = cliConfig{
    35  	logLevel: "error",
    36  	sockName: "test-volume-plugin",
    37  }
    38  
    39  func init() {
    40  	rootCmd.Flags().StringVar(&config.sockName, "sock-name", config.sockName, "Name of unix socket for plugin")
    41  	rootCmd.Flags().StringVar(&config.path, "path", "", "Path to initialize state and mount points")
    42  	rootCmd.PersistentFlags().StringVar(&config.logLevel, "log-level", config.logLevel, "Log messages including and over the specified level: debug, info, warn, error, fatal, panic")
    43  }
    44  
    45  func before(cmd *cobra.Command, args []string) error {
    46  	if config.logLevel == "" {
    47  		config.logLevel = "error"
    48  	}
    49  
    50  	level, err := logrus.ParseLevel(config.logLevel)
    51  	if err != nil {
    52  		return err
    53  	}
    54  
    55  	logrus.SetLevel(level)
    56  
    57  	return nil
    58  }
    59  
    60  func main() {
    61  	if err := rootCmd.Execute(); err != nil {
    62  		logrus.Errorf("Running volume plugin: %v", err)
    63  		os.Exit(1)
    64  	}
    65  
    66  	os.Exit(0)
    67  }
    68  
    69  // startServer runs the HTTP server and responds to requests
    70  func startServer(socketPath string) error {
    71  	logrus.Debugf("Starting server...")
    72  
    73  	if config.path == "" {
    74  		path, err := ioutil.TempDir("", "test_volume_plugin")
    75  		if err != nil {
    76  			return errors.Wrapf(err, "error getting directory for plugin")
    77  		}
    78  		config.path = path
    79  	} else {
    80  		pathStat, err := os.Stat(config.path)
    81  		if err != nil {
    82  			return errors.Wrapf(err, "unable to access requested plugin state directory")
    83  		}
    84  		if !pathStat.IsDir() {
    85  			return errors.Errorf("cannot use %v as plugin state dir as it is not a directory", config.path)
    86  		}
    87  	}
    88  
    89  	handle := makeDirDriver(config.path)
    90  	logrus.Infof("Using %s for volume path", config.path)
    91  
    92  	server := volume.NewHandler(handle)
    93  	if err := server.ServeUnix(socketPath, 0); err != nil {
    94  		return errors.Wrapf(err, "error starting server")
    95  	}
    96  	return nil
    97  }
    98  
    99  // DirDriver is a trivial volume driver implementation.
   100  // the volumes field maps name to volume
   101  type DirDriver struct {
   102  	lock        sync.Mutex
   103  	volumesPath string
   104  	volumes     map[string]*dirVol
   105  }
   106  
   107  type dirVol struct {
   108  	name       string
   109  	path       string
   110  	options    map[string]string
   111  	mounts     map[string]bool
   112  	createTime time.Time
   113  }
   114  
   115  // Make a new DirDriver.
   116  func makeDirDriver(path string) volume.Driver {
   117  	drv := new(DirDriver)
   118  	drv.volumesPath = path
   119  	drv.volumes = make(map[string]*dirVol)
   120  
   121  	return drv
   122  }
   123  
   124  // Capabilities returns the capabilities of the driver.
   125  func (d *DirDriver) Capabilities() *volume.CapabilitiesResponse {
   126  	logrus.Infof("Hit Capabilities() endpoint")
   127  
   128  	return &volume.CapabilitiesResponse{
   129  		Capabilities: volume.Capability{
   130  			Scope: "local",
   131  		},
   132  	}
   133  }
   134  
   135  // Create creates a volume.
   136  func (d *DirDriver) Create(opts *volume.CreateRequest) error {
   137  	d.lock.Lock()
   138  	defer d.lock.Unlock()
   139  
   140  	logrus.Infof("Hit Create() endpoint")
   141  
   142  	if _, exists := d.volumes[opts.Name]; exists {
   143  		return errors.Errorf("volume with name %s already exists", opts.Name)
   144  	}
   145  
   146  	newVol := new(dirVol)
   147  	newVol.name = opts.Name
   148  	newVol.mounts = make(map[string]bool)
   149  	newVol.options = make(map[string]string)
   150  	newVol.createTime = time.Now()
   151  	for k, v := range opts.Options {
   152  		newVol.options[k] = v
   153  	}
   154  
   155  	volPath := filepath.Join(d.volumesPath, opts.Name)
   156  	if err := os.Mkdir(volPath, 0755); err != nil {
   157  		return errors.Wrapf(err, "error making volume directory")
   158  	}
   159  	newVol.path = volPath
   160  
   161  	d.volumes[opts.Name] = newVol
   162  
   163  	logrus.Debugf("Made volume with name %s and path %s", newVol.name, newVol.path)
   164  
   165  	return nil
   166  }
   167  
   168  // List lists all volumes available.
   169  func (d *DirDriver) List() (*volume.ListResponse, error) {
   170  	d.lock.Lock()
   171  	defer d.lock.Unlock()
   172  
   173  	logrus.Infof("Hit List() endpoint")
   174  
   175  	vols := new(volume.ListResponse)
   176  	vols.Volumes = []*volume.Volume{}
   177  
   178  	for _, vol := range d.volumes {
   179  		newVol := new(volume.Volume)
   180  		newVol.Name = vol.name
   181  		newVol.Mountpoint = vol.path
   182  		newVol.CreatedAt = vol.createTime.String()
   183  		vols.Volumes = append(vols.Volumes, newVol)
   184  		logrus.Debugf("Adding volume %s to list response", newVol.Name)
   185  	}
   186  
   187  	return vols, nil
   188  }
   189  
   190  // Get retrieves a single volume.
   191  func (d *DirDriver) Get(req *volume.GetRequest) (*volume.GetResponse, error) {
   192  	d.lock.Lock()
   193  	defer d.lock.Unlock()
   194  
   195  	logrus.Infof("Hit Get() endpoint")
   196  
   197  	vol, exists := d.volumes[req.Name]
   198  	if !exists {
   199  		logrus.Debugf("Did not find volume %s", req.Name)
   200  		return nil, errors.Errorf("no volume with name %s found", req.Name)
   201  	}
   202  
   203  	logrus.Debugf("Found volume %s", req.Name)
   204  
   205  	resp := new(volume.GetResponse)
   206  	resp.Volume = new(volume.Volume)
   207  	resp.Volume.Name = vol.name
   208  	resp.Volume.Mountpoint = vol.path
   209  	resp.Volume.CreatedAt = vol.createTime.String()
   210  
   211  	return resp, nil
   212  }
   213  
   214  // Remove removes a single volume.
   215  func (d *DirDriver) Remove(req *volume.RemoveRequest) error {
   216  	d.lock.Lock()
   217  	defer d.lock.Unlock()
   218  
   219  	logrus.Infof("Hit Remove() endpoint")
   220  
   221  	vol, exists := d.volumes[req.Name]
   222  	if !exists {
   223  		logrus.Debugf("Did not find volume %s", req.Name)
   224  		return errors.Errorf("no volume with name %s found", req.Name)
   225  	}
   226  	logrus.Debugf("Found volume %s", req.Name)
   227  
   228  	if len(vol.mounts) > 0 {
   229  		logrus.Debugf("Cannot remove %s, is mounted", req.Name)
   230  		return errors.Errorf("volume %s is mounted and cannot be removed", req.Name)
   231  	}
   232  
   233  	delete(d.volumes, req.Name)
   234  
   235  	if err := os.RemoveAll(vol.path); err != nil {
   236  		return errors.Wrapf(err, "error removing mountpoint of volume %s", req.Name)
   237  	}
   238  
   239  	logrus.Debugf("Removed volume %s", req.Name)
   240  
   241  	return nil
   242  }
   243  
   244  // Path returns the path a single volume is mounted at.
   245  func (d *DirDriver) Path(req *volume.PathRequest) (*volume.PathResponse, error) {
   246  	d.lock.Lock()
   247  	defer d.lock.Unlock()
   248  
   249  	logrus.Infof("Hit Path() endpoint")
   250  
   251  	// TODO: Should we return error if not mounted?
   252  
   253  	vol, exists := d.volumes[req.Name]
   254  	if !exists {
   255  		logrus.Debugf("Cannot locate volume %s", req.Name)
   256  		return nil, errors.Errorf("no volume with name %s found", req.Name)
   257  	}
   258  
   259  	return &volume.PathResponse{
   260  		Mountpoint: vol.path,
   261  	}, nil
   262  }
   263  
   264  // Mount mounts the volume.
   265  func (d *DirDriver) Mount(req *volume.MountRequest) (*volume.MountResponse, error) {
   266  	d.lock.Lock()
   267  	defer d.lock.Unlock()
   268  
   269  	logrus.Infof("Hit Mount() endpoint")
   270  
   271  	vol, exists := d.volumes[req.Name]
   272  	if !exists {
   273  		logrus.Debugf("Cannot locate volume %s", req.Name)
   274  		return nil, errors.Errorf("no volume with name %s found", req.Name)
   275  	}
   276  
   277  	vol.mounts[req.ID] = true
   278  
   279  	return &volume.MountResponse{
   280  		Mountpoint: vol.path,
   281  	}, nil
   282  }
   283  
   284  // Unmount unmounts the volume.
   285  func (d *DirDriver) Unmount(req *volume.UnmountRequest) error {
   286  	d.lock.Lock()
   287  	defer d.lock.Unlock()
   288  
   289  	logrus.Infof("Hit Unmount() endpoint")
   290  
   291  	vol, exists := d.volumes[req.Name]
   292  	if !exists {
   293  		logrus.Debugf("Cannot locate volume %s", req.Name)
   294  		return errors.Errorf("no volume with name %s found", req.Name)
   295  	}
   296  
   297  	mount := vol.mounts[req.ID]
   298  	if !mount {
   299  		logrus.Debugf("Volume %s is not mounted by %s", req.Name, req.ID)
   300  		return errors.Errorf("volume %s is not mounted by %s", req.Name, req.ID)
   301  	}
   302  
   303  	delete(vol.mounts, req.ID)
   304  
   305  	return nil
   306  }