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  }