github.com/lalkh/containerd@v1.4.3/services/introspection/local.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 introspection
    18  
    19  import (
    20  	context "context"
    21  	"io/ioutil"
    22  	"os"
    23  	"path/filepath"
    24  	"sync"
    25  
    26  	api "github.com/containerd/containerd/api/services/introspection/v1"
    27  	"github.com/containerd/containerd/api/types"
    28  	"github.com/containerd/containerd/errdefs"
    29  	"github.com/containerd/containerd/filters"
    30  	"github.com/containerd/containerd/plugin"
    31  	"github.com/containerd/containerd/services"
    32  	"github.com/gogo/googleapis/google/rpc"
    33  	ptypes "github.com/gogo/protobuf/types"
    34  	"github.com/google/uuid"
    35  	"google.golang.org/grpc"
    36  	"google.golang.org/grpc/status"
    37  )
    38  
    39  func init() {
    40  	plugin.Register(&plugin.Registration{
    41  		Type:     plugin.ServicePlugin,
    42  		ID:       services.IntrospectionService,
    43  		Requires: []plugin.Type{},
    44  		InitFn: func(ic *plugin.InitContext) (interface{}, error) {
    45  			// this service works by using the plugin context up till the point
    46  			// this service is initialized. Since we require this service last,
    47  			// it should provide the full set of plugins.
    48  			pluginsPB := pluginsToPB(ic.GetAll())
    49  			return &Local{
    50  				plugins: pluginsPB,
    51  				root:    ic.Root,
    52  			}, nil
    53  		},
    54  	})
    55  }
    56  
    57  type Local struct {
    58  	mu      sync.Mutex
    59  	plugins []api.Plugin
    60  	root    string
    61  }
    62  
    63  var _ = (api.IntrospectionClient)(&Local{})
    64  
    65  func (l *Local) UpdateLocal(root string, plugins []api.Plugin) {
    66  	l.mu.Lock()
    67  	defer l.mu.Unlock()
    68  	l.root = root
    69  	l.plugins = plugins
    70  }
    71  
    72  func (l *Local) Plugins(ctx context.Context, req *api.PluginsRequest, _ ...grpc.CallOption) (*api.PluginsResponse, error) {
    73  	filter, err := filters.ParseAll(req.Filters...)
    74  	if err != nil {
    75  		return nil, errdefs.ToGRPCf(errdefs.ErrInvalidArgument, err.Error())
    76  	}
    77  
    78  	var plugins []api.Plugin
    79  	allPlugins := l.getPlugins()
    80  	for _, p := range allPlugins {
    81  		if !filter.Match(adaptPlugin(p)) {
    82  			continue
    83  		}
    84  
    85  		plugins = append(plugins, p)
    86  	}
    87  
    88  	return &api.PluginsResponse{
    89  		Plugins: plugins,
    90  	}, nil
    91  }
    92  
    93  func (l *Local) getPlugins() []api.Plugin {
    94  	l.mu.Lock()
    95  	defer l.mu.Unlock()
    96  	return l.plugins
    97  }
    98  
    99  func (l *Local) Server(ctx context.Context, _ *ptypes.Empty, _ ...grpc.CallOption) (*api.ServerResponse, error) {
   100  	u, err := l.getUUID()
   101  	if err != nil {
   102  		return nil, errdefs.ToGRPC(err)
   103  	}
   104  	return &api.ServerResponse{
   105  		UUID: u,
   106  	}, nil
   107  }
   108  
   109  func (l *Local) getUUID() (string, error) {
   110  	l.mu.Lock()
   111  	defer l.mu.Unlock()
   112  
   113  	data, err := ioutil.ReadFile(l.uuidPath())
   114  	if err != nil {
   115  		if os.IsNotExist(err) {
   116  			return l.generateUUID()
   117  		}
   118  		return "", err
   119  	}
   120  	u := string(data)
   121  	if _, err := uuid.Parse(u); err != nil {
   122  		return "", err
   123  	}
   124  	return u, nil
   125  }
   126  
   127  func (l *Local) generateUUID() (string, error) {
   128  	u, err := uuid.NewRandom()
   129  	if err != nil {
   130  		return "", err
   131  	}
   132  	path := l.uuidPath()
   133  	if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
   134  		return "", err
   135  	}
   136  	uu := u.String()
   137  	if err := ioutil.WriteFile(path, []byte(uu), 0666); err != nil {
   138  		return "", err
   139  	}
   140  	return uu, nil
   141  }
   142  
   143  func (l *Local) uuidPath() string {
   144  	return filepath.Join(l.root, "uuid")
   145  }
   146  
   147  func adaptPlugin(o interface{}) filters.Adaptor {
   148  	obj := o.(api.Plugin)
   149  	return filters.AdapterFunc(func(fieldpath []string) (string, bool) {
   150  		if len(fieldpath) == 0 {
   151  			return "", false
   152  		}
   153  
   154  		switch fieldpath[0] {
   155  		case "type":
   156  			return obj.Type, len(obj.Type) > 0
   157  		case "id":
   158  			return obj.ID, len(obj.ID) > 0
   159  		case "platforms":
   160  			// TODO(stevvooe): Another case here where have multiple values.
   161  			// May need to refactor the filter system to allow filtering by
   162  			// platform, if this is required.
   163  		case "capabilities":
   164  			// TODO(stevvooe): Need a better way to match against
   165  			// collections. We can only return "the value" but really it
   166  			// would be best if we could return a set of values for the
   167  			// path, any of which could match.
   168  		}
   169  
   170  		return "", false
   171  	})
   172  }
   173  
   174  func pluginsToPB(plugins []*plugin.Plugin) []api.Plugin {
   175  	var pluginsPB []api.Plugin
   176  	for _, p := range plugins {
   177  		var platforms []types.Platform
   178  		for _, p := range p.Meta.Platforms {
   179  			platforms = append(platforms, types.Platform{
   180  				OS:           p.OS,
   181  				Architecture: p.Architecture,
   182  				Variant:      p.Variant,
   183  			})
   184  		}
   185  
   186  		var requires []string
   187  		for _, r := range p.Registration.Requires {
   188  			requires = append(requires, r.String())
   189  		}
   190  
   191  		var initErr *rpc.Status
   192  		if err := p.Err(); err != nil {
   193  			st, ok := status.FromError(errdefs.ToGRPC(err))
   194  			if ok {
   195  				var details []*ptypes.Any
   196  				for _, d := range st.Proto().Details {
   197  					details = append(details, &ptypes.Any{
   198  						TypeUrl: d.TypeUrl,
   199  						Value:   d.Value,
   200  					})
   201  				}
   202  				initErr = &rpc.Status{
   203  					Code:    int32(st.Code()),
   204  					Message: st.Message(),
   205  					Details: details,
   206  				}
   207  			} else {
   208  				initErr = &rpc.Status{
   209  					Code:    int32(rpc.UNKNOWN),
   210  					Message: err.Error(),
   211  				}
   212  			}
   213  		}
   214  
   215  		pluginsPB = append(pluginsPB, api.Plugin{
   216  			Type:         p.Registration.Type.String(),
   217  			ID:           p.Registration.ID,
   218  			Requires:     requires,
   219  			Platforms:    platforms,
   220  			Capabilities: p.Meta.Capabilities,
   221  			Exports:      p.Meta.Exports,
   222  			InitErr:      initErr,
   223  		})
   224  	}
   225  
   226  	return pluginsPB
   227  }