github.com/flavio/docker@v0.1.3-0.20170117145210-f63d1a6eec47/plugin/manager_linux.go (about)

     1  // +build linux
     2  
     3  package plugin
     4  
     5  import (
     6  	"encoding/json"
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  	"syscall"
    11  	"time"
    12  
    13  	"github.com/Sirupsen/logrus"
    14  	"github.com/docker/docker/api/types"
    15  	"github.com/docker/docker/daemon/initlayer"
    16  	"github.com/docker/docker/libcontainerd"
    17  	"github.com/docker/docker/pkg/mount"
    18  	"github.com/docker/docker/pkg/plugins"
    19  	"github.com/docker/docker/pkg/stringid"
    20  	"github.com/docker/docker/plugin/v2"
    21  	"github.com/opencontainers/go-digest"
    22  	specs "github.com/opencontainers/runtime-spec/specs-go"
    23  	"github.com/pkg/errors"
    24  )
    25  
    26  func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) error {
    27  	p.Rootfs = filepath.Join(pm.config.Root, p.PluginObj.ID, "rootfs")
    28  	if p.IsEnabled() && !force {
    29  		return fmt.Errorf("plugin %s is already enabled", p.Name())
    30  	}
    31  	spec, err := p.InitSpec(pm.config.ExecRoot)
    32  	if err != nil {
    33  		return err
    34  	}
    35  
    36  	c.restart = true
    37  	c.exitChan = make(chan bool)
    38  
    39  	pm.mu.Lock()
    40  	pm.cMap[p] = c
    41  	pm.mu.Unlock()
    42  
    43  	if p.PropagatedMount != "" {
    44  		if err := mount.MakeRShared(p.PropagatedMount); err != nil {
    45  			return err
    46  		}
    47  	}
    48  
    49  	if err := initlayer.Setup(filepath.Join(pm.config.Root, p.PluginObj.ID, rootFSFileName), 0, 0); err != nil {
    50  		return err
    51  	}
    52  
    53  	if err := pm.containerdClient.Create(p.GetID(), "", "", specs.Spec(*spec), attachToLog(p.GetID())); err != nil {
    54  		if p.PropagatedMount != "" {
    55  			if err := mount.Unmount(p.PropagatedMount); err != nil {
    56  				logrus.Warnf("Could not unmount %s: %v", p.PropagatedMount, err)
    57  			}
    58  		}
    59  		return err
    60  	}
    61  
    62  	return pm.pluginPostStart(p, c)
    63  }
    64  
    65  func (pm *Manager) pluginPostStart(p *v2.Plugin, c *controller) error {
    66  	client, err := plugins.NewClientWithTimeout("unix://"+filepath.Join(pm.config.ExecRoot, p.GetID(), p.GetSocket()), nil, c.timeoutInSecs)
    67  	if err != nil {
    68  		c.restart = false
    69  		shutdownPlugin(p, c, pm.containerdClient)
    70  		return err
    71  	}
    72  
    73  	p.SetPClient(client)
    74  	pm.config.Store.SetState(p, true)
    75  	pm.config.Store.CallHandler(p)
    76  
    77  	return pm.save(p)
    78  }
    79  
    80  func (pm *Manager) restore(p *v2.Plugin) error {
    81  	if err := pm.containerdClient.Restore(p.GetID(), attachToLog(p.GetID())); err != nil {
    82  		return err
    83  	}
    84  
    85  	if pm.config.LiveRestoreEnabled {
    86  		c := &controller{}
    87  		if pids, _ := pm.containerdClient.GetPidsForContainer(p.GetID()); len(pids) == 0 {
    88  			// plugin is not running, so follow normal startup procedure
    89  			return pm.enable(p, c, true)
    90  		}
    91  
    92  		c.exitChan = make(chan bool)
    93  		c.restart = true
    94  		pm.mu.Lock()
    95  		pm.cMap[p] = c
    96  		pm.mu.Unlock()
    97  		return pm.pluginPostStart(p, c)
    98  	}
    99  
   100  	return nil
   101  }
   102  
   103  func shutdownPlugin(p *v2.Plugin, c *controller, containerdClient libcontainerd.Client) {
   104  	pluginID := p.GetID()
   105  
   106  	err := containerdClient.Signal(pluginID, int(syscall.SIGTERM))
   107  	if err != nil {
   108  		logrus.Errorf("Sending SIGTERM to plugin failed with error: %v", err)
   109  	} else {
   110  		select {
   111  		case <-c.exitChan:
   112  			logrus.Debug("Clean shutdown of plugin")
   113  		case <-time.After(time.Second * 10):
   114  			logrus.Debug("Force shutdown plugin")
   115  			if err := containerdClient.Signal(pluginID, int(syscall.SIGKILL)); err != nil {
   116  				logrus.Errorf("Sending SIGKILL to plugin failed with error: %v", err)
   117  			}
   118  		}
   119  	}
   120  }
   121  
   122  func (pm *Manager) disable(p *v2.Plugin, c *controller) error {
   123  	if !p.IsEnabled() {
   124  		return fmt.Errorf("plugin %s is already disabled", p.Name())
   125  	}
   126  
   127  	c.restart = false
   128  	shutdownPlugin(p, c, pm.containerdClient)
   129  	pm.config.Store.SetState(p, false)
   130  	return pm.save(p)
   131  }
   132  
   133  // Shutdown stops all plugins and called during daemon shutdown.
   134  func (pm *Manager) Shutdown() {
   135  	plugins := pm.config.Store.GetAll()
   136  	for _, p := range plugins {
   137  		pm.mu.RLock()
   138  		c := pm.cMap[p]
   139  		pm.mu.RUnlock()
   140  
   141  		if pm.config.LiveRestoreEnabled && p.IsEnabled() {
   142  			logrus.Debug("Plugin active when liveRestore is set, skipping shutdown")
   143  			continue
   144  		}
   145  		if pm.containerdClient != nil && p.IsEnabled() {
   146  			c.restart = false
   147  			shutdownPlugin(p, c, pm.containerdClient)
   148  		}
   149  	}
   150  }
   151  
   152  // createPlugin creates a new plugin. take lock before calling.
   153  func (pm *Manager) createPlugin(name string, configDigest digest.Digest, blobsums []digest.Digest, rootFSDir string, privileges *types.PluginPrivileges) (p *v2.Plugin, err error) {
   154  	if err := pm.config.Store.validateName(name); err != nil { // todo: this check is wrong. remove store
   155  		return nil, err
   156  	}
   157  
   158  	configRC, err := pm.blobStore.Get(configDigest)
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  	defer configRC.Close()
   163  
   164  	var config types.PluginConfig
   165  	dec := json.NewDecoder(configRC)
   166  	if err := dec.Decode(&config); err != nil {
   167  		return nil, errors.Wrapf(err, "failed to parse config")
   168  	}
   169  	if dec.More() {
   170  		return nil, errors.New("invalid config json")
   171  	}
   172  
   173  	requiredPrivileges, err := computePrivileges(config)
   174  	if err != nil {
   175  		return nil, err
   176  	}
   177  	if privileges != nil {
   178  		if err := validatePrivileges(requiredPrivileges, *privileges); err != nil {
   179  			return nil, err
   180  		}
   181  	}
   182  
   183  	p = &v2.Plugin{
   184  		PluginObj: types.Plugin{
   185  			Name:   name,
   186  			ID:     stringid.GenerateRandomID(),
   187  			Config: config,
   188  		},
   189  		Config:   configDigest,
   190  		Blobsums: blobsums,
   191  	}
   192  	p.InitEmptySettings()
   193  
   194  	pdir := filepath.Join(pm.config.Root, p.PluginObj.ID)
   195  	if err := os.MkdirAll(pdir, 0700); err != nil {
   196  		return nil, errors.Wrapf(err, "failed to mkdir %v", pdir)
   197  	}
   198  
   199  	defer func() {
   200  		if err != nil {
   201  			os.RemoveAll(pdir)
   202  		}
   203  	}()
   204  
   205  	if err := os.Rename(rootFSDir, filepath.Join(pdir, rootFSFileName)); err != nil {
   206  		return nil, errors.Wrap(err, "failed to rename rootfs")
   207  	}
   208  
   209  	if err := pm.save(p); err != nil {
   210  		return nil, err
   211  	}
   212  
   213  	pm.config.Store.Add(p) // todo: remove
   214  
   215  	return p, nil
   216  }