github.com/endophage/docker@v1.4.2-0.20161027011718-242853499895/plugin/v2/plugin.go (about) 1 package v2 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "os" 8 "path/filepath" 9 "strings" 10 "sync" 11 12 "github.com/docker/docker/api/types" 13 "github.com/docker/docker/pkg/plugins" 14 "github.com/docker/docker/pkg/system" 15 specs "github.com/opencontainers/runtime-spec/specs-go" 16 ) 17 18 // Plugin represents an individual plugin. 19 type Plugin struct { 20 sync.RWMutex 21 PluginObj types.Plugin `json:"plugin"` 22 PClient *plugins.Client `json:"-"` 23 RuntimeSourcePath string `json:"-"` 24 RefCount int `json:"-"` 25 Restart bool `json:"-"` 26 ExitChan chan bool `json:"-"` 27 } 28 29 const defaultPluginRuntimeDestination = "/run/docker/plugins" 30 31 // ErrInadequateCapability indicates that the plugin did not have the requested capability. 32 type ErrInadequateCapability string 33 34 func (cap ErrInadequateCapability) Error() string { 35 return fmt.Sprintf("plugin does not provide %q capability", cap) 36 } 37 38 func newPluginObj(name, id, tag string) types.Plugin { 39 return types.Plugin{Name: name, ID: id, Tag: tag} 40 } 41 42 // NewPlugin creates a plugin. 43 func NewPlugin(name, id, runRoot, tag string) *Plugin { 44 return &Plugin{ 45 PluginObj: newPluginObj(name, id, tag), 46 RuntimeSourcePath: filepath.Join(runRoot, id), 47 } 48 } 49 50 // Client returns the plugin client. 51 func (p *Plugin) Client() *plugins.Client { 52 return p.PClient 53 } 54 55 // IsV1 returns true for V1 plugins and false otherwise. 56 func (p *Plugin) IsV1() bool { 57 return false 58 } 59 60 // Name returns the plugin name. 61 func (p *Plugin) Name() string { 62 name := p.PluginObj.Name 63 if len(p.PluginObj.Tag) > 0 { 64 // TODO: this feels hacky, maybe we should be storing the distribution reference rather than splitting these 65 name += ":" + p.PluginObj.Tag 66 } 67 return name 68 } 69 70 // FilterByCap query the plugin for a given capability. 71 func (p *Plugin) FilterByCap(capability string) (*Plugin, error) { 72 capability = strings.ToLower(capability) 73 for _, typ := range p.PluginObj.Manifest.Interface.Types { 74 if typ.Capability == capability && typ.Prefix == "docker" { 75 return p, nil 76 } 77 } 78 return nil, ErrInadequateCapability(capability) 79 } 80 81 // RemoveFromDisk deletes the plugin's runtime files from disk. 82 func (p *Plugin) RemoveFromDisk() error { 83 return os.RemoveAll(p.RuntimeSourcePath) 84 } 85 86 // InitPlugin populates the plugin object from the plugin manifest file. 87 func (p *Plugin) InitPlugin(libRoot string) error { 88 dt, err := os.Open(filepath.Join(libRoot, p.PluginObj.ID, "manifest.json")) 89 if err != nil { 90 return err 91 } 92 err = json.NewDecoder(dt).Decode(&p.PluginObj.Manifest) 93 dt.Close() 94 if err != nil { 95 return err 96 } 97 98 p.PluginObj.Config.Mounts = make([]types.PluginMount, len(p.PluginObj.Manifest.Mounts)) 99 for i, mount := range p.PluginObj.Manifest.Mounts { 100 p.PluginObj.Config.Mounts[i] = mount 101 } 102 p.PluginObj.Config.Env = make([]string, 0, len(p.PluginObj.Manifest.Env)) 103 for _, env := range p.PluginObj.Manifest.Env { 104 if env.Value != nil { 105 p.PluginObj.Config.Env = append(p.PluginObj.Config.Env, fmt.Sprintf("%s=%s", env.Name, *env.Value)) 106 } 107 } 108 copy(p.PluginObj.Config.Args, p.PluginObj.Manifest.Args.Value) 109 110 f, err := os.Create(filepath.Join(libRoot, p.PluginObj.ID, "plugin-config.json")) 111 if err != nil { 112 return err 113 } 114 err = json.NewEncoder(f).Encode(&p.PluginObj.Config) 115 f.Close() 116 return err 117 } 118 119 // Set is used to pass arguments to the plugin. 120 func (p *Plugin) Set(args []string) error { 121 m := make(map[string]string, len(args)) 122 for _, arg := range args { 123 i := strings.Index(arg, "=") 124 if i < 0 { 125 return fmt.Errorf("No equal sign '=' found in %s", arg) 126 } 127 m[arg[:i]] = arg[i+1:] 128 } 129 return errors.New("not implemented") 130 } 131 132 // ComputePrivileges takes the manifest file and computes the list of access necessary 133 // for the plugin on the host. 134 func (p *Plugin) ComputePrivileges() types.PluginPrivileges { 135 m := p.PluginObj.Manifest 136 var privileges types.PluginPrivileges 137 if m.Network.Type != "null" && m.Network.Type != "bridge" { 138 privileges = append(privileges, types.PluginPrivilege{ 139 Name: "network", 140 Description: "", 141 Value: []string{m.Network.Type}, 142 }) 143 } 144 for _, mount := range m.Mounts { 145 if mount.Source != nil { 146 privileges = append(privileges, types.PluginPrivilege{ 147 Name: "mount", 148 Description: "", 149 Value: []string{*mount.Source}, 150 }) 151 } 152 } 153 for _, device := range m.Devices { 154 if device.Path != nil { 155 privileges = append(privileges, types.PluginPrivilege{ 156 Name: "device", 157 Description: "", 158 Value: []string{*device.Path}, 159 }) 160 } 161 } 162 if len(m.Capabilities) > 0 { 163 privileges = append(privileges, types.PluginPrivilege{ 164 Name: "capabilities", 165 Description: "", 166 Value: m.Capabilities, 167 }) 168 } 169 return privileges 170 } 171 172 // IsEnabled returns the active state of the plugin. 173 func (p *Plugin) IsEnabled() bool { 174 p.RLock() 175 defer p.RUnlock() 176 177 return p.PluginObj.Enabled 178 } 179 180 // GetID returns the plugin's ID. 181 func (p *Plugin) GetID() string { 182 p.RLock() 183 defer p.RUnlock() 184 185 return p.PluginObj.ID 186 } 187 188 // GetSocket returns the plugin socket. 189 func (p *Plugin) GetSocket() string { 190 p.RLock() 191 defer p.RUnlock() 192 193 return p.PluginObj.Manifest.Interface.Socket 194 } 195 196 // GetTypes returns the interface types of a plugin. 197 func (p *Plugin) GetTypes() []types.PluginInterfaceType { 198 p.RLock() 199 defer p.RUnlock() 200 201 return p.PluginObj.Manifest.Interface.Types 202 } 203 204 // InitSpec creates an OCI spec from the plugin's config. 205 func (p *Plugin) InitSpec(s specs.Spec, libRoot string) (*specs.Spec, error) { 206 rootfs := filepath.Join(libRoot, p.PluginObj.ID, "rootfs") 207 s.Root = specs.Root{ 208 Path: rootfs, 209 Readonly: false, // TODO: all plugins should be readonly? settable in manifest? 210 } 211 212 mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{ 213 Source: &p.RuntimeSourcePath, 214 Destination: defaultPluginRuntimeDestination, 215 Type: "bind", 216 Options: []string{"rbind", "rshared"}, 217 }) 218 for _, mount := range mounts { 219 m := specs.Mount{ 220 Destination: mount.Destination, 221 Type: mount.Type, 222 Options: mount.Options, 223 } 224 // TODO: if nil, then it's required and user didn't set it 225 if mount.Source != nil { 226 m.Source = *mount.Source 227 } 228 if m.Source != "" && m.Type == "bind" { 229 fi, err := os.Lstat(filepath.Join(rootfs, m.Destination)) // TODO: followsymlinks 230 if err != nil { 231 return nil, err 232 } 233 if fi.IsDir() { 234 if err := os.MkdirAll(m.Source, 0700); err != nil { 235 return nil, err 236 } 237 } 238 } 239 s.Mounts = append(s.Mounts, m) 240 } 241 242 envs := make([]string, 1, len(p.PluginObj.Config.Env)+1) 243 envs[0] = "PATH=" + system.DefaultPathEnv 244 envs = append(envs, p.PluginObj.Config.Env...) 245 246 args := append(p.PluginObj.Manifest.Entrypoint, p.PluginObj.Config.Args...) 247 cwd := p.PluginObj.Manifest.Workdir 248 if len(cwd) == 0 { 249 cwd = "/" 250 } 251 s.Process = specs.Process{ 252 Terminal: false, 253 Args: args, 254 Cwd: cwd, 255 Env: envs, 256 } 257 258 return &s, nil 259 }