mirror of
				https://github.com/containers/podman.git
				synced 2025-11-04 08:56:05 +08:00 
			
		
		
		
	Package `io/ioutil` was deprecated in golang 1.16, preventing podman from building under Fedora 37. Fortunately, functionality identical replacements are provided by the packages `io` and `os`. Replace all usage of all `io/ioutil` symbols with appropriate substitutions according to the golang docs. Signed-off-by: Chris Evich <cevich@redhat.com>
		
			
				
	
	
		
			313 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			313 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package main
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"os"
 | 
						|
	"path/filepath"
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/docker/go-plugins-helpers/volume"
 | 
						|
	"github.com/sirupsen/logrus"
 | 
						|
	"github.com/spf13/cobra"
 | 
						|
)
 | 
						|
 | 
						|
var rootCmd = &cobra.Command{
 | 
						|
	Use:               "testvol",
 | 
						|
	Short:             "testvol - volume plugin for Podman testing",
 | 
						|
	PersistentPreRunE: before,
 | 
						|
	SilenceUsage:      true,
 | 
						|
}
 | 
						|
 | 
						|
var serveCmd = &cobra.Command{
 | 
						|
	Use:   "serve",
 | 
						|
	Short: "serve the volume plugin on the unix socket",
 | 
						|
	Long:  `Creates simple directory volumes using the Volume Plugin API for testing volume plugin functionality`,
 | 
						|
	Args:  cobra.NoArgs,
 | 
						|
	RunE: func(cmd *cobra.Command, args []string) error {
 | 
						|
		return startServer(config.sockName)
 | 
						|
	},
 | 
						|
}
 | 
						|
 | 
						|
// Configuration for the volume plugin
 | 
						|
type cliConfig struct {
 | 
						|
	logLevel string
 | 
						|
	sockName string
 | 
						|
	path     string
 | 
						|
}
 | 
						|
 | 
						|
// Default configuration is stored here. Will be overwritten by flags.
 | 
						|
var config = cliConfig{
 | 
						|
	logLevel: "error",
 | 
						|
	sockName: "test-volume-plugin",
 | 
						|
}
 | 
						|
 | 
						|
func init() {
 | 
						|
	rootCmd.PersistentFlags().StringVar(&config.sockName, "sock-name", config.sockName, "Name of unix socket for plugin")
 | 
						|
	rootCmd.PersistentFlags().StringVar(&config.logLevel, "log-level", config.logLevel, "Log messages including and over the specified level: debug, info, warn, error, fatal, panic")
 | 
						|
 | 
						|
	serveCmd.Flags().StringVar(&config.path, "path", "", "Path to initialize state and mount points")
 | 
						|
 | 
						|
	rootCmd.AddCommand(serveCmd, createCmd, removeCmd, listCmd)
 | 
						|
}
 | 
						|
 | 
						|
