create local version of introspection service
Signed-off-by: Kathryn Baldauf <kabaldau@microsoft.com>
This commit is contained in:
parent
1809231003
commit
a18f77bea0
@ -54,6 +54,7 @@ import (
|
|||||||
"github.com/containerd/containerd/plugin"
|
"github.com/containerd/containerd/plugin"
|
||||||
"github.com/containerd/containerd/remotes"
|
"github.com/containerd/containerd/remotes"
|
||||||
"github.com/containerd/containerd/remotes/docker"
|
"github.com/containerd/containerd/remotes/docker"
|
||||||
|
"github.com/containerd/containerd/services/introspection"
|
||||||
"github.com/containerd/containerd/snapshots"
|
"github.com/containerd/containerd/snapshots"
|
||||||
snproxy "github.com/containerd/containerd/snapshots/proxy"
|
snproxy "github.com/containerd/containerd/snapshots/proxy"
|
||||||
"github.com/containerd/typeurl"
|
"github.com/containerd/typeurl"
|
||||||
@ -621,10 +622,13 @@ func (c *Client) DiffService() DiffService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IntrospectionService returns the underlying Introspection Client
|
// IntrospectionService returns the underlying Introspection Client
|
||||||
func (c *Client) IntrospectionService() introspectionapi.IntrospectionClient {
|
func (c *Client) IntrospectionService() introspection.Service {
|
||||||
|
if c.introspectionService != nil {
|
||||||
|
return c.introspectionService
|
||||||
|
}
|
||||||
c.connMu.Lock()
|
c.connMu.Lock()
|
||||||
defer c.connMu.Unlock()
|
defer c.connMu.Unlock()
|
||||||
return introspectionapi.NewIntrospectionClient(c.conn)
|
return introspection.NewIntrospectionServiceFromClient(introspectionapi.NewIntrospectionClient(c.conn))
|
||||||
}
|
}
|
||||||
|
|
||||||
// LeasesService returns the underlying Leases Client
|
// LeasesService returns the underlying Leases Client
|
||||||
|
@ -23,7 +23,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
introspection "github.com/containerd/containerd/api/services/introspection/v1"
|
|
||||||
"github.com/containerd/containerd/api/types"
|
"github.com/containerd/containerd/api/types"
|
||||||
"github.com/containerd/containerd/cmd/ctr/commands"
|
"github.com/containerd/containerd/cmd/ctr/commands"
|
||||||
"github.com/containerd/containerd/platforms"
|
"github.com/containerd/containerd/platforms"
|
||||||
@ -67,9 +66,7 @@ var listCommand = cli.Command{
|
|||||||
}
|
}
|
||||||
defer cancel()
|
defer cancel()
|
||||||
ps := client.IntrospectionService()
|
ps := client.IntrospectionService()
|
||||||
response, err := ps.Plugins(ctx, &introspection.PluginsRequest{
|
response, err := ps.Plugins(ctx, context.Args())
|
||||||
Filters: context.Args(),
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
introspectionapi "github.com/containerd/containerd/api/services/introspection/v1"
|
|
||||||
"github.com/containerd/containerd/archive"
|
"github.com/containerd/containerd/archive"
|
||||||
"github.com/containerd/containerd/archive/compression"
|
"github.com/containerd/containerd/archive/compression"
|
||||||
"github.com/containerd/containerd/content"
|
"github.com/containerd/containerd/content"
|
||||||
@ -99,11 +98,8 @@ func (c *Client) getInstallPath(ctx context.Context, config InstallConfig) (stri
|
|||||||
if config.Path != "" {
|
if config.Path != "" {
|
||||||
return config.Path, nil
|
return config.Path, nil
|
||||||
}
|
}
|
||||||
resp, err := c.IntrospectionService().Plugins(ctx, &introspectionapi.PluginsRequest{
|
filters := []string{"id==opt"}
|
||||||
Filters: []string{
|
resp, err := c.IntrospectionService().Plugins(ctx, filters)
|
||||||
"id==opt",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
10
services.go
10
services.go
@ -20,6 +20,7 @@ import (
|
|||||||
containersapi "github.com/containerd/containerd/api/services/containers/v1"
|
containersapi "github.com/containerd/containerd/api/services/containers/v1"
|
||||||
"github.com/containerd/containerd/api/services/diff/v1"
|
"github.com/containerd/containerd/api/services/diff/v1"
|
||||||
imagesapi "github.com/containerd/containerd/api/services/images/v1"
|
imagesapi "github.com/containerd/containerd/api/services/images/v1"
|
||||||
|
introspectionapi "github.com/containerd/containerd/api/services/introspection/v1"
|
||||||
namespacesapi "github.com/containerd/containerd/api/services/namespaces/v1"
|
namespacesapi "github.com/containerd/containerd/api/services/namespaces/v1"
|
||||||
"github.com/containerd/containerd/api/services/tasks/v1"
|
"github.com/containerd/containerd/api/services/tasks/v1"
|
||||||
"github.com/containerd/containerd/containers"
|
"github.com/containerd/containerd/containers"
|
||||||
@ -27,6 +28,7 @@ import (
|
|||||||
"github.com/containerd/containerd/images"
|
"github.com/containerd/containerd/images"
|
||||||
"github.com/containerd/containerd/leases"
|
"github.com/containerd/containerd/leases"
|
||||||
"github.com/containerd/containerd/namespaces"
|
"github.com/containerd/containerd/namespaces"
|
||||||
|
"github.com/containerd/containerd/services/introspection"
|
||||||
"github.com/containerd/containerd/snapshots"
|
"github.com/containerd/containerd/snapshots"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -40,6 +42,7 @@ type services struct {
|
|||||||
diffService DiffService
|
diffService DiffService
|
||||||
eventService EventService
|
eventService EventService
|
||||||
leasesService leases.Manager
|
leasesService leases.Manager
|
||||||
|
introspectionService introspection.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServicesOpt allows callers to set options on the services
|
// ServicesOpt allows callers to set options on the services
|
||||||
@ -110,3 +113,10 @@ func WithLeasesService(leasesService leases.Manager) ServicesOpt {
|
|||||||
s.leasesService = leasesService
|
s.leasesService = leasesService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithIntrospectionService sets the introspection service.
|
||||||
|
func WithIntrospectionService(in introspectionapi.IntrospectionClient) ServicesOpt {
|
||||||
|
return func(s *services) {
|
||||||
|
s.introspectionService = introspection.NewIntrospectionServiceFromClient(in)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
62
services/introspection/introspection.go
Normal file
62
services/introspection/introspection.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
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/api/services/introspection/v1"
|
||||||
|
"github.com/containerd/containerd/errdefs"
|
||||||
|
ptypes "github.com/gogo/protobuf/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Service interface {
|
||||||
|
Plugins(context.Context, []string) (*api.PluginsResponse, error)
|
||||||
|
Server(context.Context, *ptypes.Empty) (*api.ServerResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type introspectionRemote struct {
|
||||||
|
client api.IntrospectionClient
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = (Service)(&introspectionRemote{})
|
||||||
|
|
||||||
|
func NewIntrospectionServiceFromClient(c api.IntrospectionClient) Service {
|
||||||
|
return &introspectionRemote{client: c}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *introspectionRemote) Plugins(ctx context.Context, filters []string) (*api.PluginsResponse, error) {
|
||||||
|
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
|
||||||
|
}
|
227
services/introspection/local.go
Normal file
227
services/introspection/local.go
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
/*
|
||||||
|
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"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
api "github.com/containerd/containerd/api/services/introspection/v1"
|
||||||
|
"github.com/containerd/containerd/api/types"
|
||||||
|
"github.com/containerd/containerd/errdefs"
|
||||||
|
"github.com/containerd/containerd/filters"
|
||||||
|
"github.com/containerd/containerd/plugin"
|
||||||
|
"github.com/containerd/containerd/services"
|
||||||
|
"github.com/gogo/googleapis/google/rpc"
|
||||||
|
ptypes "github.com/gogo/protobuf/types"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
plugin.Register(&plugin.Registration{
|
||||||
|
Type: plugin.ServicePlugin,
|
||||||
|
ID: services.IntrospectionService,
|
||||||
|
Requires: []plugin.Type{},
|
||||||
|
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
||||||
|
// this service works by using the plugin context up till the point
|
||||||
|
// this service is initialized. Since we require this service last,
|
||||||
|
// it should provide the full set of plugins.
|
||||||
|
pluginsPB := pluginsToPB(ic.GetAll())
|
||||||
|
return &Local{
|
||||||
|
plugins: pluginsPB,
|
||||||
|
root: ic.Root,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type Local struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
plugins []api.Plugin
|
||||||
|
root string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = (api.IntrospectionClient)(&Local{})
|
||||||
|
|
||||||
|
func (l *Local) UpdateLocal(root string, plugins []api.Plugin) {
|
||||||
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
l.root = root
|
||||||
|
l.plugins = plugins
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Local) Plugins(ctx context.Context, req *api.PluginsRequest, _ ...grpc.CallOption) (*api.PluginsResponse, error) {
|
||||||
|
filter, err := filters.ParseAll(req.Filters...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPCf(errdefs.ErrInvalidArgument, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var plugins []api.Plugin
|
||||||
|
allPlugins := l.getPlugins()
|
||||||
|
for _, p := range allPlugins {
|
||||||
|
if !filter.Match(adaptPlugin(p)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins = append(plugins, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &api.PluginsResponse{
|
||||||
|
Plugins: plugins,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Local) getPlugins() []api.Plugin {
|
||||||
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
return l.plugins
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Local) Server(ctx context.Context, _ *ptypes.Empty, _ ...grpc.CallOption) (*api.ServerResponse, error) {
|
||||||
|
u, err := l.getUUID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
return &api.ServerResponse{
|
||||||
|
UUID: u,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Local) getUUID() (string, error) {
|
||||||
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
|
data, err := ioutil.ReadFile(l.uuidPath())
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return l.generateUUID()
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
u := string(data)
|
||||||
|
if _, err := uuid.Parse(u); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return u, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Local) generateUUID() (string, error) {
|
||||||
|
u, err := uuid.NewRandom()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
path := l.uuidPath()
|
||||||
|
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
uu := u.String()
|
||||||
|
if err := ioutil.WriteFile(path, []byte(uu), 0666); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return uu, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Local) uuidPath() string {
|
||||||
|
return filepath.Join(l.root, "uuid")
|
||||||
|
}
|
||||||
|
|
||||||
|
func adaptPlugin(o interface{}) filters.Adaptor {
|
||||||
|
obj := o.(api.Plugin)
|
||||||
|
return filters.AdapterFunc(func(fieldpath []string) (string, bool) {
|
||||||
|
if len(fieldpath) == 0 {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch fieldpath[0] {
|
||||||
|
case "type":
|
||||||
|
return obj.Type, len(obj.Type) > 0
|
||||||
|
case "id":
|
||||||
|
return obj.ID, len(obj.ID) > 0
|
||||||
|
case "platforms":
|
||||||
|
// TODO(stevvooe): Another case here where have multiple values.
|
||||||
|
// May need to refactor the filter system to allow filtering by
|
||||||
|
// platform, if this is required.
|
||||||
|
case "capabilities":
|
||||||
|
// TODO(stevvooe): Need a better way to match against
|
||||||
|
// collections. We can only return "the value" but really it
|
||||||
|
// would be best if we could return a set of values for the
|
||||||
|
// path, any of which could match.
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func pluginsToPB(plugins []*plugin.Plugin) []api.Plugin {
|
||||||
|
var pluginsPB []api.Plugin
|
||||||
|
for _, p := range plugins {
|
||||||
|
var platforms []types.Platform
|
||||||
|
for _, p := range p.Meta.Platforms {
|
||||||
|
platforms = append(platforms, types.Platform{
|
||||||
|
OS: p.OS,
|
||||||
|
Architecture: p.Architecture,
|
||||||
|
Variant: p.Variant,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var requires []string
|
||||||
|
for _, r := range p.Registration.Requires {
|
||||||
|
requires = append(requires, r.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
var initErr *rpc.Status
|
||||||
|
if err := p.Err(); err != nil {
|
||||||
|
st, ok := status.FromError(errdefs.ToGRPC(err))
|
||||||
|
if ok {
|
||||||
|
var details []*ptypes.Any
|
||||||
|
for _, d := range st.Proto().Details {
|
||||||
|
details = append(details, &ptypes.Any{
|
||||||
|
TypeUrl: d.TypeUrl,
|
||||||
|
Value: d.Value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
initErr = &rpc.Status{
|
||||||
|
Code: int32(st.Code()),
|
||||||
|
Message: st.Message(),
|
||||||
|
Details: details,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
initErr = &rpc.Status{
|
||||||
|
Code: int32(rpc.UNKNOWN),
|
||||||
|
Message: err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginsPB = append(pluginsPB, api.Plugin{
|
||||||
|
Type: p.Registration.Type.String(),
|
||||||
|
ID: p.Registration.ID,
|
||||||
|
Requires: requires,
|
||||||
|
Platforms: platforms,
|
||||||
|
Capabilities: p.Meta.Capabilities,
|
||||||
|
Exports: p.Meta.Exports,
|
||||||
|
InitErr: initErr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return pluginsPB
|
||||||
|
}
|
@ -18,21 +18,13 @@ package introspection
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
context "context"
|
context "context"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
api "github.com/containerd/containerd/api/services/introspection/v1"
|
api "github.com/containerd/containerd/api/services/introspection/v1"
|
||||||
"github.com/containerd/containerd/api/types"
|
|
||||||
"github.com/containerd/containerd/errdefs"
|
|
||||||
"github.com/containerd/containerd/filters"
|
|
||||||
"github.com/containerd/containerd/plugin"
|
"github.com/containerd/containerd/plugin"
|
||||||
"github.com/gogo/googleapis/google/rpc"
|
"github.com/containerd/containerd/services"
|
||||||
ptypes "github.com/gogo/protobuf/types"
|
ptypes "github.com/gogo/protobuf/types"
|
||||||
"github.com/google/uuid"
|
"github.com/pkg/errors"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -44,177 +36,50 @@ func init() {
|
|||||||
// this service works by using the plugin context up till the point
|
// this service works by using the plugin context up till the point
|
||||||
// this service is initialized. Since we require this service last,
|
// this service is initialized. Since we require this service last,
|
||||||
// it should provide the full set of plugins.
|
// it should provide the full set of plugins.
|
||||||
pluginsPB := pluginsToPB(ic.GetAll())
|
plugins, err := ic.GetByType(plugin.ServicePlugin)
|
||||||
return NewService(pluginsPB, ic.Root), nil
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p, ok := plugins[services.IntrospectionService]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("introspection service not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
i, err := p.Instance()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
allPluginsPB := pluginsToPB(ic.GetAll())
|
||||||
|
|
||||||
|
localClient, ok := i.(*Local)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.Errorf("Could not create a local client for introspection service")
|
||||||
|
}
|
||||||
|
localClient.UpdateLocal(ic.Root, allPluginsPB)
|
||||||
|
|
||||||
|
return &server{
|
||||||
|
local: localClient,
|
||||||
|
}, nil
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type service struct {
|
type server struct {
|
||||||
mu sync.Mutex
|
local api.IntrospectionClient
|
||||||
plugins []api.Plugin
|
|
||||||
root string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewService returns the GRPC introspection server
|
var _ = (api.IntrospectionServer)(&server{})
|
||||||
func NewService(plugins []api.Plugin, root string) api.IntrospectionServer {
|
|
||||||
return &service{
|
|
||||||
plugins: plugins,
|
|
||||||
root: root,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) Register(server *grpc.Server) error {
|
func (s *server) Register(server *grpc.Server) error {
|
||||||
api.RegisterIntrospectionServer(server, s)
|
api.RegisterIntrospectionServer(server, s)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Plugins(ctx context.Context, req *api.PluginsRequest) (*api.PluginsResponse, error) {
|
func (s *server) Plugins(ctx context.Context, req *api.PluginsRequest) (*api.PluginsResponse, error) {
|
||||||
filter, err := filters.ParseAll(req.Filters...)
|
return s.local.Plugins(ctx, req)
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPCf(errdefs.ErrInvalidArgument, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
var plugins []api.Plugin
|
|
||||||
for _, p := range s.plugins {
|
|
||||||
if !filter.Match(adaptPlugin(p)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
plugins = append(plugins, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &api.PluginsResponse{
|
|
||||||
Plugins: plugins,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Server(ctx context.Context, _ *ptypes.Empty) (*api.ServerResponse, error) {
|
func (s *server) Server(ctx context.Context, empty *ptypes.Empty) (*api.ServerResponse, error) {
|
||||||
u, err := s.getUUID()
|
return s.local.Server(ctx, empty)
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
return &api.ServerResponse{
|
|
||||||
UUID: u,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) getUUID() (string, error) {
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
|
|
||||||
data, err := ioutil.ReadFile(s.uuidPath())
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return s.generateUUID()
|
|
||||||
}
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
u := string(data)
|
|
||||||
if _, err := uuid.Parse(u); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return u, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) generateUUID() (string, error) {
|
|
||||||
u, err := uuid.NewRandom()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
path := s.uuidPath()
|
|
||||||
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
uu := u.String()
|
|
||||||
if err := ioutil.WriteFile(path, []byte(uu), 0666); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return uu, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) uuidPath() string {
|
|
||||||
return filepath.Join(s.root, "uuid")
|
|
||||||
}
|
|
||||||
|
|
||||||
func adaptPlugin(o interface{}) filters.Adaptor {
|
|
||||||
obj := o.(api.Plugin)
|
|
||||||
return filters.AdapterFunc(func(fieldpath []string) (string, bool) {
|
|
||||||
if len(fieldpath) == 0 {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
switch fieldpath[0] {
|
|
||||||
case "type":
|
|
||||||
return obj.Type, len(obj.Type) > 0
|
|
||||||
case "id":
|
|
||||||
return obj.ID, len(obj.ID) > 0
|
|
||||||
case "platforms":
|
|
||||||
// TODO(stevvooe): Another case here where have multiple values.
|
|
||||||
// May need to refactor the filter system to allow filtering by
|
|
||||||
// platform, if this is required.
|
|
||||||
case "capabilities":
|
|
||||||
// TODO(stevvooe): Need a better way to match against
|
|
||||||
// collections. We can only return "the value" but really it
|
|
||||||
// would be best if we could return a set of values for the
|
|
||||||
// path, any of which could match.
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func pluginsToPB(plugins []*plugin.Plugin) []api.Plugin {
|
|
||||||
var pluginsPB []api.Plugin
|
|
||||||
for _, p := range plugins {
|
|
||||||
var platforms []types.Platform
|
|
||||||
for _, p := range p.Meta.Platforms {
|
|
||||||
platforms = append(platforms, types.Platform{
|
|
||||||
OS: p.OS,
|
|
||||||
Architecture: p.Architecture,
|
|
||||||
Variant: p.Variant,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var requires []string
|
|
||||||
for _, r := range p.Registration.Requires {
|
|
||||||
requires = append(requires, r.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
var initErr *rpc.Status
|
|
||||||
if err := p.Err(); err != nil {
|
|
||||||
st, ok := status.FromError(errdefs.ToGRPC(err))
|
|
||||||
if ok {
|
|
||||||
var details []*ptypes.Any
|
|
||||||
for _, d := range st.Proto().Details {
|
|
||||||
details = append(details, &ptypes.Any{
|
|
||||||
TypeUrl: d.TypeUrl,
|
|
||||||
Value: d.Value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
initErr = &rpc.Status{
|
|
||||||
Code: int32(st.Code()),
|
|
||||||
Message: st.Message(),
|
|
||||||
Details: details,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
initErr = &rpc.Status{
|
|
||||||
Code: int32(rpc.UNKNOWN),
|
|
||||||
Message: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pluginsPB = append(pluginsPB, api.Plugin{
|
|
||||||
Type: p.Registration.Type.String(),
|
|
||||||
ID: p.Registration.ID,
|
|
||||||
Requires: requires,
|
|
||||||
Platforms: platforms,
|
|
||||||
Capabilities: p.Meta.Capabilities,
|
|
||||||
Exports: p.Meta.Exports,
|
|
||||||
InitErr: initErr,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return pluginsPB
|
|
||||||
}
|
}
|
||||||
|
@ -33,4 +33,6 @@ const (
|
|||||||
LeasesService = "leases-service"
|
LeasesService = "leases-service"
|
||||||
// DiffService is id of diff service.
|
// DiffService is id of diff service.
|
||||||
DiffService = "diff-service"
|
DiffService = "diff-service"
|
||||||
|
// IntrospectionService is the id of introspection service
|
||||||
|
IntrospectionService = "introspection-service"
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user