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  }