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