func before(cmd *cobra.Command, args []string) error {
 | 
						|
	if config.logLevel == "" {
 | 
						|
		config.logLevel = "error"
 | 
						|
	}
 | 
						|
 | 
						|
	level, err := logrus.ParseLevel(config.logLevel)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	logrus.SetLevel(level)
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func main() {
 | 
						|
	if err := rootCmd.Execute(); err != nil {
 | 
						|
		os.Exit(1)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// startServer runs the HTTP server and responds to requests
 | 
						|
func startServer(socketPath string) error {
 | 
						|
	logrus.Debugf("Starting server...")
 | 
						|
 | 
						|
	if config.path == "" {
 | 
						|
		path, err := os.MkdirTemp("", "test_volume_plugin")
 | 
						|
		if err != nil {
 | 
						|
			return fmt.Errorf("getting directory for plugin: %w", err)
 | 
						|
		}
 | 
						|
		config.path = path
 | 
						|
	} else {
 | 
						|
		pathStat, err := os.Stat(config.path)
 | 
						|
		if err != nil {
 | 
						|
			return fmt.Errorf("unable to access requested plugin state directory: %w", err)
 | 
						|
		}
 | 
						|
		if !pathStat.IsDir() {
 | 
						|
			return fmt.Errorf("cannot use %v as plugin state dir as it is not a directory", config.path)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	handle := makeDirDriver(config.path)
 | 
						|
	logrus.Infof("Using %s for volume path", config.path)
 | 
						|
 | 
						|
	server := volume.NewHandler(handle)
 | 
						|
	if err := server.ServeUnix(socketPath, 0); err != nil {
 | 
						|
		return fmt.Errorf("starting server: %w", err)
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// DirDriver is a trivial volume driver implementation.
 | 
						|
// the volumes field maps name to volume
 | 
						|
type DirDriver struct {
 | 
						|
	lock        sync.Mutex
 | 
						|
	volumesPath string
 | 
						|
	volumes     map[string]*dirVol
 | 
						|
}
 | 
						|
 | 
						|
type dirVol struct {
 | 
						|
	name       string
 | 
						|
	path       string
 | 
						|
	options    map[string]string
 | 
						|
	mounts     map[string]bool
 | 
						|
	createTime time.Time
 | 
						|
}
 | 
						|
 | 
						|
// Make a new DirDriver.
 | 
						|
func makeDirDriver(path string) volume.Driver {
 | 
						|
	drv := new(DirDriver)
 | 
						|
	drv.volumesPath = path
 | 
						|
	drv.volumes = make(map[string]*dirVol)
 | 
						|
 | 
						|
	return drv
 | 
						|
}
 | 
						|
 | 
						|
// Capabilities returns the capabilities of the driver.
 | 
						|
func (d *DirDriver) Capabilities() *volume.CapabilitiesResponse {
 | 
						|
	logrus.Infof("Hit Capabilities() endpoint")
 | 
						|
 | 
						|
	return &volume.CapabilitiesResponse{
 | 
						|
		Capabilities: volume.Capability{
 | 
						|
			Scope: "local",
 | 
						|
		},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Create creates a volume.
 | 
						|
func (d *DirDriver) Create(opts *volume.CreateRequest) error {
 | 
						|
	d.lock.Lock()
 | 
						|
	defer d.lock.Unlock()
 | 
						|
 | 
						|
	logrus.Infof("Hit Create() endpoint")
 | 
						|
 | 
						|
	if _, exists := d.volumes[opts.Name]; exists {
 | 
						|
		return fmt.Errorf("volume with name %s already exists", opts.Name)
 | 
						|
	}
 | 
						|
 | 
						|
	newVol := new(dirVol)
 | 
						|
	newVol.name = opts.Name
 | 
						|
	newVol.mounts = make(map[string]bool)
 | 
						|
	newVol.options = make(map[string]string)
 | 
						|
	newVol.createTime = time.Now()
 | 
						|
	for k, v := range opts.Options {
 | 
						|
		newVol.options[k] = v
 | 
						|
	}
 | 
						|
 | 
						|
	volPath := filepath.Join(d.volumesPath, opts.Name)
 | 
						|
	if err := os.Mkdir(volPath, 0755); err != nil {
 | 
						|
		return fmt.Errorf("making volume directory: %w", err)
 | 
						|
	}
 | 
						|
	newVol.path = volPath
 | 
						|
 | 
						|
	d.volumes[opts.Name] = newVol
 | 
						|
 | 
						|
	logrus.Debugf("Made volume with name %s and path %s", newVol.name, newVol.path)
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// List lists all volumes available.
 | 
						|
func (d *DirDriver) List() (*volume.ListResponse, error) {
 | 
						|
	d.lock.Lock()
 | 
						|
	defer d.lock.Unlock()
 | 
						|
 | 
						|
	logrus.Infof("Hit List() endpoint")
 | 
						|
 | 
						|
	vols := new(volume.ListResponse)
 | 
						|
	vols.Volumes = []*volume.Volume{}
 | 
						|
 | 
						|
	for _, vol := range d.volumes {
 | 
						|
		newVol := new(volume.Volume)
 | 
						|
		newVol.Name = vol.name
 | 
						|
		newVol.Mountpoint = vol.path
 | 
						|
		newVol.CreatedAt = vol.createTime.String()
 | 
						|
		vols.Volumes = append(vols.Volumes, newVol)
 | 
						|
		logrus.Debugf("Adding volume %s to list response", newVol.Name)
 | 
						|
	}
 | 
						|
 | 
						|
	return vols, nil
 | 
						|
}
 | 
						|
 | 
						|
// Get retrieves a single volume.
 | 
						|
func (d *DirDriver) Get(req *volume.GetRequest) (*volume.GetResponse, error) {
 | 
						|
	d.lock.Lock()
 | 
						|
	defer d.lock.Unlock()
 | 
						|
 | 
						|
	logrus.Infof("Hit Get() endpoint")
 | 
						|
 | 
						|
	vol, exists := d.volumes[req.Name]
 | 
						|
	if !exists {
 | 
						|
		logrus.Debugf("Did not find volume %s", req.Name)
 | 
						|
		return nil, fmt.Errorf("no volume with name %s found", req.Name)
 | 
						|
	}
 | 
						|
 | 
						|
	logrus.Debugf("Found volume %s", req.Name)
 | 
						|
 | 
						|
	resp := new(volume.GetResponse)
 | 
						|
	resp.Volume = new(volume.Volume)
 | 
						|
	resp.Volume.Name = vol.name
 | 
						|
	resp.Volume.Mountpoint = vol.path
 | 
						|
	resp.Volume.CreatedAt = vol.createTime.String()
 | 
						|
 | 
						|
	return resp, nil
 | 
						|
}
 | 
						|
 | 
						|
// Remove removes a single volume.
 | 
						|
func (d *DirDriver) Remove(req *volume.RemoveRequest) error {
 | 
						|
	d.lock.Lock()
 | 
						|
	defer d.lock.Unlock()
 | 
						|
 | 
						|
	logrus.Infof("Hit Remove() endpoint")
 | 
						|
 | 
						|
	vol, exists := d.volumes[req.Name]
 | 
						|
	if !exists {
 | 
						|
		logrus.Debugf("Did not find volume %s", req.Name)
 | 
						|
		return fmt.Errorf("no volume with name %s found", req.Name)
 | 
						|
	}
 | 
						|
	logrus.Debugf("Found volume %s", req.Name)
 | 
						|
 | 
						|
	if len(vol.mounts) > 0 {
 | 
						|
		logrus.Debugf("Cannot remove %s, is mounted", req.Name)
 | 
						|
		return fmt.Errorf("volume %s is mounted and cannot be removed", req.Name)
 | 
						|
	}
 | 
						|
 | 
						|
	delete(d.volumes, req.Name)
 | 
						|
 | 
						|
	if err := os.RemoveAll(vol.path); err != nil {
 | 
						|
		return fmt.Errorf("removing mountpoint of volume %s: %w", req.Name, err)
 | 
						|
	}
 | 
						|
 | 
						|
	logrus.Debugf("Removed volume %s", req.Name)
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// Path returns the path a single volume is mounted at.
 | 
						|
func (d *DirDriver) Path(req *volume.PathRequest) (*volume.PathResponse, error) {
 | 
						|
	d.lock.Lock()
 | 
						|
	defer d.lock.Unlock()
 | 
						|
 | 
						|
	logrus.Infof("Hit Path() endpoint")
 | 
						|
 | 
						|
	// TODO: Should we return error if not mounted?
 | 
						|
 | 
						|
	vol, exists := d.volumes[req.Name]
 | 
						|
	if !exists {
 | 
						|
		logrus.Debugf("Cannot locate volume %s", req.Name)
 | 
						|
		return nil, fmt.Errorf("no volume with name %s found", req.Name)
 | 
						|
	}
 | 
						|
 | 
						|
	return &volume.PathResponse{
 | 
						|
		Mountpoint: vol.path,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
// Mount mounts the volume.
 | 
						|
func (d *DirDriver) Mount(req *volume.MountRequest) (*volume.MountResponse, error) {
 | 
						|
	d.lock.Lock()
 | 
						|
	defer d.lock.Unlock()
 | 
						|
 | 
						|
	logrus.Infof("Hit Mount() endpoint")
 | 
						|
 | 
						|
	vol, exists := d.volumes[req.Name]
 | 
						|
	if !exists {
 | 
						|
		logrus.Debugf("Cannot locate volume %s", req.Name)
 | 
						|
		return nil, fmt.Errorf("no volume with name %s found", req.Name)
 | 
						|
	}
 | 
						|
 | 
						|
	vol.mounts[req.ID] = true
 | 
						|
 | 
						|
	return &volume.MountResponse{
 | 
						|
		Mountpoint: vol.path,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
// Unmount unmounts the volume.
 | 
						|
func (d *DirDriver) Unmount(req *volume.UnmountRequest) error {
 | 
						|
	d.lock.Lock()
 | 
						|
	defer d.lock.Unlock()
 | 
						|
 | 
						|
	logrus.Infof("Hit Unmount() endpoint")
 | 
						|
 | 
						|
	vol, exists := d.volumes[req.Name]
 | 
						|
	if !exists {
 | 
						|
		logrus.Debugf("Cannot locate volume %s", req.Name)
 | 
						|
		return fmt.Errorf("no volume with name %s found", req.Name)
 | 
						|
	}
 | 
						|
 | 
						|
	mount := vol.mounts[req.ID]
 | 
						|
	if !mount {
 | 
						|
		logrus.Debugf("Volume %s is not mounted by %s", req.Name, req.ID)
 | 
						|
		return fmt.Errorf("volume %s is not mounted by %s", req.Name, req.ID)
 | 
						|
	}
 | 
						|
 | 
						|
	delete(vol.mounts, req.ID)
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 |