github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/plugin/plugin.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package plugin
    18  
    19  import (
    20  	"fmt"
    21  	"sync"
    22  
    23  	"github.com/containerd/ttrpc"
    24  	"github.com/pkg/errors"
    25  	"google.golang.org/grpc"
    26  )
    27  
    28  var (
    29  	// ErrNoType is returned when no type is specified
    30  	ErrNoType = errors.New("plugin: no type")
    31  	// ErrNoPluginID is returned when no id is specified
    32  	ErrNoPluginID = errors.New("plugin: no id")
    33  	// ErrIDRegistered is returned when a duplicate id is already registered
    34  	ErrIDRegistered = errors.New("plugin: id already registered")
    35  	// ErrSkipPlugin is used when a plugin is not initialized and should not be loaded,
    36  	// this allows the plugin loader differentiate between a plugin which is configured
    37  	// not to load and one that fails to load.
    38  	ErrSkipPlugin = errors.New("skip plugin")
    39  
    40  	// ErrInvalidRequires will be thrown if the requirements for a plugin are
    41  	// defined in an invalid manner.
    42  	ErrInvalidRequires = errors.New("invalid requires")
    43  )
    44  
    45  // IsSkipPlugin returns true if the error is skipping the plugin
    46  func IsSkipPlugin(err error) bool {
    47  	return errors.Is(err, ErrSkipPlugin)
    48  }
    49  
    50  // Type is the type of the plugin
    51  type Type string
    52  
    53  func (t Type) String() string { return string(t) }
    54  
    55  const (
    56  	// InternalPlugin implements an internal plugin to containerd
    57  	InternalPlugin Type = "io.containerd.internal.v1"
    58  	// RuntimePlugin implements a runtime
    59  	RuntimePlugin Type = "io.containerd.runtime.v1"
    60  	// RuntimePluginV2 implements a runtime v2
    61  	RuntimePluginV2 Type = "io.containerd.runtime.v2"
    62  	// ServicePlugin implements a internal service
    63  	ServicePlugin Type = "io.containerd.service.v1"
    64  	// GRPCPlugin implements a grpc service
    65  	GRPCPlugin Type = "io.containerd.grpc.v1"
    66  	// SnapshotPlugin implements a snapshotter
    67  	SnapshotPlugin Type = "io.containerd.snapshotter.v1"
    68  	// TaskMonitorPlugin implements a task monitor
    69  	TaskMonitorPlugin Type = "io.containerd.monitor.v1"
    70  	// DiffPlugin implements a differ
    71  	DiffPlugin Type = "io.containerd.differ.v1"
    72  	// MetadataPlugin implements a metadata store
    73  	MetadataPlugin Type = "io.containerd.metadata.v1"
    74  	// ContentPlugin implements a content store
    75  	ContentPlugin Type = "io.containerd.content.v1"
    76  	// GCPlugin implements garbage collection policy
    77  	GCPlugin Type = "io.containerd.gc.v1"
    78  )
    79  
    80  const (
    81  	// RuntimeLinuxV1 is the legacy linux runtime
    82  	RuntimeLinuxV1 = "io.containerd.runtime.v1.linux"
    83  	// RuntimeRuncV1 is the runc runtime that supports a single container
    84  	RuntimeRuncV1 = "io.containerd.runc.v1"
    85  	// RuntimeRuncV2 is the runc runtime that supports multiple containers per shim
    86  	RuntimeRuncV2 = "io.containerd.runc.v2"
    87  )
    88  
    89  // Registration contains information for registering a plugin
    90  type Registration struct {
    91  	// Type of the plugin
    92  	Type Type
    93  	// ID of the plugin
    94  	ID string
    95  	// Config specific to the plugin
    96  	Config interface{}
    97  	// Requires is a list of plugins that the registered plugin requires to be available
    98  	Requires []Type
    99  
   100  	// InitFn is called when initializing a plugin. The registration and
   101  	// context are passed in. The init function may modify the registration to
   102  	// add exports, capabilities and platform support declarations.
   103  	InitFn func(*InitContext) (interface{}, error)
   104  	// Disable the plugin from loading
   105  	Disable bool
   106  }
   107  
   108  // Init the registered plugin
   109  func (r *Registration) Init(ic *InitContext) *Plugin {
   110  	p, err := r.InitFn(ic)
   111  	return &Plugin{
   112  		Registration: r,
   113  		Config:       ic.Config,
   114  		Meta:         ic.Meta,
   115  		instance:     p,
   116  		err:          err,
   117  	}
   118  }
   119  
   120  // URI returns the full plugin URI
   121  func (r *Registration) URI() string {
   122  	return fmt.Sprintf("%s.%s", r.Type, r.ID)
   123  }
   124  
   125  // Service allows GRPC services to be registered with the underlying server
   126  type Service interface {
   127  	Register(*grpc.Server) error
   128  }
   129  
   130  // TTRPCService allows TTRPC services to be registered with the underlying server
   131  type TTRPCService interface {
   132  	RegisterTTRPC(*ttrpc.Server) error
   133  }
   134  
   135  // TCPService allows GRPC services to be registered with the underlying tcp server
   136  type TCPService interface {
   137  	RegisterTCP(*grpc.Server) error
   138  }
   139  
   140  var register = struct {
   141  	sync.RWMutex
   142  	r []*Registration
   143  }{}
   144  
   145  // Load loads all plugins at the provided path into containerd
   146  func Load(path string) (err error) {
   147  	defer func() {
   148  		if v := recover(); v != nil {
   149  			rerr, ok := v.(error)
   150  			if !ok {
   151  				rerr = fmt.Errorf("%s", v)
   152  			}
   153  			err = rerr
   154  		}
   155  	}()
   156  	return loadPlugins(path)
   157  }
   158  
   159  // Register allows plugins to register
   160  func Register(r *Registration) {
   161  	register.Lock()
   162  	defer register.Unlock()
   163  
   164  	if r.Type == "" {
   165  		panic(ErrNoType)
   166  	}
   167  	if r.ID == "" {
   168  		panic(ErrNoPluginID)
   169  	}
   170  	if err := checkUnique(r); err != nil {
   171  		panic(err)
   172  	}
   173  
   174  	var last bool
   175  	for _, requires := range r.Requires {
   176  		if requires == "*" {
   177  			last = true
   178  		}
   179  	}
   180  	if last && len(r.Requires) != 1 {
   181  		panic(ErrInvalidRequires)
   182  	}
   183  
   184  	register.r = append(register.r, r)
   185  }
   186  
   187  func checkUnique(r *Registration) error {
   188  	for _, registered := range register.r {
   189  		if r.URI() == registered.URI() {
   190  			return errors.Wrap(ErrIDRegistered, r.URI())
   191  		}
   192  	}
   193  	return nil
   194  }
   195  
   196  // DisableFilter filters out disabled plugins
   197  type DisableFilter func(r *Registration) bool
   198  
   199  // Graph returns an ordered list of registered plugins for initialization.
   200  // Plugins in disableList specified by id will be disabled.
   201  func Graph(filter DisableFilter) (ordered []*Registration) {
   202  	register.RLock()
   203  	defer register.RUnlock()
   204  
   205  	for _, r := range register.r {
   206  		if filter(r) {
   207  			r.Disable = true
   208  		}
   209  	}
   210  
   211  	added := map[*Registration]bool{}
   212  	for _, r := range register.r {
   213  		if r.Disable {
   214  			continue
   215  		}
   216  		children(r, added, &ordered)
   217  		if !added[r] {
   218  			ordered = append(ordered, r)
   219  			added[r] = true
   220  		}
   221  	}
   222  	return ordered
   223  }
   224  
   225  func children(reg *Registration, added map[*Registration]bool, ordered *[]*Registration) {
   226  	for _, t := range reg.Requires {
   227  		for _, r := range register.r {
   228  			if !r.Disable &&
   229  				r.URI() != reg.URI() &&
   230  				(t == "*" || r.Type == t) {
   231  				children(r, added, ordered)
   232  				if !added[r] {
   233  					*ordered = append(*ordered, r)
   234  					added[r] = true
   235  				}
   236  			}
   237  		}
   238  	}
   239  }