github.com/rhatdan/docker@v0.7.7-0.20180119204836-47a0dcbcd20a/plugin/manager_linux.go (about)

     1  package plugin
     2  
     3  import (
     4  	"encoding/json"
     5  	"net"
     6  	"os"
     7  	"path/filepath"
     8  	"time"
     9  
    10  	"github.com/docker/docker/api/types"
    11  	"github.com/docker/docker/daemon/initlayer"
    12  	"github.com/docker/docker/errdefs"
    13  	"github.com/docker/docker/pkg/containerfs"
    14  	"github.com/docker/docker/pkg/idtools"
    15  	"github.com/docker/docker/pkg/mount"
    16  	"github.com/docker/docker/pkg/plugins"
    17  	"github.com/docker/docker/pkg/stringid"
    18  	"github.com/docker/docker/plugin/v2"
    19  	"github.com/opencontainers/go-digest"
    20  	"github.com/pkg/errors"
    21  	"github.com/sirupsen/logrus"
    22  	"golang.org/x/sys/unix"
    23  )
    24  
    25  func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) (err error) {
    26  	p.Rootfs = filepath.Join(pm.config.Root, p.PluginObj.ID, "rootfs")
    27  	if p.IsEnabled() && !force {
    28  		return errors.Wrap(enabledError(p.Name()), "plugin already enabled")
    29  	}
    30  	spec, err := p.InitSpec(pm.config.ExecRoot)
    31  	if err != nil {
    32  		return err
    33  	}
    34  
    35  	c.restart = true
    36  	c.exitChan = make(chan bool)
    37  
    38  	pm.mu.Lock()
    39  	pm.cMap[p] = c
    40  	pm.mu.Unlock()
    41  
    42  	var propRoot string
    43  	if p.PropagatedMount != "" {
    44  		propRoot = filepath.Join(filepath.Dir(p.Rootfs), "propagated-mount")
    45  
    46  		if err = os.MkdirAll(propRoot, 0755); err != nil {
    47  			logrus.Errorf("failed to create PropagatedMount directory at %s: %v", propRoot, err)
    48  		}
    49  
    50  		if err = mount.MakeRShared(propRoot); err != nil {
    51  			return errors.Wrap(err, "error setting up propagated mount dir")
    52  		}
    53  
    54  		if err = mount.Mount(propRoot, p.PropagatedMount, "none", "rbind"); err != nil {
    55  			return errors.Wrap(err, "error creating mount for propagated mount")
    56  		}
    57  	}
    58  
    59  	rootFS := containerfs.NewLocalContainerFS(filepath.Join(pm.config.Root, p.PluginObj.ID, rootFSFileName))
    60  	if err := initlayer.Setup(rootFS, idtools.IDPair{0, 0}); err != nil {
    61  		return errors.WithStack(err)
    62  	}
    63  
    64  	stdout, stderr := makeLoggerStreams(p.GetID())
    65  	if err := pm.executor.Create(p.GetID(), *spec, stdout, stderr); err != nil {
    66  		if p.PropagatedMount != "" {
    67  			if err := mount.Unmount(p.PropagatedMount); err != nil {
    68  				logrus.Warnf("Could not unmount %s: %v", p.PropagatedMount, err)
    69  			}
    70  			if err := mount.Unmount(propRoot); err != nil {
    71  				logrus.Warnf("Could not unmount %s: %v", propRoot, err)
    72  			}
    73  		}
    74  	}
    75  
    76  	return pm.pluginPostStart(p, c)
    77  }
    78  
    79  func (pm *Manager) pluginPostStart(p *v2.Plugin, c *controller) error {
    80  	sockAddr := filepath.Join(pm.config.ExecRoot, p.GetID(), p.GetSocket())
    81  	client, err := plugins.NewClientWithTimeout("unix://"+sockAddr, nil, time.Duration(c.timeoutInSecs)*time.Second)
    82  	if err != nil {
    83  		c.restart = false
    84  		shutdownPlugin(p, c, pm.executor)
    85  		return errors.WithStack(err)
    86  	}
    87  
    88  	p.SetPClient(client)
    89  
    90  	// Initial sleep before net Dial to allow plugin to listen on socket.
    91  	time.Sleep(500 * time.Millisecond)
    92  	maxRetries := 3
    93  	var retries int
    94  	for {
    95  		// net dial into the unix socket to see if someone's listening.
    96  		conn, err := net.Dial("unix", sockAddr)
    97  		if err == nil {
    98  			conn.Close()
    99  			break
   100  		}
   101  
   102  		time.Sleep(3 * time.Second)
   103  		retries++
   104  
   105  		if retries > maxRetries {
   106  			logrus.Debugf("error net dialing plugin: %v", err)
   107  			c.restart = false
   108  			// While restoring plugins, we need to explicitly set the state to disabled
   109  			pm.config.Store.SetState(p, false)
   110  			shutdownPlugin(p, c, pm.executor)
   111  			return err
   112  		}
   113  
   114  	}
   115  	pm.config.Store.SetState(p, true)
   116  	pm.config.Store.CallHandler(p)
   117  
   118  	return pm.save(p)
   119  }
   120  
   121  func (pm *Manager) restore(p *v2.Plugin) error {
   122  	stdout, stderr := makeLoggerStreams(p.GetID())
   123  	if err := pm.executor.Restore(p.GetID(), stdout, stderr); err != nil {
   124  		return err
   125  	}
   126  
   127  	if pm.config.LiveRestoreEnabled {
   128  		c := &controller{}
   129  		if isRunning, _ := pm.executor.IsRunning(p.GetID()); !isRunning {
   130  			// plugin is not running, so follow normal startup procedure
   131  			return pm.enable(p, c, true)
   132  		}
   133  
   134  		c.exitChan = make(chan bool)
   135  		c.restart = true
   136  		pm.mu.Lock()
   137  		pm.cMap[p] = c
   138  		pm.mu.Unlock()
   139  		return pm.pluginPostStart(p, c)
   140  	}
   141  
   142  	return nil
   143  }
   144  
   145  func shutdownPlugin(p *v2.Plugin, c *controller, executor Executor) {
   146  	pluginID := p.GetID()
   147  
   148  	err := executor.Signal(pluginID, int(unix.SIGTERM))
   149  	if err != nil {
   150  		logrus.Errorf("Sending SIGTERM to plugin failed with error: %v", err)
   151  	} else {
   152  		select {
   153  		case <-c.exitChan:
   154  			logrus.Debug("Clean shutdown of plugin")
   155  		case <-time.After(time.Second * 10):
   156  			logrus.Debug("Force shutdown plugin")
   157  			if err := executor.Signal(pluginID, int(unix.SIGKILL)); err != nil {
   158  				logrus.Errorf("Sending SIGKILL to plugin failed with error: %v", err)
   159  			}
   160  			select {
   161  			case <-c.exitChan:
   162  				logrus.Debug("SIGKILL plugin shutdown")
   163  			case <-time.After(time.Second * 10):
   164  				logrus.Debug("Force shutdown plugin FAILED")
   165  			}
   166  		}
   167  	}
   168  }
   169  
   170  func setupRoot(root string) error {
   171  	if err := mount.MakePrivate(root); err != nil {
   172  		return errors.Wrap(err, "error setting plugin manager root to private")
   173  	}
   174  	return nil
   175  }
   176  
   177  func (pm *Manager) disable(p *v2.Plugin, c *controller) error {
   178  	if !p.IsEnabled() {
   179  		return errors.Wrap(errDisabled(p.Name()), "plugin is already disabled")
   180  	}
   181  
   182  	c.restart = false
   183  	shutdownPlugin(p, c, pm.executor)
   184  	pm.config.Store.SetState(p, false)
   185  	return pm.save(p)
   186  }
   187  
   188  // Shutdown stops all plugins and called during daemon shutdown.
   189  func (pm *Manager) Shutdown() {
   190  	plugins := pm.config.Store.GetAll()
   191  	for _, p := range plugins {
   192  		pm.mu.RLock()
   193  		c := pm.cMap[p]
   194  		pm.mu.RUnlock()
   195  
   196  		if pm.config.LiveRestoreEnabled && p.IsEnabled() {
   197  			logrus.Debug("Plugin active when liveRestore is set, skipping shutdown")
   198  			continue
   199  		}
   200  		if pm.executor != nil && p.IsEnabled() {
   201  			c.restart = false
   202  			shutdownPlugin(p, c, pm.executor)
   203  		}
   204  	}
   205  	mount.Unmount(pm.config.Root)
   206  }
   207  
   208  func (pm *Manager) upgradePlugin(p *v2.Plugin, configDigest digest.Digest, blobsums []digest.Digest, tmpRootFSDir string, privileges *types.PluginPrivileges) (err error) {
   209  	config, err := pm.setupNewPlugin(configDigest, blobsums, privileges)
   210  	if err != nil {
   211  		return err
   212  	}
   213  
   214  	pdir := filepath.Join(pm.config.Root, p.PluginObj.ID)
   215  	orig := filepath.Join(pdir, "rootfs")
   216  
   217  	// Make sure nothing is mounted
   218  	// This could happen if the plugin was disabled with `-f` with active mounts.
   219  	// If there is anything in `orig` is still mounted, this should error out.
   220  	if err := mount.RecursiveUnmount(orig); err != nil {
   221  		return errdefs.System(err)
   222  	}
   223  
   224  	backup := orig + "-old"
   225  	if err := os.Rename(orig, backup); err != nil {
   226  		return errors.Wrap(errdefs.System(err), "error backing up plugin data before upgrade")
   227  	}
   228  
   229  	defer func() {
   230  		if err != nil {
   231  			if rmErr := os.RemoveAll(orig); rmErr != nil && !os.IsNotExist(rmErr) {
   232  				logrus.WithError(rmErr).WithField("dir", backup).Error("error cleaning up after failed upgrade")
   233  				return
   234  			}
   235  			if mvErr := os.Rename(backup, orig); mvErr != nil {
   236  				err = errors.Wrap(mvErr, "error restoring old plugin root on upgrade failure")
   237  			}
   238  			if rmErr := os.RemoveAll(tmpRootFSDir); rmErr != nil && !os.IsNotExist(rmErr) {
   239  				logrus.WithError(rmErr).WithField("plugin", p.Name()).Errorf("error cleaning up plugin upgrade dir: %s", tmpRootFSDir)
   240  			}
   241  		} else {
   242  			if rmErr := os.RemoveAll(backup); rmErr != nil && !os.IsNotExist(rmErr) {
   243  				logrus.WithError(rmErr).WithField("dir", backup).Error("error cleaning up old plugin root after successful upgrade")
   244  			}
   245  
   246  			p.Config = configDigest
   247  			p.Blobsums = blobsums
   248  		}
   249  	}()
   250  
   251  	if err := os.Rename(tmpRootFSDir, orig); err != nil {
   252  		return errors.Wrap(errdefs.System(err), "error upgrading")
   253  	}
   254  
   255  	p.PluginObj.Config = config
   256  	err = pm.save(p)
   257  	return errors.Wrap(err, "error saving upgraded plugin config")
   258  }
   259  
   260  func (pm *Manager) setupNewPlugin(configDigest digest.Digest, blobsums []digest.Digest, privileges *types.PluginPrivileges) (types.PluginConfig, error) {
   261  	configRC, err := pm.blobStore.Get(configDigest)
   262  	if err != nil {
   263  		return types.PluginConfig{}, err
   264  	}
   265  	defer configRC.Close()
   266  
   267  	var config types.PluginConfig
   268  	dec := json.NewDecoder(configRC)
   269  	if err := dec.Decode(&config); err != nil {
   270  		return types.PluginConfig{}, errors.Wrapf(err, "failed to parse config")
   271  	}
   272  	if dec.More() {
   273  		return types.PluginConfig{}, errors.New("invalid config json")
   274  	}
   275  
   276  	requiredPrivileges := computePrivileges(config)
   277  	if err != nil {
   278  		return types.PluginConfig{}, err
   279  	}
   280  	if privileges != nil {
   281  		if err := validatePrivileges(requiredPrivileges, *privileges); err != nil {
   282  			return types.PluginConfig{}, err
   283  		}
   284  	}
   285  
   286  	return config, nil
   287  }
   288  
   289  // createPlugin creates a new plugin. take lock before calling.
   290  func (pm *Manager) createPlugin(name string, configDigest digest.Digest, blobsums []digest.Digest, rootFSDir string, privileges *types.PluginPrivileges, opts ...CreateOpt) (p *v2.Plugin, err error) {
   291  	if err := pm.config.Store.validateName(name); err != nil { // todo: this check is wrong. remove store
   292  		return nil, errdefs.InvalidParameter(err)
   293  	}
   294  
   295  	config, err := pm.setupNewPlugin(configDigest, blobsums, privileges)
   296  	if err != nil {
   297  		return nil, err
   298  	}
   299  
   300  	p = &v2.Plugin{
   301  		PluginObj: types.Plugin{
   302  			Name:   name,
   303  			ID:     stringid.GenerateRandomID(),
   304  			Config: config,
   305  		},
   306  		Config:   configDigest,
   307  		Blobsums: blobsums,
   308  	}
   309  	p.InitEmptySettings()
   310  	for _, o := range opts {
   311  		o(p)
   312  	}
   313  
   314  	pdir := filepath.Join(pm.config.Root, p.PluginObj.ID)
   315  	if err := os.MkdirAll(pdir, 0700); err != nil {
   316  		return nil, errors.Wrapf(err, "failed to mkdir %v", pdir)
   317  	}
   318  
   319  	defer func() {
   320  		if err != nil {
   321  			os.RemoveAll(pdir)
   322  		}
   323  	}()
   324  
   325  	if err := os.Rename(rootFSDir, filepath.Join(pdir, rootFSFileName)); err != nil {
   326  		return nil, errors.Wrap(err, "failed to rename rootfs")
   327  	}
   328  
   329  	if err := pm.save(p); err != nil {
   330  		return nil, err
   331  	}
   332  
   333  	pm.config.Store.Add(p) // todo: remove
   334  
   335  	return p, nil
   336  }