Cleanup introspection interface

Split service proxy from service plugin.
Make introspection service easier for clients to use.
Update service proxy to support grpc and ttrpc.

Signed-off-by: Derek McGowan <derek@mcg.dev>
This commit is contained in:
Derek McGowan
2024-03-01 23:06:22 -08:00
parent 9a2b85561a
commit 1bf781d8eb
14 changed files with 195 additions and 159 deletions

View File

@@ -1,77 +0,0 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package introspection
import (
context "context"
api "github.com/containerd/containerd/v2/api/services/introspection/v1"
ptypes "github.com/containerd/containerd/v2/protobuf/types"
"github.com/containerd/errdefs"
"github.com/containerd/log"
)
// Service defines the introspection service interface
type Service interface {
Plugins(context.Context, []string) (*api.PluginsResponse, error)
Server(context.Context, *ptypes.Empty) (*api.ServerResponse, error)
PluginInfo(ctx context.Context, in *api.PluginInfoRequest) (*api.PluginInfoResponse, error)
}
type introspectionRemote struct {
client api.IntrospectionClient
}
var _ = (Service)(&introspectionRemote{})
// NewIntrospectionServiceFromClient creates a new introspection service from an API client
func NewIntrospectionServiceFromClient(c api.IntrospectionClient) Service {
return &introspectionRemote{client: c}
}
func (i *introspectionRemote) Plugins(ctx context.Context, filters []string) (*api.PluginsResponse, error) {
log.G(ctx).WithField("filters", filters).Debug("remote introspection plugin filters")
resp, err := i.client.Plugins(ctx, &api.PluginsRequest{
Filters: filters,
})
if err != nil {
return nil, errdefs.FromGRPC(err)
}
return resp, nil
}
func (i *introspectionRemote) Server(ctx context.Context, in *ptypes.Empty) (*api.ServerResponse, error) {
resp, err := i.client.Server(ctx, in)
if err != nil {
return nil, errdefs.FromGRPC(err)
}
return resp, nil
}
func (i *introspectionRemote) PluginInfo(ctx context.Context, in *api.PluginInfoRequest) (*api.PluginInfoResponse, error) {
resp, err := i.client.PluginInfo(ctx, in)
if err != nil {
return nil, errdefs.FromGRPC(err)
}
return resp, nil
}

View File

