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 }