github.com/docker/engine@v22.0.0-20211208180946-d456264580cf+incompatible/pkg/plugins/discovery.go (about) 1 package plugins // import "github.com/docker/docker/pkg/plugins" 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/url" 7 "os" 8 "path/filepath" 9 "strings" 10 "sync" 11 12 "github.com/pkg/errors" 13 ) 14 15 var ( 16 // ErrNotFound plugin not found 17 ErrNotFound = errors.New("plugin not found") 18 socketsPath = "/run/docker/plugins" 19 ) 20 21 // localRegistry defines a registry that is local (using unix socket). 22 type localRegistry struct{} 23 24 func newLocalRegistry() localRegistry { 25 return localRegistry{} 26 } 27 28 // Scan scans all the plugin paths and returns all the names it found 29 func Scan() ([]string, error) { 30 var names []string 31 dirEntries, err := os.ReadDir(socketsPath) 32 if err != nil && !os.IsNotExist(err) { 33 return nil, errors.Wrap(err, "error reading dir entries") 34 } 35 36 for _, entry := range dirEntries { 37 if entry.IsDir() { 38 fi, err := os.Stat(filepath.Join(socketsPath, entry.Name(), entry.Name()+".sock")) 39 if err != nil { 40 continue 41 } 42 43 entry = fileInfoToDirEntry(fi) 44 } 45 46 if entry.Type()&os.ModeSocket != 0 { 47 names = append(names, strings.TrimSuffix(filepath.Base(entry.Name()), filepath.Ext(entry.Name()))) 48 } 49 } 50 51 for _, p := range specsPaths { 52 dirEntries, err := os.ReadDir(p) 53 if err != nil && !os.IsNotExist(err) { 54 return nil, errors.Wrap(err, "error reading dir entries") 55 } 56 57 for _, fi := range dirEntries { 58 if fi.IsDir() { 59 infos, err := os.ReadDir(filepath.Join(p, fi.Name())) 60 if err != nil { 61 continue 62 } 63 64 for _, info := range infos { 65 if strings.TrimSuffix(info.Name(), filepath.Ext(info.Name())) == fi.Name() { 66 fi = info 67 break 68 } 69 } 70 } 71 72 ext := filepath.Ext(fi.Name()) 73 switch ext { 74 case ".spec", ".json": 75 plugin := strings.TrimSuffix(fi.Name(), ext) 76 names = append(names, plugin) 77 default: 78 } 79 } 80 } 81 return names, nil 82 } 83 84 // Plugin returns the plugin registered with the given name (or returns an error). 85 func (l *localRegistry) Plugin(name string) (*Plugin, error) { 86 socketpaths := pluginPaths(socketsPath, name, ".sock") 87 88 for _, p := range socketpaths { 89 if fi, err := os.Stat(p); err == nil && fi.Mode()&os.ModeSocket != 0 { 90 return NewLocalPlugin(name, "unix://"+p), nil 91 } 92 } 93 94 var txtspecpaths []string 95 for _, p := range specsPaths { 96 txtspecpaths = append(txtspecpaths, pluginPaths(p, name, ".spec")...) 97 txtspecpaths = append(txtspecpaths, pluginPaths(p, name, ".json")...) 98 } 99 100 for _, p := range txtspecpaths { 101 if _, err := os.Stat(p); err == nil { 102 if strings.HasSuffix(p, ".json") { 103 return readPluginJSONInfo(name, p) 104 } 105 return readPluginInfo(name, p) 106 } 107 } 108 return nil, errors.Wrapf(ErrNotFound, "could not find plugin %s in v1 plugin registry", name) 109 } 110 111 func readPluginInfo(name, path string) (*Plugin, error) { 112 content, err := os.ReadFile(path) 113 if err != nil { 114 return nil, err 115 } 116 addr := strings.TrimSpace(string(content)) 117 118 u, err := url.Parse(addr) 119 if err != nil { 120 return nil, err 121 } 122 123 if len(u.Scheme) == 0 { 124 return nil, fmt.Errorf("Unknown protocol") 125 } 126 127 return NewLocalPlugin(name, addr), nil 128 } 129 130 func readPluginJSONInfo(name, path string) (*Plugin, error) { 131 f, err := os.Open(path) 132 if err != nil { 133 return nil, err 134 } 135 defer f.Close() 136 137 var p Plugin 138 if err := json.NewDecoder(f).Decode(&p); err != nil { 139 return nil, err 140 } 141 p.name = name 142 if p.TLSConfig != nil && len(p.TLSConfig.CAFile) == 0 { 143 p.TLSConfig.InsecureSkipVerify = true 144 } 145 p.activateWait = sync.NewCond(&sync.Mutex{}) 146 147 return &p, nil 148 } 149 150 func pluginPaths(base, name, ext string) []string { 151 return []string{ 152 filepath.Join(base, name+ext), 153 filepath.Join(base, name, name+ext), 154 } 155 }