github.com/DaoCloud/dao@v0.0.0-20161212064103-c3dbfd13ee36/plugin/manager_linux.go (about)

     1  // +build linux,experimental
     2  
     3  package plugin
     4  
     5  import (
     6  	"fmt"
     7  	"os"
     8  	"path/filepath"
     9  	"syscall"
    10  	"time"
    11  
    12  	"github.com/Sirupsen/logrus"
    13  	"github.com/docker/docker/libcontainerd"
    14  	"github.com/docker/docker/oci"
    15  	"github.com/docker/docker/pkg/plugins"
    16  	"github.com/docker/docker/pkg/system"
    17  	"github.com/docker/docker/restartmanager"
    18  	"github.com/docker/engine-api/types"
    19  	"github.com/docker/engine-api/types/container"
    20  	"github.com/opencontainers/specs/specs-go"
    21  )
    22  
    23  func (pm *Manager) enable(p *plugin, force bool) error {
    24  	if p.PluginObj.Active && !force {
    25  		return fmt.Errorf("plugin %s is already enabled", p.Name())
    26  	}
    27  	spec, err := pm.initSpec(p)
    28  	if err != nil {
    29  		return err
    30  	}
    31  
    32  	p.restartManager = restartmanager.New(container.RestartPolicy{Name: "always"}, 0)
    33  	if err := pm.containerdClient.Create(p.PluginObj.ID, libcontainerd.Spec(*spec), libcontainerd.WithRestartManager(p.restartManager)); err != nil { // POC-only
    34  		if err := p.restartManager.Cancel(); err != nil {
    35  			logrus.Errorf("enable: restartManager.Cancel failed due to %v", err)
    36  		}
    37  		return err
    38  	}
    39  
    40  	socket := p.PluginObj.Manifest.Interface.Socket
    41  	p.client, err = plugins.NewClient("unix://"+filepath.Join(p.runtimeSourcePath, socket), nil)
    42  	if err != nil {
    43  		if err := p.restartManager.Cancel(); err != nil {
    44  			logrus.Errorf("enable: restartManager.Cancel failed due to %v", err)
    45  		}
    46  		return err
    47  	}
    48  
    49  	pm.Lock() // fixme: lock single record
    50  	p.PluginObj.Active = true
    51  	pm.save()
    52  	pm.Unlock()
    53  
    54  	for _, typ := range p.PluginObj.Manifest.Interface.Types {
    55  		if handler := pm.handlers[typ.String()]; handler != nil {
    56  			handler(p.Name(), p.Client())
    57  		}
    58  	}
    59  
    60  	return nil
    61  }
    62  
    63  func (pm *Manager) restore(p *plugin) error {
    64  	p.restartManager = restartmanager.New(container.RestartPolicy{Name: "always"}, 0)
    65  	return pm.containerdClient.Restore(p.PluginObj.ID, libcontainerd.WithRestartManager(p.restartManager))
    66  }
    67  
    68  func (pm *Manager) initSpec(p *plugin) (*specs.Spec, error) {
    69  	s := oci.DefaultSpec()
    70  
    71  	rootfs := filepath.Join(pm.libRoot, p.PluginObj.ID, "rootfs")
    72  	s.Root = specs.Root{
    73  		Path:     rootfs,
    74  		Readonly: false, // TODO: all plugins should be readonly? settable in manifest?
    75  	}
    76  
    77  	mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
    78  		Source:      &p.runtimeSourcePath,
    79  		Destination: defaultPluginRuntimeDestination,
    80  		Type:        "bind",
    81  		Options:     []string{"rbind", "rshared"},
    82  	})
    83  	for _, mount := range mounts {
    84  		m := specs.Mount{
    85  			Destination: mount.Destination,
    86  			Type:        mount.Type,
    87  			Options:     mount.Options,
    88  		}
    89  		// TODO: if nil, then it's required and user didn't set it
    90  		if mount.Source != nil {
    91  			m.Source = *mount.Source
    92  		}
    93  
    94  		if m.Source != "" && m.Type == "bind" {
    95  			/* Debugging issue #25511: Volumes and other content created under the
    96  			bind mount should be recursively propagated. rshared, not shared.
    97  			This could be the reason for EBUSY during removal. Override options
    98  			with rbind, rshared and see if CI errors are fixed. */
    99  			m.Options = []string{"rbind", "rshared"}
   100  			fi, err := os.Lstat(filepath.Join(rootfs, string(os.PathSeparator), m.Destination)) // TODO: followsymlinks
   101  			if err != nil {
   102  				return nil, err
   103  			}
   104  			if fi.IsDir() {
   105  				if err := os.MkdirAll(m.Source, 0700); err != nil {
   106  					return nil, err
   107  				}
   108  			}
   109  		}
   110  		s.Mounts = append(s.Mounts, m)
   111  	}
   112  
   113  	envs := make([]string, 1, len(p.PluginObj.Config.Env)+1)
   114  	envs[0] = "PATH=" + system.DefaultPathEnv
   115  	envs = append(envs, p.PluginObj.Config.Env...)
   116  
   117  	args := append(p.PluginObj.Manifest.Entrypoint, p.PluginObj.Config.Args...)
   118  	cwd := p.PluginObj.Manifest.Workdir
   119  	if len(cwd) == 0 {
   120  		cwd = "/"
   121  	}
   122  	s.Process = specs.Process{
   123  		Terminal: false,
   124  		Args:     args,
   125  		Cwd:      cwd,
   126  		Env:      envs,
   127  	}
   128  
   129  	return &s, nil
   130  }
   131  
   132  func (pm *Manager) disable(p *plugin) error {
   133  	if !p.PluginObj.Active {
   134  		return fmt.Errorf("plugin %s is already disabled", p.Name())
   135  	}
   136  	if err := p.restartManager.Cancel(); err != nil {
   137  		logrus.Error(err)
   138  	}
   139  	if err := pm.containerdClient.Signal(p.PluginObj.ID, int(syscall.SIGKILL)); err != nil {
   140  		logrus.Error(err)
   141  	}
   142  	os.RemoveAll(p.runtimeSourcePath)
   143  	pm.Lock() // fixme: lock single record
   144  	defer pm.Unlock()
   145  	p.PluginObj.Active = false
   146  	pm.save()
   147  	return nil
   148  }
   149  
   150  // Shutdown stops all plugins and called during daemon shutdown.
   151  func (pm *Manager) Shutdown() {
   152  	pm.RLock()
   153  	defer pm.RUnlock()
   154  
   155  	pm.shutdown = true
   156  	for _, p := range pm.plugins {
   157  		if pm.liveRestore && p.PluginObj.Active {
   158  			logrus.Debug("Plugin active when liveRestore is set, skipping shutdown")
   159  			continue
   160  		}
   161  		if p.restartManager != nil {
   162  			if err := p.restartManager.Cancel(); err != nil {
   163  				logrus.Error(err)
   164  			}
   165  		}
   166  		if pm.containerdClient != nil && p.PluginObj.Active {
   167  			p.exitChan = make(chan bool)
   168  			err := pm.containerdClient.Signal(p.PluginObj.ID, int(syscall.SIGTERM))
   169  			if err != nil {
   170  				logrus.Errorf("Sending SIGTERM to plugin failed with error: %v", err)
   171  			} else {
   172  				select {
   173  				case <-p.exitChan:
   174  					logrus.Debug("Clean shutdown of plugin")
   175  				case <-time.After(time.Second * 10):
   176  					logrus.Debug("Force shutdown plugin")
   177  					if err := pm.containerdClient.Signal(p.PluginObj.ID, int(syscall.SIGKILL)); err != nil {
   178  						logrus.Errorf("Sending SIGKILL to plugin failed with error: %v", err)
   179  					}
   180  				}
   181  			}
   182  			close(p.exitChan)
   183  		}
   184  		if err := os.RemoveAll(p.runtimeSourcePath); err != nil {
   185  			logrus.Errorf("Remove plugin runtime failed with error: %v", err)
   186  		}
   187  	}
   188  }