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