@@ -28,11 +28,11 @@ import (
"github.com/google/uuid"
"google.golang.org/genproto/googleapis/rpc/code"
rpc "google.golang.org/genproto/googleapis/rpc/status"
"google.golang.org/grpc"
"google.golang.org/grpc/status"
api "github.com/containerd/containerd/v2/api/services/introspection/v1"
"github.com/containerd/containerd/v2/api/types"
"github.com/containerd/containerd/v2/core/introspection"
"github.com/containerd/containerd/v2/pkg/filters"
"github.com/containerd/containerd/v2/plugins"
"github.com/containerd/containerd/v2/plugins/services"
@@ -80,7 +80,7 @@ type Local struct {
warningClient warning.Service
}
var _ = (api.IntrospectionClient)(&Local{})
var _ = (introspection.Service)(&Local{})
// UpdateLocal updates the local introspection service
func (l *Local) UpdateLocal(root string) {
@@ -90,10 +90,10 @@ func (l *Local) UpdateLocal(root string) {
}
// Plugins returns the locally defined plugins
func (l *Local) Plugins(ctx context.Context, req *api.PluginsRequest, _ ...grpc.CallOption) (*api.PluginsResponse, error) {
filter, err := filters.ParseAll(req.Filters...)
func (l *Local) Plugins(ctx context.Context, fs ...string) (*api.PluginsResponse, error) {
filter, err := filters.ParseAll(fs...)
if err != nil {
return nil, errdefs.ToGRPCf(errdefs.ErrInvalidArgument, err.Error())
return nil, fmt.Errorf("%w: %w", errdefs.ErrInvalidArgument, err)
}
var plugins []*api.Plugin
@@ -121,17 +121,17 @@ func (l *Local) getPlugins() []*api.Plugin {
}
// Server returns the local server information
func (l *Local) Server(ctx context.Context, _ *ptypes.Empty, _ ...grpc.CallOption) (*api.ServerResponse, error) {
func (l *Local) Server(ctx context.Context) (*api.ServerResponse, error) {
u, err := l.getUUID()
if err != nil {
return nil, errdefs.ToGRPC(err)
return nil, err
}
pid := os.Getpid()
var pidns uint64
if runtime.GOOS == "linux" {
pidns, err = statPIDNS(pid)
if err != nil {
return nil, errdefs.ToGRPC(err)
return nil, err
}
}
return &api.ServerResponse{
@@ -278,11 +278,10 @@ type pluginInfoProvider interface {
PluginInfo(context.Context, interface{}) (interface{}, error)
}
func (l *Local) PluginInfo(ctx context.Context, req *api.PluginInfoRequest, _ ...grpc.CallOption) (*api.PluginInfoResponse, error) {
p := l.plugins.Get(plugin.Type(req.Type), req.ID)
func (l *Local) PluginInfo(ctx context.Context, pluginType, id string, options any) (*api.PluginInfoResponse, error) {
p := l.plugins.Get(plugin.Type(pluginType), id)
if p == nil {
err := fmt.Errorf("plugin %s.%s not found: %w", req.Type, req.ID, errdefs.ErrNotFound)
return nil, errdefs.ToGRPC(err)
return nil, fmt.Errorf("plugin %s.%s not found: %w", pluginType, id, errdefs.ErrNotFound)
}
resp := &api.PluginInfoResponse{
@@ -290,35 +289,26 @@ func (l *Local) PluginInfo(ctx context.Context, req *api.PluginInfoRequest, _ ..
}
// Request additional info from plugin instance
if req.Options != nil {
if options != nil {
if p.Err() != nil {
err := fmt.Errorf("cannot get extra info, plugin not successfully loaded: %w", errdefs.ErrFailedPrecondition)
return resp, errdefs.ToGRPC(err)
return resp, fmt.Errorf("cannot get extra info, plugin not successfully loaded: %w", errdefs.ErrFailedPrecondition)
}
inst, err := p.Instance()
if err != nil {
err := fmt.Errorf("failed to get plugin instance: %w", errdefs.ErrFailedPrecondition)
return resp, errdefs.ToGRPC(err)
return resp, fmt.Errorf("failed to get plugin instance: %w", errdefs.ErrFailedPrecondition)
}
pi, ok := inst.(pluginInfoProvider)
if !ok {
err := fmt.Errorf("plugin does not provided extra information: %w", errdefs.ErrNotImplemented)
return resp, errdefs.ToGRPC(err)
return resp, fmt.Errorf("plugin does not provided extra information: %w", errdefs.ErrNotImplemented)
}
options, err := typeurl.UnmarshalAny(req.Options)
if err != nil {
err = fmt.Errorf("failed to unmarshal plugin info Options: %w", err)
return resp, errdefs.ToGRPC(err)
}
info, err := pi.PluginInfo(ctx, options)
if err != nil {
return resp, errdefs.ToGRPC(err)
}
ai, err := typeurl.MarshalAny(info)
if err != nil {
err = fmt.Errorf("failed to marshal plugin info: %w", err)
return resp, errdefs.ToGRPC(err)
return resp, fmt.Errorf("failed to marshal plugin info: %w", err)
}
resp.Extra = &ptypes.Any{
TypeUrl: ai.GetTypeUrl(),

View File

@@ -19,13 +19,17 @@ package introspection
import (
context "context"
"errors"
"fmt"
api "github.com/containerd/containerd/v2/api/services/introspection/v1"
"github.com/containerd/containerd/v2/core/introspection"
"github.com/containerd/containerd/v2/plugins"
"github.com/containerd/containerd/v2/plugins/services"
ptypes "github.com/containerd/containerd/v2/protobuf/types"
"github.com/containerd/errdefs"
"github.com/containerd/plugin"
"github.com/containerd/plugin/registry"
"github.com/containerd/typeurl/v2"
"google.golang.org/grpc"
)
@@ -54,7 +58,7 @@ func init() {
}
type server struct {
local api.IntrospectionClient
local introspection.Service
api.UnimplementedIntrospectionServer
}
@@ -65,14 +69,25 @@ func (s *server) Register(server *grpc.Server) error {
return nil
}
func (s *server) Plugins(ctx context.Context, req *api.PluginsRequest) (*api.PluginsResponse, error) {
return s.local.Plugins(ctx, req)
func (s *server) Plugins(ctx context.Context, req *api.PluginsRequest) (resp *api.PluginsResponse, err error) {
resp, err = s.local.Plugins(ctx, req.Filters...)
return resp, errdefs.ToGRPC(err)
}
func (s *server) Server(ctx context.Context, empty *ptypes.Empty) (*api.ServerResponse, error) {
return s.local.Server(ctx, empty)
func (s *server) Server(ctx context.Context, _ *ptypes.Empty) (resp *api.ServerResponse, err error) {
resp, err = s.local.Server(ctx)
return resp, errdefs.ToGRPC(err)
}
func (s *server) PluginInfo(ctx context.Context, req *api.PluginInfoRequest) (*api.PluginInfoResponse, error) {
return s.local.PluginInfo(ctx, req)
func (s *server) PluginInfo(ctx context.Context, req *api.PluginInfoRequest) (resp *api.PluginInfoResponse, err error) {
var options any
if req.Options != nil {
options, err = typeurl.UnmarshalAny(req.Options)
if err != nil {
return resp, errdefs.ToGRPC(fmt.Errorf("failed to unmarshal plugin info Options: %w", err))
}
}
resp, err = s.local.PluginInfo(ctx, req.Type, req.ID, options)
return resp, errdefs.ToGRPC(err)
}