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 }