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 }