Merge pull request #2203 from Random-Liu/support-in-process-integration
Support in process integration
This commit is contained in:
commit
3013762fc5
118
client.go
118
client.go
@ -31,6 +31,7 @@ import (
|
|||||||
eventsapi "github.com/containerd/containerd/api/services/events/v1"
|
eventsapi "github.com/containerd/containerd/api/services/events/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"
|
introspectionapi "github.com/containerd/containerd/api/services/introspection/v1"
|
||||||
|
leasesapi "github.com/containerd/containerd/api/services/leases/v1"
|
||||||
namespacesapi "github.com/containerd/containerd/api/services/namespaces/v1"
|
namespacesapi "github.com/containerd/containerd/api/services/namespaces/v1"
|
||||||
snapshotsapi "github.com/containerd/containerd/api/services/snapshots/v1"
|
snapshotsapi "github.com/containerd/containerd/api/services/snapshots/v1"
|
||||||
"github.com/containerd/containerd/api/services/tasks/v1"
|
"github.com/containerd/containerd/api/services/tasks/v1"
|
||||||
@ -39,6 +40,7 @@ import (
|
|||||||
"github.com/containerd/containerd/content"
|
"github.com/containerd/containerd/content"
|
||||||
"github.com/containerd/containerd/dialer"
|
"github.com/containerd/containerd/dialer"
|
||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
|
"github.com/containerd/containerd/events"
|
||||||
"github.com/containerd/containerd/images"
|
"github.com/containerd/containerd/images"
|
||||||
"github.com/containerd/containerd/namespaces"
|
"github.com/containerd/containerd/namespaces"
|
||||||
"github.com/containerd/containerd/platforms"
|
"github.com/containerd/containerd/platforms"
|
||||||
@ -75,6 +77,13 @@ func New(address string, opts ...ClientOpt) (*Client, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
c := &Client{
|
||||||
|
runtime: fmt.Sprintf("%s.%s", plugin.RuntimePlugin, runtime.GOOS),
|
||||||
|
}
|
||||||
|
if copts.services != nil {
|
||||||
|
c.services = *copts.services
|
||||||
|
}
|
||||||
|
if address != "" {
|
||||||
gopts := []grpc.DialOption{
|
gopts := []grpc.DialOption{
|
||||||
grpc.WithBlock(),
|
grpc.WithBlock(),
|
||||||
grpc.WithInsecure(),
|
grpc.WithInsecure(),
|
||||||
@ -104,25 +113,37 @@ func New(address string, opts ...ClientOpt) (*Client, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &Client{
|
c.conn, c.connector = conn, connector
|
||||||
conn: conn,
|
}
|
||||||
connector: connector,
|
if copts.services == nil && c.conn == nil {
|
||||||
runtime: fmt.Sprintf("%s.%s", plugin.RuntimePlugin, runtime.GOOS),
|
return nil, errors.New("no grpc connection or services is available")
|
||||||
}, nil
|
}
|
||||||
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWithConn returns a new containerd client that is connected to the containerd
|
// NewWithConn returns a new containerd client that is connected to the containerd
|
||||||
// instance provided by the connection
|
// instance provided by the connection
|
||||||
func NewWithConn(conn *grpc.ClientConn, opts ...ClientOpt) (*Client, error) {
|
func NewWithConn(conn *grpc.ClientConn, opts ...ClientOpt) (*Client, error) {
|
||||||
return &Client{
|
var copts clientOpts
|
||||||
|
for _, o := range opts {
|
||||||
|
if err := o(&copts); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c := &Client{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
runtime: fmt.Sprintf("%s.%s", plugin.RuntimePlugin, runtime.GOOS),
|
runtime: fmt.Sprintf("%s.%s", plugin.RuntimePlugin, runtime.GOOS),
|
||||||
}, nil
|
}
|
||||||
|
if copts.services != nil {
|
||||||
|
c.services = *copts.services
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client is the client to interact with containerd and its various services
|
// Client is the client to interact with containerd and its various services
|
||||||
// using a uniform interface
|
// using a uniform interface
|
||||||
type Client struct {
|
type Client struct {
|
||||||
|
services
|
||||||
conn *grpc.ClientConn
|
conn *grpc.ClientConn
|
||||||
runtime string
|
runtime string
|
||||||
connector func() (*grpc.ClientConn, error)
|
connector func() (*grpc.ClientConn, error)
|
||||||
@ -149,6 +170,9 @@ func (c *Client) Reconnect() error {
|
|||||||
// connection. A timeout can be set in the context to ensure it returns
|
// connection. A timeout can be set in the context to ensure it returns
|
||||||
// early.
|
// early.
|
||||||
func (c *Client) IsServing(ctx context.Context) (bool, error) {
|
func (c *Client) IsServing(ctx context.Context) (bool, error) {
|
||||||
|
if c.conn == nil {
|
||||||
|
return false, errors.New("no grpc connection available")
|
||||||
|
}
|
||||||
r, err := c.HealthService().Check(ctx, &grpc_health_v1.HealthCheckRequest{}, grpc.FailFast(false))
|
r, err := c.HealthService().Check(ctx, &grpc_health_v1.HealthCheckRequest{}, grpc.FailFast(false))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@ -385,43 +409,8 @@ func (c *Client) ListImages(ctx context.Context, filters ...string) ([]Image, er
|
|||||||
//
|
//
|
||||||
// The subscriber can stop receiving events by canceling the provided context.
|
// The subscriber can stop receiving events by canceling the provided context.
|
||||||
// The errs channel will be closed and return a nil error.
|
// The errs channel will be closed and return a nil error.
|
||||||
func (c *Client) Subscribe(ctx context.Context, filters ...string) (ch <-chan *eventsapi.Envelope, errs <-chan error) {
|
func (c *Client) Subscribe(ctx context.Context, filters ...string) (ch <-chan *events.Envelope, errs <-chan error) {
|
||||||
var (
|
return c.EventService().Subscribe(ctx, filters...)
|
||||||
evq = make(chan *eventsapi.Envelope)
|
|
||||||
errq = make(chan error, 1)
|
|
||||||
)
|
|
||||||
|
|
||||||
errs = errq
|
|
||||||
ch = evq
|
|
||||||
|
|
||||||
session, err := c.EventService().Subscribe(ctx, &eventsapi.SubscribeRequest{
|
|
||||||
Filters: filters,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
errq <- err
|
|
||||||
close(errq)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer close(errq)
|
|
||||||
|
|
||||||
for {
|
|
||||||
ev, err := session.Recv()
|
|
||||||
if err != nil {
|
|
||||||
errq <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case evq <- ev:
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return ch, errs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the clients connection to containerd
|
// Close closes the clients connection to containerd
|
||||||
@ -431,36 +420,57 @@ func (c *Client) Close() error {
|
|||||||
|
|
||||||
// NamespaceService returns the underlying Namespaces Store
|
// NamespaceService returns the underlying Namespaces Store
|
||||||
func (c *Client) NamespaceService() namespaces.Store {
|
func (c *Client) NamespaceService() namespaces.Store {
|
||||||
|
if c.namespaceStore != nil {
|
||||||
|
return c.namespaceStore
|
||||||
|
}
|
||||||
return NewNamespaceStoreFromClient(namespacesapi.NewNamespacesClient(c.conn))
|
return NewNamespaceStoreFromClient(namespacesapi.NewNamespacesClient(c.conn))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainerService returns the underlying container Store
|
// ContainerService returns the underlying container Store
|
||||||
func (c *Client) ContainerService() containers.Store {
|
func (c *Client) ContainerService() containers.Store {
|
||||||
|
if c.containerStore != nil {
|
||||||
|
return c.containerStore
|
||||||
|
}
|
||||||
return NewRemoteContainerStore(containersapi.NewContainersClient(c.conn))
|
return NewRemoteContainerStore(containersapi.NewContainersClient(c.conn))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContentStore returns the underlying content Store
|
// ContentStore returns the underlying content Store
|
||||||
func (c *Client) ContentStore() content.Store {
|
func (c *Client) ContentStore() content.Store {
|
||||||
|
if c.contentStore != nil {
|
||||||
|
return c.contentStore
|
||||||
|
}
|
||||||
return NewContentStoreFromClient(contentapi.NewContentClient(c.conn))
|
return NewContentStoreFromClient(contentapi.NewContentClient(c.conn))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SnapshotService returns the underlying snapshotter for the provided snapshotter name
|
// SnapshotService returns the underlying snapshotter for the provided snapshotter name
|
||||||
func (c *Client) SnapshotService(snapshotterName string) snapshots.Snapshotter {
|
func (c *Client) SnapshotService(snapshotterName string) snapshots.Snapshotter {
|
||||||
|
if c.snapshotters != nil {
|
||||||
|
return c.snapshotters[snapshotterName]
|
||||||
|
}
|
||||||
return NewSnapshotterFromClient(snapshotsapi.NewSnapshotsClient(c.conn), snapshotterName)
|
return NewSnapshotterFromClient(snapshotsapi.NewSnapshotsClient(c.conn), snapshotterName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TaskService returns the underlying TasksClient
|
// TaskService returns the underlying TasksClient
|
||||||
func (c *Client) TaskService() tasks.TasksClient {
|
func (c *Client) TaskService() tasks.TasksClient {
|
||||||
|
if c.taskService != nil {
|
||||||
|
return c.taskService
|
||||||
|
}
|
||||||
return tasks.NewTasksClient(c.conn)
|
return tasks.NewTasksClient(c.conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImageService returns the underlying image Store
|
// ImageService returns the underlying image Store
|
||||||
func (c *Client) ImageService() images.Store {
|
func (c *Client) ImageService() images.Store {
|
||||||
|
if c.imageStore != nil {
|
||||||
|
return c.imageStore
|
||||||
|
}
|
||||||
return NewImageStoreFromClient(imagesapi.NewImagesClient(c.conn))
|
return NewImageStoreFromClient(imagesapi.NewImagesClient(c.conn))
|
||||||
}
|
}
|
||||||
|
|
||||||
// DiffService returns the underlying Differ
|
// DiffService returns the underlying Differ
|
||||||
func (c *Client) DiffService() DiffService {
|
func (c *Client) DiffService() DiffService {
|
||||||
|
if c.diffService != nil {
|
||||||
|
return c.diffService
|
||||||
|
}
|
||||||
return NewDiffServiceFromClient(diffapi.NewDiffClient(c.conn))
|
return NewDiffServiceFromClient(diffapi.NewDiffClient(c.conn))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -469,14 +479,25 @@ func (c *Client) IntrospectionService() introspectionapi.IntrospectionClient {
|
|||||||
return introspectionapi.NewIntrospectionClient(c.conn)
|
return introspectionapi.NewIntrospectionClient(c.conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LeasesService returns the underlying Leases Client
|
||||||
|
func (c *Client) LeasesService() leasesapi.LeasesClient {
|
||||||
|
if c.leasesService != nil {
|
||||||
|
return c.leasesService
|
||||||
|
}
|
||||||
|
return leasesapi.NewLeasesClient(c.conn)
|
||||||
|
}
|
||||||
|
|
||||||
// HealthService returns the underlying GRPC HealthClient
|
// HealthService returns the underlying GRPC HealthClient
|
||||||
func (c *Client) HealthService() grpc_health_v1.HealthClient {
|
func (c *Client) HealthService() grpc_health_v1.HealthClient {
|
||||||
return grpc_health_v1.NewHealthClient(c.conn)
|
return grpc_health_v1.NewHealthClient(c.conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EventService returns the underlying EventsClient
|
// EventService returns the underlying event service
|
||||||
func (c *Client) EventService() eventsapi.EventsClient {
|
func (c *Client) EventService() EventService {
|
||||||
return eventsapi.NewEventsClient(c.conn)
|
if c.eventService != nil {
|
||||||
|
return c.eventService
|
||||||
|
}
|
||||||
|
return NewEventServiceFromClient(eventsapi.NewEventsClient(c.conn))
|
||||||
}
|
}
|
||||||
|
|
||||||
// VersionService returns the underlying VersionClient
|
// VersionService returns the underlying VersionClient
|
||||||
@ -494,6 +515,9 @@ type Version struct {
|
|||||||
|
|
||||||
// Version returns the version of containerd that the client is connected to
|
// Version returns the version of containerd that the client is connected to
|
||||||
func (c *Client) Version(ctx context.Context) (Version, error) {
|
func (c *Client) Version(ctx context.Context) (Version, error) {
|
||||||
|
if c.conn == nil {
|
||||||
|
return Version{}, errors.New("no grpc connection available")
|
||||||
|
}
|
||||||
response, err := c.VersionService().Version(ctx, &ptypes.Empty{})
|
response, err := c.VersionService().Version(ctx, &ptypes.Empty{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Version{}, err
|
return Version{}, err
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
|
|
||||||
type clientOpts struct {
|
type clientOpts struct {
|
||||||
defaultns string
|
defaultns string
|
||||||
|
services *services
|
||||||
dialOptions []grpc.DialOption
|
dialOptions []grpc.DialOption
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,6 +50,17 @@ func WithDialOpts(opts []grpc.DialOption) ClientOpt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithServices sets services used by the client.
|
||||||
|
func WithServices(opts ...ServicesOpt) ClientOpt {
|
||||||
|
return func(c *clientOpts) error {
|
||||||
|
c.services = &services{}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(c.services)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// RemoteOpt allows the caller to set distribution options for a remote
|
// RemoteOpt allows the caller to set distribution options for a remote
|
||||||
type RemoteOpt func(*Client, *RemoteContext) error
|
type RemoteOpt func(*Client, *RemoteContext) error
|
||||||
|
|
||||||
|
@ -20,8 +20,8 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
eventsapi "github.com/containerd/containerd/api/services/events/v1"
|
|
||||||
"github.com/containerd/containerd/cmd/ctr/commands"
|
"github.com/containerd/containerd/cmd/ctr/commands"
|
||||||
|
"github.com/containerd/containerd/events"
|
||||||
"github.com/containerd/typeurl"
|
"github.com/containerd/typeurl"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
|
||||||
@ -41,15 +41,16 @@ var Command = cli.Command{
|
|||||||
}
|
}
|
||||||
defer cancel()
|
defer cancel()
|
||||||
eventsClient := client.EventService()
|
eventsClient := client.EventService()
|
||||||
events, err := eventsClient.Subscribe(ctx, &eventsapi.SubscribeRequest{
|
eventsCh, errCh := eventsClient.Subscribe(ctx, context.Args()...)
|
||||||
Filters: context.Args(),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for {
|
for {
|
||||||
e, err := events.Recv()
|
var e *events.Envelope
|
||||||
if err != nil {
|
select {
|
||||||
|
case evt, closed := <-eventsCh:
|
||||||
|
if closed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
e = evt
|
||||||
|
case err := <-errCh:
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
5
diff.go
5
diff.go
@ -22,6 +22,7 @@ import (
|
|||||||
diffapi "github.com/containerd/containerd/api/services/diff/v1"
|
diffapi "github.com/containerd/containerd/api/services/diff/v1"
|
||||||
"github.com/containerd/containerd/api/types"
|
"github.com/containerd/containerd/api/types"
|
||||||
"github.com/containerd/containerd/diff"
|
"github.com/containerd/containerd/diff"
|
||||||
|
"github.com/containerd/containerd/errdefs"
|
||||||
"github.com/containerd/containerd/mount"
|
"github.com/containerd/containerd/mount"
|
||||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
)
|
)
|
||||||
@ -51,7 +52,7 @@ func (r *diffRemote) Apply(ctx context.Context, diff ocispec.Descriptor, mounts
|
|||||||
}
|
}
|
||||||
resp, err := r.client.Apply(ctx, req)
|
resp, err := r.client.Apply(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ocispec.Descriptor{}, err
|
return ocispec.Descriptor{}, errdefs.FromGRPC(err)
|
||||||
}
|
}
|
||||||
return toDescriptor(resp.Applied), nil
|
return toDescriptor(resp.Applied), nil
|
||||||
}
|
}
|
||||||
@ -72,7 +73,7 @@ func (r *diffRemote) Compare(ctx context.Context, a, b []mount.Mount, opts ...di
|
|||||||
}
|
}
|
||||||
resp, err := r.client.Diff(ctx, req)
|
resp, err := r.client.Diff(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ocispec.Descriptor{}, err
|
return ocispec.Descriptor{}, errdefs.FromGRPC(err)
|
||||||
}
|
}
|
||||||
return toDescriptor(resp.Diff), nil
|
return toDescriptor(resp.Diff), nil
|
||||||
}
|
}
|
||||||
|
119
events.go
Normal file
119
events.go
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
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 containerd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
eventsapi "github.com/containerd/containerd/api/services/events/v1"
|
||||||
|
"github.com/containerd/containerd/errdefs"
|
||||||
|
"github.com/containerd/containerd/events"
|
||||||
|
"github.com/containerd/typeurl"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EventService handles the publish, forward and subscribe of events.
|
||||||
|
type EventService interface {
|
||||||
|
events.Publisher
|
||||||
|
events.Forwarder
|
||||||
|
events.Subscriber
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEventServiceFromClient returns a new event service which communicates
|
||||||
|
// over a GRPC connection.
|
||||||
|
func NewEventServiceFromClient(client eventsapi.EventsClient) EventService {
|
||||||
|
return &eventRemote{
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type eventRemote struct {
|
||||||
|
client eventsapi.EventsClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *eventRemote) Publish(ctx context.Context, topic string, event events.Event) error {
|
||||||
|
any, err := typeurl.MarshalAny(event)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req := &eventsapi.PublishRequest{
|
||||||
|
Topic: topic,
|
||||||
|
Event: any,
|
||||||
|
}
|
||||||
|
if _, err := e.client.Publish(ctx, req); err != nil {
|
||||||
|
return errdefs.FromGRPC(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *eventRemote) Forward(ctx context.Context, envelope *events.Envelope) error {
|
||||||
|
req := &eventsapi.ForwardRequest{
|
||||||
|
Envelope: &eventsapi.Envelope{
|
||||||
|
Timestamp: envelope.Timestamp,
|
||||||
|
Namespace: envelope.Namespace,
|
||||||
|
Topic: envelope.Topic,
|
||||||
|
Event: envelope.Event,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if _, err := e.client.Forward(ctx, req); err != nil {
|
||||||
|
return errdefs.FromGRPC(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *eventRemote) Subscribe(ctx context.Context, filters ...string) (ch <-chan *events.Envelope, errs <-chan error) {
|
||||||
|
var (
|
||||||
|
evq = make(chan *events.Envelope)
|
||||||
|
errq = make(chan error, 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
errs = errq
|
||||||
|
ch = evq
|
||||||
|
|
||||||
|
session, err := e.client.Subscribe(ctx, &eventsapi.SubscribeRequest{
|
||||||
|
Filters: filters,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
errq <- err
|
||||||
|
close(errq)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(errq)
|
||||||
|
|
||||||
|
for {
|
||||||
|
ev, err := session.Recv()
|
||||||
|
if err != nil {
|
||||||
|
errq <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case evq <- &events.Envelope{
|
||||||
|
Timestamp: ev.Timestamp,
|
||||||
|
Namespace: ev.Namespace,
|
||||||
|
Topic: ev.Topic,
|
||||||
|
Event: ev.Event,
|
||||||
|
}:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ch, errs
|
||||||
|
}
|
6
lease.go
6
lease.go
@ -36,7 +36,7 @@ type Lease struct {
|
|||||||
|
|
||||||
// CreateLease creates a new lease
|
// CreateLease creates a new lease
|
||||||
func (c *Client) CreateLease(ctx context.Context) (Lease, error) {
|
func (c *Client) CreateLease(ctx context.Context) (Lease, error) {
|
||||||
lapi := leasesapi.NewLeasesClient(c.conn)
|
lapi := c.LeasesService()
|
||||||
resp, err := lapi.Create(ctx, &leasesapi.CreateRequest{})
|
resp, err := lapi.Create(ctx, &leasesapi.CreateRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Lease{}, err
|
return Lease{}, err
|
||||||
@ -50,7 +50,7 @@ func (c *Client) CreateLease(ctx context.Context) (Lease, error) {
|
|||||||
|
|
||||||
// ListLeases lists active leases
|
// ListLeases lists active leases
|
||||||
func (c *Client) ListLeases(ctx context.Context) ([]Lease, error) {
|
func (c *Client) ListLeases(ctx context.Context) ([]Lease, error) {
|
||||||
lapi := leasesapi.NewLeasesClient(c.conn)
|
lapi := c.LeasesService()
|
||||||
resp, err := lapi.List(ctx, &leasesapi.ListRequest{})
|
resp, err := lapi.List(ctx, &leasesapi.ListRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -100,7 +100,7 @@ func (l Lease) CreatedAt() time.Time {
|
|||||||
// Delete deletes the lease, removing the reference to all resources created
|
// Delete deletes the lease, removing the reference to all resources created
|
||||||
// during the lease.
|
// during the lease.
|
||||||
func (l Lease) Delete(ctx context.Context) error {
|
func (l Lease) Delete(ctx context.Context) error {
|
||||||
lapi := leasesapi.NewLeasesClient(l.client.conn)
|
lapi := l.client.LeasesService()
|
||||||
_, err := lapi.Delete(ctx, &leasesapi.DeleteRequest{
|
_, err := lapi.Delete(ctx, &leasesapi.DeleteRequest{
|
||||||
ID: l.id,
|
ID: l.id,
|
||||||
})
|
})
|
||||||
|
@ -195,6 +195,15 @@ func (m *DB) Snapshotter(name string) snapshots.Snapshotter {
|
|||||||
return sn
|
return sn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Snapshotters returns all available snapshotters.
|
||||||
|
func (m *DB) Snapshotters() map[string]snapshots.Snapshotter {
|
||||||
|
ss := make(map[string]snapshots.Snapshotter, len(m.ss))
|
||||||
|
for n, sn := range m.ss {
|
||||||
|
ss[n] = sn
|
||||||
|
}
|
||||||
|
return ss
|
||||||
|
}
|
||||||
|
|
||||||
// View runs a readonly transaction on the metadata store.
|
// View runs a readonly transaction on the metadata store.
|
||||||
func (m *DB) View(fn func(*bolt.Tx) error) error {
|
func (m *DB) View(fn func(*bolt.Tx) error) error {
|
||||||
return m.db.View(fn)
|
return m.db.View(fn)
|
||||||
|
@ -58,6 +58,8 @@ const (
|
|||||||
AllPlugins Type = "*"
|
AllPlugins Type = "*"
|
||||||
// RuntimePlugin implements a runtime
|
// RuntimePlugin implements a runtime
|
||||||
RuntimePlugin Type = "io.containerd.runtime.v1"
|
RuntimePlugin Type = "io.containerd.runtime.v1"
|
||||||
|
// ServicePlugin implements a internal service
|
||||||
|
ServicePlugin Type = "io.containerd.service.v1"
|
||||||
// GRPCPlugin implements a grpc service
|
// GRPCPlugin implements a grpc service
|
||||||
GRPCPlugin Type = "io.containerd.grpc.v1"
|
GRPCPlugin Type = "io.containerd.grpc.v1"
|
||||||
// SnapshotPlugin implements a snapshotter
|
// SnapshotPlugin implements a snapshotter
|
||||||
|
112
services.go
Normal file
112
services.go
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
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 containerd
|
||||||
|
|
||||||
|
import (
|
||||||
|
containersapi "github.com/containerd/containerd/api/services/containers/v1"
|
||||||
|
"github.com/containerd/containerd/api/services/diff/v1"
|
||||||
|
imagesapi "github.com/containerd/containerd/api/services/images/v1"
|
||||||
|
"github.com/containerd/containerd/api/services/leases/v1"
|
||||||
|
namespacesapi "github.com/containerd/containerd/api/services/namespaces/v1"
|
||||||
|
"github.com/containerd/containerd/api/services/tasks/v1"
|
||||||
|
"github.com/containerd/containerd/containers"
|
||||||
|
"github.com/containerd/containerd/content"
|
||||||
|
"github.com/containerd/containerd/images"
|
||||||
|
"github.com/containerd/containerd/namespaces"
|
||||||
|
"github.com/containerd/containerd/snapshots"
|
||||||
|
)
|
||||||
|
|
||||||
|
type services struct {
|
||||||
|
contentStore content.Store
|
||||||
|
imageStore images.Store
|
||||||
|
containerStore containers.Store
|
||||||
|
namespaceStore namespaces.Store
|
||||||
|
snapshotters map[string]snapshots.Snapshotter
|
||||||
|
taskService tasks.TasksClient
|
||||||
|
diffService DiffService
|
||||||
|
eventService EventService
|
||||||
|
leasesService leases.LeasesClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServicesOpt allows callers to set options on the services
|
||||||
|
type ServicesOpt func(c *services)
|
||||||
|
|
||||||
|
// WithContentStore sets the content store.
|
||||||
|
func WithContentStore(contentStore content.Store) ServicesOpt {
|
||||||
|
return func(s *services) {
|
||||||
|
s.contentStore = contentStore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithImageService sets the image service.
|
||||||
|
func WithImageService(imageService imagesapi.ImagesClient) ServicesOpt {
|
||||||
|
return func(s *services) {
|
||||||
|
s.imageStore = NewImageStoreFromClient(imageService)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSnapshotters sets the snapshotters.
|
||||||
|
func WithSnapshotters(snapshotters map[string]snapshots.Snapshotter) ServicesOpt {
|
||||||
|
return func(s *services) {
|
||||||
|
s.snapshotters = make(map[string]snapshots.Snapshotter)
|
||||||
|
for n, sn := range snapshotters {
|
||||||
|
s.snapshotters[n] = sn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithContainerService sets the container service.
|
||||||
|
func WithContainerService(containerService containersapi.ContainersClient) ServicesOpt {
|
||||||
|
return func(s *services) {
|
||||||
|
s.containerStore = NewRemoteContainerStore(containerService)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithTaskService sets the task service.
|
||||||
|
func WithTaskService(taskService tasks.TasksClient) ServicesOpt {
|
||||||
|
return func(s *services) {
|
||||||
|
s.taskService = taskService
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDiffService sets the diff service.
|
||||||
|
func WithDiffService(diffService diff.DiffClient) ServicesOpt {
|
||||||
|
return func(s *services) {
|
||||||
|
s.diffService = NewDiffServiceFromClient(diffService)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithEventService sets the event service.
|
||||||
|
func WithEventService(eventService EventService) ServicesOpt {
|
||||||
|
return func(s *services) {
|
||||||
|
s.eventService = eventService
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithNamespaceService sets the namespace service.
|
||||||
|
func WithNamespaceService(namespaceService namespacesapi.NamespacesClient) ServicesOpt {
|
||||||
|
return func(s *services) {
|
||||||
|
s.namespaceStore = NewNamespaceStoreFromClient(namespaceService)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLeasesService sets the lease service.
|
||||||
|
func WithLeasesService(leasesService leases.LeasesClient) ServicesOpt {
|
||||||
|
return func(s *services) {
|
||||||
|
s.leasesService = leasesService
|
||||||
|
}
|
||||||
|
}
|
189
services/containers/local.go
Normal file
189
services/containers/local.go
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
/*
|
||||||
|
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 containers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/boltdb/bolt"
|
||||||
|
eventstypes "github.com/containerd/containerd/api/events"
|
||||||
|
api "github.com/containerd/containerd/api/services/containers/v1"
|
||||||
|
"github.com/containerd/containerd/containers"
|
||||||
|
"github.com/containerd/containerd/errdefs"
|
||||||
|
"github.com/containerd/containerd/events"
|
||||||
|
"github.com/containerd/containerd/metadata"
|
||||||
|
"github.com/containerd/containerd/plugin"
|
||||||
|
"github.com/containerd/containerd/services"
|
||||||
|
ptypes "github.com/gogo/protobuf/types"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
plugin.Register(&plugin.Registration{
|
||||||
|
Type: plugin.ServicePlugin,
|
||||||
|
ID: services.ContainersService,
|
||||||
|
Requires: []plugin.Type{
|
||||||
|
plugin.MetadataPlugin,
|
||||||
|
},
|
||||||
|
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
||||||
|
m, err := ic.Get(plugin.MetadataPlugin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &local{
|
||||||
|
db: m.(*metadata.DB),
|
||||||
|
publisher: ic.Events,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type local struct {
|
||||||
|
db *metadata.DB
|
||||||
|
publisher events.Publisher
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ api.ContainersClient = &local{}
|
||||||
|
|
||||||
|
func (l *local) Get(ctx context.Context, req *api.GetContainerRequest, _ ...grpc.CallOption) (*api.GetContainerResponse, error) {
|
||||||
|
var resp api.GetContainerResponse
|
||||||
|
|
||||||
|
return &resp, errdefs.ToGRPC(l.withStoreView(ctx, func(ctx context.Context, store containers.Store) error {
|
||||||
|
container, err := store.Get(ctx, req.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
containerpb := containerToProto(&container)
|
||||||
|
resp.Container = containerpb
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) List(ctx context.Context, req *api.ListContainersRequest, _ ...grpc.CallOption) (*api.ListContainersResponse, error) {
|
||||||
|
var resp api.ListContainersResponse
|
||||||
|
|
||||||
|
return &resp, errdefs.ToGRPC(l.withStoreView(ctx, func(ctx context.Context, store containers.Store) error {
|
||||||
|
containers, err := store.List(ctx, req.Filters...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Containers = containersToProto(containers)
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Create(ctx context.Context, req *api.CreateContainerRequest, _ ...grpc.CallOption) (*api.CreateContainerResponse, error) {
|
||||||
|
var resp api.CreateContainerResponse
|
||||||
|
|
||||||
|
if err := l.withStoreUpdate(ctx, func(ctx context.Context, store containers.Store) error {
|
||||||
|
container := containerFromProto(&req.Container)
|
||||||
|
|
||||||
|
created, err := store.Create(ctx, container)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Container = containerToProto(&created)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return &resp, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
if err := l.publisher.Publish(ctx, "/containers/create", &eventstypes.ContainerCreate{
|
||||||
|
ID: resp.Container.ID,
|
||||||
|
Image: resp.Container.Image,
|
||||||
|
Runtime: &eventstypes.ContainerCreate_Runtime{
|
||||||
|
Name: resp.Container.Runtime.Name,
|
||||||
|
Options: resp.Container.Runtime.Options,
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
|
return &resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Update(ctx context.Context, req *api.UpdateContainerRequest, _ ...grpc.CallOption) (*api.UpdateContainerResponse, error) {
|
||||||
|
if req.Container.ID == "" {
|
||||||
|
return nil, status.Errorf(codes.InvalidArgument, "Container.ID required")
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
resp api.UpdateContainerResponse
|
||||||
|
container = containerFromProto(&req.Container)
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := l.withStoreUpdate(ctx, func(ctx context.Context, store containers.Store) error {
|
||||||
|
var fieldpaths []string
|
||||||
|
if req.UpdateMask != nil && len(req.UpdateMask.Paths) > 0 {
|
||||||
|
for _, path := range req.UpdateMask.Paths {
|
||||||
|
fieldpaths = append(fieldpaths, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updated, err := store.Update(ctx, container, fieldpaths...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Container = containerToProto(&updated)
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return &resp, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := l.publisher.Publish(ctx, "/containers/update", &eventstypes.ContainerUpdate{
|
||||||
|
ID: resp.Container.ID,
|
||||||
|
Image: resp.Container.Image,
|
||||||
|
Labels: resp.Container.Labels,
|
||||||
|
SnapshotKey: resp.Container.SnapshotKey,
|
||||||
|
}); err != nil {
|
||||||
|
return &resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Delete(ctx context.Context, req *api.DeleteContainerRequest, _ ...grpc.CallOption) (*ptypes.Empty, error) {
|
||||||
|
if err := l.withStoreUpdate(ctx, func(ctx context.Context, store containers.Store) error {
|
||||||
|
return store.Delete(ctx, req.ID)
|
||||||
|
}); err != nil {
|
||||||
|
return &ptypes.Empty{}, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := l.publisher.Publish(ctx, "/containers/delete", &eventstypes.ContainerDelete{
|
||||||
|
ID: req.ID,
|
||||||
|
}); err != nil {
|
||||||
|
return &ptypes.Empty{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ptypes.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) withStore(ctx context.Context, fn func(ctx context.Context, store containers.Store) error) func(tx *bolt.Tx) error {
|
||||||
|
return func(tx *bolt.Tx) error { return fn(ctx, metadata.NewContainerStore(tx)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) withStoreView(ctx context.Context, fn func(ctx context.Context, store containers.Store) error) error {
|
||||||
|
return l.db.View(l.withStore(ctx, fn))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) withStoreUpdate(ctx context.Context, fn func(ctx context.Context, store containers.Store) error) error {
|
||||||
|
return l.db.Update(l.withStore(ctx, fn))
|
||||||
|
}
|
@ -17,19 +17,13 @@
|
|||||||
package containers
|
package containers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/boltdb/bolt"
|
|
||||||
eventstypes "github.com/containerd/containerd/api/events"
|
|
||||||
api "github.com/containerd/containerd/api/services/containers/v1"
|
api "github.com/containerd/containerd/api/services/containers/v1"
|
||||||
"github.com/containerd/containerd/containers"
|
|
||||||
"github.com/containerd/containerd/errdefs"
|
|
||||||
"github.com/containerd/containerd/events"
|
|
||||||
"github.com/containerd/containerd/metadata"
|
|
||||||
"github.com/containerd/containerd/plugin"
|
"github.com/containerd/containerd/plugin"
|
||||||
|
"github.com/containerd/containerd/services"
|
||||||
ptypes "github.com/gogo/protobuf/types"
|
ptypes "github.com/gogo/protobuf/types"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -37,27 +31,31 @@ func init() {
|
|||||||
Type: plugin.GRPCPlugin,
|
Type: plugin.GRPCPlugin,
|
||||||
ID: "containers",
|
ID: "containers",
|
||||||
Requires: []plugin.Type{
|
Requires: []plugin.Type{
|
||||||
plugin.MetadataPlugin,
|
plugin.ServicePlugin,
|
||||||
},
|
},
|
||||||
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
||||||
m, err := ic.Get(plugin.MetadataPlugin)
|
plugins, err := ic.GetByType(plugin.ServicePlugin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewService(m.(*metadata.DB), ic.Events), nil
|
p, ok := plugins[services.ContainersService]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("containers service not found")
|
||||||
|
}
|
||||||
|
i, err := p.Instance()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &service{local: i.(api.ContainersClient)}, nil
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type service struct {
|
type service struct {
|
||||||
db *metadata.DB
|
local api.ContainersClient
|
||||||
publisher events.Publisher
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewService returns the container GRPC server
|
var _ api.ContainersServer = &service{}
|
||||||
func NewService(db *metadata.DB, publisher events.Publisher) api.ContainersServer {
|
|
||||||
return &service{db: db, publisher: publisher}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) Register(server *grpc.Server) error {
|
func (s *service) Register(server *grpc.Server) error {
|
||||||
api.RegisterContainersServer(server, s)
|
api.RegisterContainersServer(server, s)
|
||||||
@ -65,129 +63,21 @@ func (s *service) Register(server *grpc.Server) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Get(ctx context.Context, req *api.GetContainerRequest) (*api.GetContainerResponse, error) {
|
func (s *service) Get(ctx context.Context, req *api.GetContainerRequest) (*api.GetContainerResponse, error) {
|
||||||
var resp api.GetContainerResponse
|
return s.local.Get(ctx, req)
|
||||||
|
|
||||||
return &resp, errdefs.ToGRPC(s.withStoreView(ctx, func(ctx context.Context, store containers.Store) error {
|
|
||||||
container, err := store.Get(ctx, req.ID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
containerpb := containerToProto(&container)
|
|
||||||
resp.Container = containerpb
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) List(ctx context.Context, req *api.ListContainersRequest) (*api.ListContainersResponse, error) {
|
func (s *service) List(ctx context.Context, req *api.ListContainersRequest) (*api.ListContainersResponse, error) {
|
||||||
var resp api.ListContainersResponse
|
return s.local.List(ctx, req)
|
||||||
|
|
||||||
return &resp, errdefs.ToGRPC(s.withStoreView(ctx, func(ctx context.Context, store containers.Store) error {
|
|
||||||
containers, err := store.List(ctx, req.Filters...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.Containers = containersToProto(containers)
|
|
||||||
return nil
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Create(ctx context.Context, req *api.CreateContainerRequest) (*api.CreateContainerResponse, error) {
|
func (s *service) Create(ctx context.Context, req *api.CreateContainerRequest) (*api.CreateContainerResponse, error) {
|
||||||
var resp api.CreateContainerResponse
|
return s.local.Create(ctx, req)
|
||||||
|
|
||||||
if err := s.withStoreUpdate(ctx, func(ctx context.Context, store containers.Store) error {
|
|
||||||
container := containerFromProto(&req.Container)
|
|
||||||
|
|
||||||
created, err := store.Create(ctx, container)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.Container = containerToProto(&created)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
return &resp, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
if err := s.publisher.Publish(ctx, "/containers/create", &eventstypes.ContainerCreate{
|
|
||||||
ID: resp.Container.ID,
|
|
||||||
Image: resp.Container.Image,
|
|
||||||
Runtime: &eventstypes.ContainerCreate_Runtime{
|
|
||||||
Name: resp.Container.Runtime.Name,
|
|
||||||
Options: resp.Container.Runtime.Options,
|
|
||||||
},
|
|
||||||
}); err != nil {
|
|
||||||
return &resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &resp, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Update(ctx context.Context, req *api.UpdateContainerRequest) (*api.UpdateContainerResponse, error) {
|
func (s *service) Update(ctx context.Context, req *api.UpdateContainerRequest) (*api.UpdateContainerResponse, error) {
|
||||||
if req.Container.ID == "" {
|
return s.local.Update(ctx, req)
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "Container.ID required")
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
resp api.UpdateContainerResponse
|
|
||||||
container = containerFromProto(&req.Container)
|
|
||||||
)
|
|
||||||
|
|
||||||
if err := s.withStoreUpdate(ctx, func(ctx context.Context, store containers.Store) error {
|
|
||||||
var fieldpaths []string
|
|
||||||
if req.UpdateMask != nil && len(req.UpdateMask.Paths) > 0 {
|
|
||||||
for _, path := range req.UpdateMask.Paths {
|
|
||||||
fieldpaths = append(fieldpaths, path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updated, err := store.Update(ctx, container, fieldpaths...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.Container = containerToProto(&updated)
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
return &resp, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.publisher.Publish(ctx, "/containers/update", &eventstypes.ContainerUpdate{
|
|
||||||
ID: resp.Container.ID,
|
|
||||||
Image: resp.Container.Image,
|
|
||||||
Labels: resp.Container.Labels,
|
|
||||||
SnapshotKey: resp.Container.SnapshotKey,
|
|
||||||
}); err != nil {
|
|
||||||
return &resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &resp, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Delete(ctx context.Context, req *api.DeleteContainerRequest) (*ptypes.Empty, error) {
|
func (s *service) Delete(ctx context.Context, req *api.DeleteContainerRequest) (*ptypes.Empty, error) {
|
||||||
if err := s.withStoreUpdate(ctx, func(ctx context.Context, store containers.Store) error {
|
return s.local.Delete(ctx, req)
|
||||||
return store.Delete(ctx, req.ID)
|
|
||||||
}); err != nil {
|
|
||||||
return &ptypes.Empty{}, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.publisher.Publish(ctx, "/containers/delete", &eventstypes.ContainerDelete{
|
|
||||||
ID: req.ID,
|
|
||||||
}); err != nil {
|
|
||||||
return &ptypes.Empty{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &ptypes.Empty{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) withStore(ctx context.Context, fn func(ctx context.Context, store containers.Store) error) func(tx *bolt.Tx) error {
|
|
||||||
return func(tx *bolt.Tx) error { return fn(ctx, metadata.NewContainerStore(tx)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) withStoreView(ctx context.Context, fn func(ctx context.Context, store containers.Store) error) error {
|
|
||||||
return s.db.View(s.withStore(ctx, fn))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) withStoreUpdate(ctx context.Context, fn func(ctx context.Context, store containers.Store) error) error {
|
|
||||||
return s.db.Update(s.withStore(ctx, fn))
|
|
||||||
}
|
}
|
||||||
|
@ -20,14 +20,12 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
eventstypes "github.com/containerd/containerd/api/events"
|
|
||||||
api "github.com/containerd/containerd/api/services/content/v1"
|
api "github.com/containerd/containerd/api/services/content/v1"
|
||||||
"github.com/containerd/containerd/content"
|
"github.com/containerd/containerd/content"
|
||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
"github.com/containerd/containerd/events"
|
|
||||||
"github.com/containerd/containerd/log"
|
"github.com/containerd/containerd/log"
|
||||||
"github.com/containerd/containerd/metadata"
|
|
||||||
"github.com/containerd/containerd/plugin"
|
"github.com/containerd/containerd/plugin"
|
||||||
|
"github.com/containerd/containerd/services"
|
||||||
ptypes "github.com/gogo/protobuf/types"
|
ptypes "github.com/gogo/protobuf/types"
|
||||||
digest "github.com/opencontainers/go-digest"
|
digest "github.com/opencontainers/go-digest"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -40,7 +38,6 @@ import (
|
|||||||
|
|
||||||
type service struct {
|
type service struct {
|
||||||
store content.Store
|
store content.Store
|
||||||
publisher events.Publisher
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var bufPool = sync.Pool{
|
var bufPool = sync.Pool{
|
||||||
@ -57,26 +54,29 @@ func init() {
|
|||||||
Type: plugin.GRPCPlugin,
|
Type: plugin.GRPCPlugin,
|
||||||
ID: "content",
|
ID: "content",
|
||||||
Requires: []plugin.Type{
|
Requires: []plugin.Type{
|
||||||
plugin.MetadataPlugin,
|
plugin.ServicePlugin,
|
||||||
},
|
},
|
||||||
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
||||||
m, err := ic.Get(plugin.MetadataPlugin)
|
plugins, err := ic.GetByType(plugin.ServicePlugin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
p, ok := plugins[services.ContentService]
|
||||||
s, err := NewService(m.(*metadata.DB).ContentStore(), ic.Events)
|
if !ok {
|
||||||
return s, err
|
return nil, errors.New("content store service not found")
|
||||||
|
}
|
||||||
|
cs, err := p.Instance()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newService(cs.(content.Store)), nil
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewService returns the content GRPC server
|
// newService returns the content GRPC server
|
||||||
func NewService(cs content.Store, publisher events.Publisher) (api.ContentServer, error) {
|
func newService(cs content.Store) api.ContentServer {
|
||||||
return &service{
|
return &service{store: cs}
|
||||||
store: cs,
|
|
||||||
publisher: publisher,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Register(server *grpc.Server) error {
|
func (s *service) Register(server *grpc.Server) error {
|
||||||
@ -166,12 +166,6 @@ func (s *service) Delete(ctx context.Context, req *api.DeleteContentRequest) (*p
|
|||||||
return nil, errdefs.ToGRPC(err)
|
return nil, errdefs.ToGRPC(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.publisher.Publish(ctx, "/content/delete", &eventstypes.ContentDelete{
|
|
||||||
Digest: req.Digest,
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &ptypes.Empty{}, nil
|
return &ptypes.Empty{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
71
services/content/store.go
Normal file
71
services/content/store.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
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 content
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
eventstypes "github.com/containerd/containerd/api/events"
|
||||||
|
"github.com/containerd/containerd/content"
|
||||||
|
"github.com/containerd/containerd/events"
|
||||||
|
"github.com/containerd/containerd/metadata"
|
||||||
|
"github.com/containerd/containerd/plugin"
|
||||||
|
"github.com/containerd/containerd/services"
|
||||||
|
digest "github.com/opencontainers/go-digest"
|
||||||
|
)
|
||||||
|
|
||||||
|
// store wraps content.Store with proper event published.
|
||||||
|
type store struct {
|
||||||
|
content.Store
|
||||||
|
publisher events.Publisher
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
plugin.Register(&plugin.Registration{
|
||||||
|
Type: plugin.ServicePlugin,
|
||||||
|
ID: services.ContentService,
|
||||||
|
Requires: []plugin.Type{
|
||||||
|
plugin.MetadataPlugin,
|
||||||
|
},
|
||||||
|
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
||||||
|
m, err := ic.Get(plugin.MetadataPlugin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := newContentStore(m.(*metadata.DB).ContentStore(), ic.Events)
|
||||||
|
return s, err
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func newContentStore(cs content.Store, publisher events.Publisher) (content.Store, error) {
|
||||||
|
return &store{
|
||||||
|
Store: cs,
|
||||||
|
publisher: publisher,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *store) Delete(ctx context.Context, dgst digest.Digest) error {
|
||||||
|
if err := s.Store.Delete(ctx, dgst); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// TODO: Consider whether we should return error here.
|
||||||
|
return s.publisher.Publish(ctx, "/content/delete", &eventstypes.ContentDelete{
|
||||||
|
Digest: dgst,
|
||||||
|
})
|
||||||
|
}
|
178
services/diff/local.go
Normal file
178
services/diff/local.go
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
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 diff
|
||||||
|
|
||||||
|
import (
|
||||||
|
diffapi "github.com/containerd/containerd/api/services/diff/v1"
|
||||||
|
"github.com/containerd/containerd/api/types"
|
||||||
|
"github.com/containerd/containerd/diff"
|
||||||
|
"github.com/containerd/containerd/errdefs"
|
||||||
|
"github.com/containerd/containerd/mount"
|
||||||
|
"github.com/containerd/containerd/plugin"
|
||||||
|
"github.com/containerd/containerd/services"
|
||||||
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
// Order is the order of preference in which to try diff algorithms, the
|
||||||
|
// first differ which is supported is used.
|
||||||
|
// Note when multiple differs may be supported, this order will be
|
||||||
|
// respected for which is choosen. Each differ should return the same
|
||||||
|
// correct output, allowing any ordering to be used to prefer
|
||||||
|
// more optimimal implementations.
|
||||||
|
Order []string `toml:"default"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type differ interface {
|
||||||
|
diff.Comparer
|
||||||
|
diff.Applier
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
plugin.Register(&plugin.Registration{
|
||||||
|
Type: plugin.ServicePlugin,
|
||||||
|
ID: services.DiffService,
|
||||||
|
Requires: []plugin.Type{
|
||||||
|
plugin.DiffPlugin,
|
||||||
|
},
|
||||||
|
Config: defaultDifferConfig,
|
||||||
|
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
||||||
|
differs, err := ic.GetByType(plugin.DiffPlugin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
orderedNames := ic.Config.(*config).Order
|
||||||
|
ordered := make([]differ, len(orderedNames))
|
||||||
|
for i, n := range orderedNames {
|
||||||
|
differp, ok := differs[n]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.Errorf("needed differ not loaded: %s", n)
|
||||||
|
}
|
||||||
|
d, err := differp.Instance()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "could not load required differ due plugin init error: %s", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
ordered[i], ok = d.(differ)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.Errorf("differ does not implement Comparer and Applier interface: %s", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &local{
|
||||||
|
differs: ordered,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type local struct {
|
||||||
|
differs []differ
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ diffapi.DiffClient = &local{}
|
||||||
|
|
||||||
|
func (l *local) Apply(ctx context.Context, er *diffapi.ApplyRequest, _ ...grpc.CallOption) (*diffapi.ApplyResponse, error) {
|
||||||
|
var (
|
||||||
|
ocidesc ocispec.Descriptor
|
||||||
|
err error
|
||||||
|
desc = toDescriptor(er.Diff)
|
||||||
|
mounts = toMounts(er.Mounts)
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, differ := range l.differs {
|
||||||
|
ocidesc, err = differ.Apply(ctx, desc, mounts)
|
||||||
|
if !errdefs.IsNotImplemented(err) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &diffapi.ApplyResponse{
|
||||||
|
Applied: fromDescriptor(ocidesc),
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Diff(ctx context.Context, dr *diffapi.DiffRequest, _ ...grpc.CallOption) (*diffapi.DiffResponse, error) {
|
||||||
|
var (
|
||||||
|
ocidesc ocispec.Descriptor
|
||||||
|
err error
|
||||||
|
aMounts = toMounts(dr.Left)
|
||||||
|
bMounts = toMounts(dr.Right)
|
||||||
|
)
|
||||||
|
|
||||||
|
var opts []diff.Opt
|
||||||
|
if dr.MediaType != "" {
|
||||||
|
opts = append(opts, diff.WithMediaType(dr.MediaType))
|
||||||
|
}
|
||||||
|
if dr.Ref != "" {
|
||||||
|
opts = append(opts, diff.WithReference(dr.Ref))
|
||||||
|
}
|
||||||
|
if dr.Labels != nil {
|
||||||
|
opts = append(opts, diff.WithLabels(dr.Labels))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, d := range l.differs {
|
||||||
|
ocidesc, err = d.Compare(ctx, aMounts, bMounts, opts...)
|
||||||
|
if !errdefs.IsNotImplemented(err) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &diffapi.DiffResponse{
|
||||||
|
Diff: fromDescriptor(ocidesc),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func toMounts(apim []*types.Mount) []mount.Mount {
|
||||||
|
mounts := make([]mount.Mount, len(apim))
|
||||||
|
for i, m := range apim {
|
||||||
|
mounts[i] = mount.Mount{
|
||||||
|
Type: m.Type,
|
||||||
|
Source: m.Source,
|
||||||
|
Options: m.Options,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mounts
|
||||||
|
}
|
||||||
|
|
||||||
|
func toDescriptor(d *types.Descriptor) ocispec.Descriptor {
|
||||||
|
return ocispec.Descriptor{
|
||||||
|
MediaType: d.MediaType,
|
||||||
|
Digest: d.Digest,
|
||||||
|
Size: d.Size_,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fromDescriptor(d ocispec.Descriptor) *types.Descriptor {
|
||||||
|
return &types.Descriptor{
|
||||||
|
MediaType: d.MediaType,
|
||||||
|
Digest: d.Digest,
|
||||||
|
Size_: d.Size,
|
||||||
|
}
|
||||||
|
}
|
@ -18,163 +18,53 @@ package diff
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
diffapi "github.com/containerd/containerd/api/services/diff/v1"
|
diffapi "github.com/containerd/containerd/api/services/diff/v1"
|
||||||
"github.com/containerd/containerd/api/types"
|
|
||||||
"github.com/containerd/containerd/diff"
|
|
||||||
"github.com/containerd/containerd/errdefs"
|
|
||||||
"github.com/containerd/containerd/mount"
|
|
||||||
"github.com/containerd/containerd/plugin"
|
"github.com/containerd/containerd/plugin"
|
||||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
"github.com/containerd/containerd/services"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
type config struct {
|
|
||||||
// Order is the order of preference in which to try diff algorithms, the
|
|
||||||
// first differ which is supported is used.
|
|
||||||
// Note when multiple differs may be supported, this order will be
|
|
||||||
// respected for which is choosen. Each differ should return the same
|
|
||||||
// correct output, allowing any ordering to be used to prefer
|
|
||||||
// more optimimal implementations.
|
|
||||||
Order []string `toml:"default"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type differ interface {
|
|
||||||
diff.Comparer
|
|
||||||
diff.Applier
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
plugin.Register(&plugin.Registration{
|
plugin.Register(&plugin.Registration{
|
||||||
Type: plugin.GRPCPlugin,
|
Type: plugin.GRPCPlugin,
|
||||||
ID: "diff",
|
ID: "diff",
|
||||||
Requires: []plugin.Type{
|
Requires: []plugin.Type{
|
||||||
plugin.DiffPlugin,
|
plugin.ServicePlugin,
|
||||||
},
|
},
|
||||||
Config: defaultDifferConfig,
|
|
||||||
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
||||||
differs, err := ic.GetByType(plugin.DiffPlugin)
|
plugins, err := ic.GetByType(plugin.ServicePlugin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
p, ok := plugins[services.DiffService]
|
||||||
orderedNames := ic.Config.(*config).Order
|
|
||||||
ordered := make([]differ, len(orderedNames))
|
|
||||||
for i, n := range orderedNames {
|
|
||||||
differp, ok := differs[n]
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.Errorf("needed differ not loaded: %s", n)
|
return nil, errors.New("diff service not found")
|
||||||
}
|
}
|
||||||
d, err := differp.Instance()
|
i, err := p.Instance()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "could not load required differ due plugin init error: %s", n)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return &service{local: i.(diffapi.DiffClient)}, nil
|
||||||
ordered[i], ok = d.(differ)
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.Errorf("differ does not implement Comparer and Applier interface: %s", n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &service{
|
|
||||||
differs: ordered,
|
|
||||||
}, nil
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type service struct {
|
type service struct {
|
||||||
differs []differ
|
local diffapi.DiffClient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ diffapi.DiffServer = &service{}
|
||||||
|
|
||||||
func (s *service) Register(gs *grpc.Server) error {
|
func (s *service) Register(gs *grpc.Server) error {
|
||||||
diffapi.RegisterDiffServer(gs, s)
|
diffapi.RegisterDiffServer(gs, s)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Apply(ctx context.Context, er *diffapi.ApplyRequest) (*diffapi.ApplyResponse, error) {
|
func (s *service) Apply(ctx context.Context, er *diffapi.ApplyRequest) (*diffapi.ApplyResponse, error) {
|
||||||
var (
|
return s.local.Apply(ctx, er)
|
||||||
ocidesc ocispec.Descriptor
|
|
||||||
err error
|
|
||||||
desc = toDescriptor(er.Diff)
|
|
||||||
mounts = toMounts(er.Mounts)
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, differ := range s.differs {
|
|
||||||
ocidesc, err = differ.Apply(ctx, desc, mounts)
|
|
||||||
if !errdefs.IsNotImplemented(err) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &diffapi.ApplyResponse{
|
|
||||||
Applied: fromDescriptor(ocidesc),
|
|
||||||
}, nil
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Diff(ctx context.Context, dr *diffapi.DiffRequest) (*diffapi.DiffResponse, error) {
|
func (s *service) Diff(ctx context.Context, dr *diffapi.DiffRequest) (*diffapi.DiffResponse, error) {
|
||||||
var (
|
return s.local.Diff(ctx, dr)
|
||||||
ocidesc ocispec.Descriptor
|
|
||||||
err error
|
|
||||||
aMounts = toMounts(dr.Left)
|
|
||||||
bMounts = toMounts(dr.Right)
|
|
||||||
)
|
|
||||||
|
|
||||||
var opts []diff.Opt
|
|
||||||
if dr.MediaType != "" {
|
|
||||||
opts = append(opts, diff.WithMediaType(dr.MediaType))
|
|
||||||
}
|
|
||||||
if dr.Ref != "" {
|
|
||||||
opts = append(opts, diff.WithReference(dr.Ref))
|
|
||||||
}
|
|
||||||
if dr.Labels != nil {
|
|
||||||
opts = append(opts, diff.WithLabels(dr.Labels))
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, d := range s.differs {
|
|
||||||
ocidesc, err = d.Compare(ctx, aMounts, bMounts, opts...)
|
|
||||||
if !errdefs.IsNotImplemented(err) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &diffapi.DiffResponse{
|
|
||||||
Diff: fromDescriptor(ocidesc),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func toMounts(apim []*types.Mount) []mount.Mount {
|
|
||||||
mounts := make([]mount.Mount, len(apim))
|
|
||||||
for i, m := range apim {
|
|
||||||
mounts[i] = mount.Mount{
|
|
||||||
Type: m.Type,
|
|
||||||
Source: m.Source,
|
|
||||||
Options: m.Options,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mounts
|
|
||||||
}
|
|
||||||
|
|
||||||
func toDescriptor(d *types.Descriptor) ocispec.Descriptor {
|
|
||||||
return ocispec.Descriptor{
|
|
||||||
MediaType: d.MediaType,
|
|
||||||
Digest: d.Digest,
|
|
||||||
Size: d.Size_,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func fromDescriptor(d ocispec.Descriptor) *types.Descriptor {
|
|
||||||
return &types.Descriptor{
|
|
||||||
MediaType: d.MediaType,
|
|
||||||
Digest: d.Digest,
|
|
||||||
Size_: d.Size,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
183
services/images/local.go
Normal file
183
services/images/local.go
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
/*
|
||||||
|
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 images
|
||||||
|
|
||||||
|
import (
|
||||||
|
gocontext "context"
|
||||||
|
|
||||||
|
eventstypes "github.com/containerd/containerd/api/events"
|
||||||
|
imagesapi "github.com/containerd/containerd/api/services/images/v1"
|
||||||
|
"github.com/containerd/containerd/errdefs"
|
||||||
|
"github.com/containerd/containerd/events"
|
||||||
|
"github.com/containerd/containerd/gc"
|
||||||
|
"github.com/containerd/containerd/images"
|
||||||
|
"github.com/containerd/containerd/log"
|
||||||
|
"github.com/containerd/containerd/metadata"
|
||||||
|
"github.com/containerd/containerd/plugin"
|
||||||
|
"github.com/containerd/containerd/services"
|
||||||
|
ptypes "github.com/gogo/protobuf/types"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
plugin.Register(&plugin.Registration{
|
||||||
|
Type: plugin.ServicePlugin,
|
||||||
|
ID: services.ImagesService,
|
||||||
|
Requires: []plugin.Type{
|
||||||
|
plugin.MetadataPlugin,
|
||||||
|
plugin.GCPlugin,
|
||||||
|
},
|
||||||
|
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
||||||
|
m, err := ic.Get(plugin.MetadataPlugin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
g, err := ic.Get(plugin.GCPlugin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &local{
|
||||||
|
store: metadata.NewImageStore(m.(*metadata.DB)),
|
||||||
|
publisher: ic.Events,
|
||||||
|
gc: g.(gcScheduler),
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type gcScheduler interface {
|
||||||
|
ScheduleAndWait(gocontext.Context) (gc.Stats, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type local struct {
|
||||||
|
store images.Store
|
||||||
|
gc gcScheduler
|
||||||
|
publisher events.Publisher
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ imagesapi.ImagesClient = &local{}
|
||||||
|
|
||||||
|
func (l *local) Get(ctx context.Context, req *imagesapi.GetImageRequest, _ ...grpc.CallOption) (*imagesapi.GetImageResponse, error) {
|
||||||
|
image, err := l.store.Get(ctx, req.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
imagepb := imageToProto(&image)
|
||||||
|
return &imagesapi.GetImageResponse{
|
||||||
|
Image: &imagepb,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) List(ctx context.Context, req *imagesapi.ListImagesRequest, _ ...grpc.CallOption) (*imagesapi.ListImagesResponse, error) {
|
||||||
|
images, err := l.store.List(ctx, req.Filters...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &imagesapi.ListImagesResponse{
|
||||||
|
Images: imagesToProto(images),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Create(ctx context.Context, req *imagesapi.CreateImageRequest, _ ...grpc.CallOption) (*imagesapi.CreateImageResponse, error) {
|
||||||
|
log.G(ctx).WithField("name", req.Image.Name).WithField("target", req.Image.Target.Digest).Debugf("create image")
|
||||||
|
if req.Image.Name == "" {
|
||||||
|
return nil, status.Errorf(codes.InvalidArgument, "Image.Name required")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
image = imageFromProto(&req.Image)
|
||||||
|
resp imagesapi.CreateImageResponse
|
||||||
|
)
|
||||||
|
created, err := l.store.Create(ctx, image)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Image = imageToProto(&created)
|
||||||
|
|
||||||
|
if err := l.publisher.Publish(ctx, "/images/create", &eventstypes.ImageCreate{
|
||||||
|
Name: resp.Image.Name,
|
||||||
|
Labels: resp.Image.Labels,
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &resp, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Update(ctx context.Context, req *imagesapi.UpdateImageRequest, _ ...grpc.CallOption) (*imagesapi.UpdateImageResponse, error) {
|
||||||
|
if req.Image.Name == "" {
|
||||||
|
return nil, status.Errorf(codes.InvalidArgument, "Image.Name required")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
image = imageFromProto(&req.Image)
|
||||||
|
resp imagesapi.UpdateImageResponse
|
||||||
|
fieldpaths []string
|
||||||
|
)
|
||||||
|
|
||||||
|
if req.UpdateMask != nil && len(req.UpdateMask.Paths) > 0 {
|
||||||
|
for _, path := range req.UpdateMask.Paths {
|
||||||
|
fieldpaths = append(fieldpaths, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updated, err := l.store.Update(ctx, image, fieldpaths...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Image = imageToProto(&updated)
|
||||||
|
|
||||||
|
if err := l.publisher.Publish(ctx, "/images/update", &eventstypes.ImageUpdate{
|
||||||
|
Name: resp.Image.Name,
|
||||||
|
Labels: resp.Image.Labels,
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Delete(ctx context.Context, req *imagesapi.DeleteImageRequest, _ ...grpc.CallOption) (*ptypes.Empty, error) {
|
||||||
|
log.G(ctx).WithField("name", req.Name).Debugf("delete image")
|
||||||
|
|
||||||
|
if err := l.store.Delete(ctx, req.Name); err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := l.publisher.Publish(ctx, "/images/delete", &eventstypes.ImageDelete{
|
||||||
|
Name: req.Name,
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Sync {
|
||||||
|
if _, err := l.gc.ScheduleAndWait(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ptypes.Empty{}, nil
|
||||||
|
}
|
@ -17,22 +17,13 @@
|
|||||||
package images
|
package images
|
||||||
|
|
||||||
import (
|
import (
|
||||||
gocontext "context"
|
|
||||||
|
|
||||||
eventstypes "github.com/containerd/containerd/api/events"
|
|
||||||
imagesapi "github.com/containerd/containerd/api/services/images/v1"
|
imagesapi "github.com/containerd/containerd/api/services/images/v1"
|
||||||
"github.com/containerd/containerd/errdefs"
|
|
||||||
"github.com/containerd/containerd/events"
|
|
||||||
"github.com/containerd/containerd/gc"
|
|
||||||
"github.com/containerd/containerd/images"
|
|
||||||
"github.com/containerd/containerd/log"
|
|
||||||
"github.com/containerd/containerd/metadata"
|
|
||||||
"github.com/containerd/containerd/plugin"
|
"github.com/containerd/containerd/plugin"
|
||||||
|
"github.com/containerd/containerd/services"
|
||||||
ptypes "github.com/gogo/protobuf/types"
|
ptypes "github.com/gogo/protobuf/types"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -40,42 +31,31 @@ func init() {
|
|||||||
Type: plugin.GRPCPlugin,
|
Type: plugin.GRPCPlugin,
|
||||||
ID: "images",
|
ID: "images",
|
||||||
Requires: []plugin.Type{
|
Requires: []plugin.Type{
|
||||||
plugin.MetadataPlugin,
|
plugin.ServicePlugin,
|
||||||
plugin.GCPlugin,
|
|
||||||
},
|
},
|
||||||
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
||||||
m, err := ic.Get(plugin.MetadataPlugin)
|
plugins, err := ic.GetByType(plugin.ServicePlugin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
g, err := ic.Get(plugin.GCPlugin)
|
p, ok := plugins[services.ImagesService]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("images service not found")
|
||||||
|
}
|
||||||
|
i, err := p.Instance()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return &service{local: i.(imagesapi.ImagesClient)}, nil
|
||||||
return NewService(metadata.NewImageStore(m.(*metadata.DB)), ic.Events, g.(gcScheduler)), nil
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type gcScheduler interface {
|
|
||||||
ScheduleAndWait(gocontext.Context) (gc.Stats, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type service struct {
|
type service struct {
|
||||||
store images.Store
|
local imagesapi.ImagesClient
|
||||||
gc gcScheduler
|
|
||||||
publisher events.Publisher
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewService returns the GRPC image server
|
var _ imagesapi.ImagesServer = &service{}
|
||||||
func NewService(is images.Store, publisher events.Publisher, gc gcScheduler) imagesapi.ImagesServer {
|
|
||||||
return &service{
|
|
||||||
store: is,
|
|
||||||
gc: gc,
|
|
||||||
publisher: publisher,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) Register(server *grpc.Server) error {
|
func (s *service) Register(server *grpc.Server) error {
|
||||||
imagesapi.RegisterImagesServer(server, s)
|
imagesapi.RegisterImagesServer(server, s)
|
||||||
@ -83,108 +63,21 @@ func (s *service) Register(server *grpc.Server) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Get(ctx context.Context, req *imagesapi.GetImageRequest) (*imagesapi.GetImageResponse, error) {
|
func (s *service) Get(ctx context.Context, req *imagesapi.GetImageRequest) (*imagesapi.GetImageResponse, error) {
|
||||||
image, err := s.store.Get(ctx, req.Name)
|
return s.local.Get(ctx, req)
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
imagepb := imageToProto(&image)
|
|
||||||
return &imagesapi.GetImageResponse{
|
|
||||||
Image: &imagepb,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) List(ctx context.Context, req *imagesapi.ListImagesRequest) (*imagesapi.ListImagesResponse, error) {
|
func (s *service) List(ctx context.Context, req *imagesapi.ListImagesRequest) (*imagesapi.ListImagesResponse, error) {
|
||||||
images, err := s.store.List(ctx, req.Filters...)
|
return s.local.List(ctx, req)
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &imagesapi.ListImagesResponse{
|
|
||||||
Images: imagesToProto(images),
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Create(ctx context.Context, req *imagesapi.CreateImageRequest) (*imagesapi.CreateImageResponse, error) {
|
func (s *service) Create(ctx context.Context, req *imagesapi.CreateImageRequest) (*imagesapi.CreateImageResponse, error) {
|
||||||
log.G(ctx).WithField("name", req.Image.Name).WithField("target", req.Image.Target.Digest).Debugf("create image")
|
return s.local.Create(ctx, req)
|
||||||
if req.Image.Name == "" {
|
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "Image.Name required")
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
image = imageFromProto(&req.Image)
|
|
||||||
resp imagesapi.CreateImageResponse
|
|
||||||
)
|
|
||||||
created, err := s.store.Create(ctx, image)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.Image = imageToProto(&created)
|
|
||||||
|
|
||||||
if err := s.publisher.Publish(ctx, "/images/create", &eventstypes.ImageCreate{
|
|
||||||
Name: resp.Image.Name,
|
|
||||||
Labels: resp.Image.Labels,
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &resp, nil
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Update(ctx context.Context, req *imagesapi.UpdateImageRequest) (*imagesapi.UpdateImageResponse, error) {
|
func (s *service) Update(ctx context.Context, req *imagesapi.UpdateImageRequest) (*imagesapi.UpdateImageResponse, error) {
|
||||||
if req.Image.Name == "" {
|
return s.local.Update(ctx, req)
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "Image.Name required")
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
image = imageFromProto(&req.Image)
|
|
||||||
resp imagesapi.UpdateImageResponse
|
|
||||||
fieldpaths []string
|
|
||||||
)
|
|
||||||
|
|
||||||
if req.UpdateMask != nil && len(req.UpdateMask.Paths) > 0 {
|
|
||||||
for _, path := range req.UpdateMask.Paths {
|
|
||||||
fieldpaths = append(fieldpaths, path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updated, err := s.store.Update(ctx, image, fieldpaths...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.Image = imageToProto(&updated)
|
|
||||||
|
|
||||||
if err := s.publisher.Publish(ctx, "/images/update", &eventstypes.ImageUpdate{
|
|
||||||
Name: resp.Image.Name,
|
|
||||||
Labels: resp.Image.Labels,
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &resp, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Delete(ctx context.Context, req *imagesapi.DeleteImageRequest) (*ptypes.Empty, error) {
|
func (s *service) Delete(ctx context.Context, req *imagesapi.DeleteImageRequest) (*ptypes.Empty, error) {
|
||||||
log.G(ctx).WithField("name", req.Name).Debugf("delete image")
|
return s.local.Delete(ctx, req)
|
||||||
|
|
||||||
if err := s.store.Delete(ctx, req.Name); err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.publisher.Publish(ctx, "/images/delete", &eventstypes.ImageDelete{
|
|
||||||
Name: req.Name,
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.Sync {
|
|
||||||
if _, err := s.gc.ScheduleAndWait(ctx); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &ptypes.Empty{}, nil
|
|
||||||
}
|
}
|
||||||
|
120
services/leases/local.go
Normal file
120
services/leases/local.go
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
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 leases
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
|
"github.com/boltdb/bolt"
|
||||||
|
api "github.com/containerd/containerd/api/services/leases/v1"
|
||||||
|
"github.com/containerd/containerd/metadata"
|
||||||
|
"github.com/containerd/containerd/plugin"
|
||||||
|
"github.com/containerd/containerd/services"
|
||||||
|
ptypes "github.com/gogo/protobuf/types"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
plugin.Register(&plugin.Registration{
|
||||||
|
Type: plugin.ServicePlugin,
|
||||||
|
ID: services.LeasesService,
|
||||||
|
Requires: []plugin.Type{
|
||||||
|
plugin.MetadataPlugin,
|
||||||
|
},
|
||||||
|
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
||||||
|
m, err := ic.Get(plugin.MetadataPlugin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &local{db: m.(*metadata.DB)}, nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type local struct {
|
||||||
|
db *metadata.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Create(ctx context.Context, r *api.CreateRequest, _ ...grpc.CallOption) (*api.CreateResponse, error) {
|
||||||
|
lid := r.ID
|
||||||
|
if lid == "" {
|
||||||
|
lid = generateLeaseID()
|
||||||
|
}
|
||||||
|
var trans metadata.Lease
|
||||||
|
if err := l.db.Update(func(tx *bolt.Tx) error {
|
||||||
|
var err error
|
||||||
|
trans, err = metadata.NewLeaseManager(tx).Create(ctx, lid, r.Labels)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &api.CreateResponse{
|
||||||
|
Lease: txToGRPC(trans),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Delete(ctx context.Context, r *api.DeleteRequest, _ ...grpc.CallOption) (*ptypes.Empty, error) {
|
||||||
|
if err := l.db.Update(func(tx *bolt.Tx) error {
|
||||||
|
return metadata.NewLeaseManager(tx).Delete(ctx, r.ID)
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &ptypes.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) List(ctx context.Context, r *api.ListRequest, _ ...grpc.CallOption) (*api.ListResponse, error) {
|
||||||
|
var leases []metadata.Lease
|
||||||
|
if err := l.db.View(func(tx *bolt.Tx) error {
|
||||||
|
var err error
|
||||||
|
leases, err = metadata.NewLeaseManager(tx).List(ctx, false, r.Filters...)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
apileases := make([]*api.Lease, len(leases))
|
||||||
|
for i := range leases {
|
||||||
|
apileases[i] = txToGRPC(leases[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
return &api.ListResponse{
|
||||||
|
Leases: apileases,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func txToGRPC(tx metadata.Lease) *api.Lease {
|
||||||
|
return &api.Lease{
|
||||||
|
ID: tx.ID,
|
||||||
|
Labels: tx.Labels,
|
||||||
|
CreatedAt: tx.CreatedAt,
|
||||||
|
// TODO: Snapshots
|
||||||
|
// TODO: Content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateLeaseID() string {
|
||||||
|
t := time.Now()
|
||||||
|
var b [3]byte
|
||||||
|
// Ignore read failures, just decreases uniqueness
|
||||||
|
rand.Read(b[:])
|
||||||
|
return fmt.Sprintf("%d-%s", t.Nanosecond(), base64.URLEncoding.EncodeToString(b[:]))
|
||||||
|
}
|
@ -17,18 +17,13 @@
|
|||||||
package leases
|
package leases
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
"github.com/boltdb/bolt"
|
|
||||||
api "github.com/containerd/containerd/api/services/leases/v1"
|
api "github.com/containerd/containerd/api/services/leases/v1"
|
||||||
"github.com/containerd/containerd/metadata"
|
|
||||||
"github.com/containerd/containerd/plugin"
|
"github.com/containerd/containerd/plugin"
|
||||||
|
"github.com/containerd/containerd/services"
|
||||||
ptypes "github.com/gogo/protobuf/types"
|
ptypes "github.com/gogo/protobuf/types"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -37,27 +32,28 @@ func init() {
|
|||||||
Type: plugin.GRPCPlugin,
|
Type: plugin.GRPCPlugin,
|
||||||
ID: "leases",
|
ID: "leases",
|
||||||
Requires: []plugin.Type{
|
Requires: []plugin.Type{
|
||||||
plugin.MetadataPlugin,
|
plugin.ServicePlugin,
|
||||||
},
|
},
|
||||||
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
||||||
m, err := ic.Get(plugin.MetadataPlugin)
|
plugins, err := ic.GetByType(plugin.ServicePlugin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewService(m.(*metadata.DB)), nil
|
p, ok := plugins[services.LeasesService]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("leases service not found")
|
||||||
|
}
|
||||||
|
i, err := p.Instance()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &service{local: i.(api.LeasesClient)}, nil
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type service struct {
|
type service struct {
|
||||||
db *metadata.DB
|
local api.LeasesClient
|
||||||
}
|
|
||||||
|
|
||||||
// NewService returns the GRPC metadata server
|
|
||||||
func NewService(db *metadata.DB) api.LeasesServer {
|
|
||||||
return &service{
|
|
||||||
db: db,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Register(server *grpc.Server) error {
|
func (s *service) Register(server *grpc.Server) error {
|
||||||
@ -66,66 +62,13 @@ func (s *service) Register(server *grpc.Server) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Create(ctx context.Context, r *api.CreateRequest) (*api.CreateResponse, error) {
|
func (s *service) Create(ctx context.Context, r *api.CreateRequest) (*api.CreateResponse, error) {
|
||||||
lid := r.ID
|
return s.local.Create(ctx, r)
|
||||||
if lid == "" {
|
|
||||||
lid = generateLeaseID()
|
|
||||||
}
|
|
||||||
var trans metadata.Lease
|
|
||||||
if err := s.db.Update(func(tx *bolt.Tx) error {
|
|
||||||
var err error
|
|
||||||
trans, err = metadata.NewLeaseManager(tx).Create(ctx, lid, r.Labels)
|
|
||||||
return err
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &api.CreateResponse{
|
|
||||||
Lease: txToGRPC(trans),
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Delete(ctx context.Context, r *api.DeleteRequest) (*ptypes.Empty, error) {
|
func (s *service) Delete(ctx context.Context, r *api.DeleteRequest) (*ptypes.Empty, error) {
|
||||||
if err := s.db.Update(func(tx *bolt.Tx) error {
|
return s.local.Delete(ctx, r)
|
||||||
return metadata.NewLeaseManager(tx).Delete(ctx, r.ID)
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &ptypes.Empty{}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) List(ctx context.Context, r *api.ListRequest) (*api.ListResponse, error) {
|
func (s *service) List(ctx context.Context, r *api.ListRequest) (*api.ListResponse, error) {
|
||||||
var leases []metadata.Lease
|
return s.local.List(ctx, r)
|
||||||
if err := s.db.View(func(tx *bolt.Tx) error {
|
|
||||||
var err error
|
|
||||||
leases, err = metadata.NewLeaseManager(tx).List(ctx, false, r.Filters...)
|
|
||||||
return err
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
apileases := make([]*api.Lease, len(leases))
|
|
||||||
for i := range leases {
|
|
||||||
apileases[i] = txToGRPC(leases[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
return &api.ListResponse{
|
|
||||||
Leases: apileases,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func txToGRPC(tx metadata.Lease) *api.Lease {
|
|
||||||
return &api.Lease{
|
|
||||||
ID: tx.ID,
|
|
||||||
Labels: tx.Labels,
|
|
||||||
CreatedAt: tx.CreatedAt,
|
|
||||||
// TODO: Snapshots
|
|
||||||
// TODO: Content
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateLeaseID() string {
|
|
||||||
t := time.Now()
|
|
||||||
var b [3]byte
|
|
||||||
// Ignore read failures, just decreases uniqueness
|
|
||||||
rand.Read(b[:])
|
|
||||||
return fmt.Sprintf("%d-%s", t.Nanosecond(), base64.URLEncoding.EncodeToString(b[:]))
|
|
||||||
}
|
}
|
||||||
|
223
services/namespaces/local.go
Normal file
223
services/namespaces/local.go
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
/*
|
||||||
|
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 namespaces
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/boltdb/bolt"
|
||||||
|
eventstypes "github.com/containerd/containerd/api/events"
|
||||||
|
api "github.com/containerd/containerd/api/services/namespaces/v1"
|
||||||
|
"github.com/containerd/containerd/errdefs"
|
||||||
|
"github.com/containerd/containerd/events"
|
||||||
|
"github.com/containerd/containerd/metadata"
|
||||||
|
"github.com/containerd/containerd/namespaces"
|
||||||
|
"github.com/containerd/containerd/plugin"
|
||||||
|
"github.com/containerd/containerd/services"
|
||||||
|
ptypes "github.com/gogo/protobuf/types"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
plugin.Register(&plugin.Registration{
|
||||||
|
Type: plugin.ServicePlugin,
|
||||||
|
ID: services.NamespacesService,
|
||||||
|
Requires: []plugin.Type{
|
||||||
|
plugin.MetadataPlugin,
|
||||||
|
},
|
||||||
|
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
||||||
|
m, err := ic.Get(plugin.MetadataPlugin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &local{
|
||||||
|
db: m.(*metadata.DB),
|
||||||
|
publisher: ic.Events,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provide local namespaces service instead of local namespace store,
|
||||||
|
// because namespace store interface doesn't provide enough functionality
|
||||||
|
// for namespaces service.
|
||||||
|
type local struct {
|
||||||
|
db *metadata.DB
|
||||||
|
publisher events.Publisher
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ api.NamespacesClient = &local{}
|
||||||
|
|
||||||
|
func (l *local) Get(ctx context.Context, req *api.GetNamespaceRequest, _ ...grpc.CallOption) (*api.GetNamespaceResponse, error) {
|
||||||
|
var resp api.GetNamespaceResponse
|
||||||
|
|
||||||
|
return &resp, l.withStoreView(ctx, func(ctx context.Context, store namespaces.Store) error {
|
||||||
|
labels, err := store.Labels(ctx, req.Name)
|
||||||
|
if err != nil {
|
||||||
|
return errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Namespace = api.Namespace{
|
||||||
|
Name: req.Name,
|
||||||
|
Labels: labels,
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) List(ctx context.Context, req *api.ListNamespacesRequest, _ ...grpc.CallOption) (*api.ListNamespacesResponse, error) {
|
||||||
|
var resp api.ListNamespacesResponse
|
||||||
|
|
||||||
|
return &resp, l.withStoreView(ctx, func(ctx context.Context, store namespaces.Store) error {
|
||||||
|
namespaces, err := store.List(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, namespace := range namespaces {
|
||||||
|
labels, err := store.Labels(ctx, namespace)
|
||||||
|
if err != nil {
|
||||||
|
// In general, this should be unlikely, since we are holding a
|
||||||
|
// transaction to service this request.
|
||||||
|
return errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Namespaces = append(resp.Namespaces, api.Namespace{
|
||||||
|
Name: namespace,
|
||||||
|
Labels: labels,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Create(ctx context.Context, req *api.CreateNamespaceRequest, _ ...grpc.CallOption) (*api.CreateNamespaceResponse, error) {
|
||||||
|
var resp api.CreateNamespaceResponse
|
||||||
|
|
||||||
|
if err := l.withStoreUpdate(ctx, func(ctx context.Context, store namespaces.Store) error {
|
||||||
|
if err := store.Create(ctx, req.Namespace.Name, req.Namespace.Labels); err != nil {
|
||||||
|
return errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range req.Namespace.Labels {
|
||||||
|
if err := store.SetLabel(ctx, req.Namespace.Name, k, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Namespace = req.Namespace
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return &resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := l.publisher.Publish(ctx, "/namespaces/create", &eventstypes.NamespaceCreate{
|
||||||
|
Name: req.Namespace.Name,
|
||||||
|
Labels: req.Namespace.Labels,
|
||||||
|
}); err != nil {
|
||||||
|
return &resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &resp, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Update(ctx context.Context, req *api.UpdateNamespaceRequest, _ ...grpc.CallOption) (*api.UpdateNamespaceResponse, error) {
|
||||||
|
var resp api.UpdateNamespaceResponse
|
||||||
|
if err := l.withStoreUpdate(ctx, func(ctx context.Context, store namespaces.Store) error {
|
||||||
|
if req.UpdateMask != nil && len(req.UpdateMask.Paths) > 0 {
|
||||||
|
for _, path := range req.UpdateMask.Paths {
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(path, "labels."):
|
||||||
|
key := strings.TrimPrefix(path, "labels.")
|
||||||
|
if err := store.SetLabel(ctx, req.Namespace.Name, key, req.Namespace.Labels[key]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return status.Errorf(codes.InvalidArgument, "cannot update %q field", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// clear out the existing labels and then set them to the incoming request.
|
||||||
|
// get current set of labels
|
||||||
|
labels, err := store.Labels(ctx, req.Namespace.Name)
|
||||||
|
if err != nil {
|
||||||
|
return errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for k := range labels {
|
||||||
|
if err := store.SetLabel(ctx, req.Namespace.Name, k, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range req.Namespace.Labels {
|
||||||
|
if err := store.SetLabel(ctx, req.Namespace.Name, k, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return &resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := l.publisher.Publish(ctx, "/namespaces/update", &eventstypes.NamespaceUpdate{
|
||||||
|
Name: req.Namespace.Name,
|
||||||
|
Labels: req.Namespace.Labels,
|
||||||
|
}); err != nil {
|
||||||
|
return &resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Delete(ctx context.Context, req *api.DeleteNamespaceRequest, _ ...grpc.CallOption) (*ptypes.Empty, error) {
|
||||||
|
if err := l.withStoreUpdate(ctx, func(ctx context.Context, store namespaces.Store) error {
|
||||||
|
return errdefs.ToGRPC(store.Delete(ctx, req.Name))
|
||||||
|
}); err != nil {
|
||||||
|
return &ptypes.Empty{}, err
|
||||||
|
}
|
||||||
|
// set the namespace in the context before publishing the event
|
||||||
|
ctx = namespaces.WithNamespace(ctx, req.Name)
|
||||||
|
if err := l.publisher.Publish(ctx, "/namespaces/delete", &eventstypes.NamespaceDelete{
|
||||||
|
Name: req.Name,
|
||||||
|
}); err != nil {
|
||||||
|
return &ptypes.Empty{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ptypes.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) withStore(ctx context.Context, fn func(ctx context.Context, store namespaces.Store) error) func(tx *bolt.Tx) error {
|
||||||
|
return func(tx *bolt.Tx) error { return fn(ctx, metadata.NewNamespaceStore(tx)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) withStoreView(ctx context.Context, fn func(ctx context.Context, store namespaces.Store) error) error {
|
||||||
|
return l.db.View(l.withStore(ctx, fn))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) withStoreUpdate(ctx context.Context, fn func(ctx context.Context, store namespaces.Store) error) error {
|
||||||
|
return l.db.Update(l.withStore(ctx, fn))
|
||||||
|
}
|
@ -17,21 +17,13 @@
|
|||||||
package namespaces
|
package namespaces
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/boltdb/bolt"
|
|
||||||
eventstypes "github.com/containerd/containerd/api/events"
|
|
||||||
api "github.com/containerd/containerd/api/services/namespaces/v1"
|
api "github.com/containerd/containerd/api/services/namespaces/v1"
|
||||||
"github.com/containerd/containerd/errdefs"
|
|
||||||
"github.com/containerd/containerd/events"
|
|
||||||
"github.com/containerd/containerd/metadata"
|
|
||||||
"github.com/containerd/containerd/namespaces"
|
|
||||||
"github.com/containerd/containerd/plugin"
|
"github.com/containerd/containerd/plugin"
|
||||||
|
"github.com/containerd/containerd/services"
|
||||||
ptypes "github.com/gogo/protobuf/types"
|
ptypes "github.com/gogo/protobuf/types"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -39,191 +31,53 @@ func init() {
|
|||||||
Type: plugin.GRPCPlugin,
|
Type: plugin.GRPCPlugin,
|
||||||
ID: "namespaces",
|
ID: "namespaces",
|
||||||
Requires: []plugin.Type{
|
Requires: []plugin.Type{
|
||||||
plugin.MetadataPlugin,
|
plugin.ServicePlugin,
|
||||||
},
|
},
|
||||||
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
||||||
m, err := ic.Get(plugin.MetadataPlugin)
|
plugins, err := ic.GetByType(plugin.ServicePlugin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewService(m.(*metadata.DB), ic.Events), nil
|
p, ok := plugins[services.NamespacesService]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("namespaces service not found")
|
||||||
|
}
|
||||||
|
i, err := p.Instance()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &service{local: i.(api.NamespacesClient)}, nil
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type service struct {
|
type service struct {
|
||||||
db *metadata.DB
|
local api.NamespacesClient
|
||||||
publisher events.Publisher
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ api.NamespacesServer = &service{}
|
var _ api.NamespacesServer = &service{}
|
||||||
|
|
||||||
// NewService returns the GRPC namespaces server
|
|
||||||
func NewService(db *metadata.DB, publisher events.Publisher) api.NamespacesServer {
|
|
||||||
return &service{
|
|
||||||
db: db,
|
|
||||||
publisher: publisher,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) Register(server *grpc.Server) error {
|
func (s *service) Register(server *grpc.Server) error {
|
||||||
api.RegisterNamespacesServer(server, s)
|
api.RegisterNamespacesServer(server, s)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Get(ctx context.Context, req *api.GetNamespaceRequest) (*api.GetNamespaceResponse, error) {
|
func (s *service) Get(ctx context.Context, req *api.GetNamespaceRequest) (*api.GetNamespaceResponse, error) {
|
||||||
var resp api.GetNamespaceResponse
|
return s.local.Get(ctx, req)
|
||||||
|
|
||||||
return &resp, s.withStoreView(ctx, func(ctx context.Context, store namespaces.Store) error {
|
|
||||||
labels, err := store.Labels(ctx, req.Name)
|
|
||||||
if err != nil {
|
|
||||||
return errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.Namespace = api.Namespace{
|
|
||||||
Name: req.Name,
|
|
||||||
Labels: labels,
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) List(ctx context.Context, req *api.ListNamespacesRequest) (*api.ListNamespacesResponse, error) {
|
func (s *service) List(ctx context.Context, req *api.ListNamespacesRequest) (*api.ListNamespacesResponse, error) {
|
||||||
var resp api.ListNamespacesResponse
|
return s.local.List(ctx, req)
|
||||||
|
|
||||||
return &resp, s.withStoreView(ctx, func(ctx context.Context, store namespaces.Store) error {
|
|
||||||
namespaces, err := store.List(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, namespace := range namespaces {
|
|
||||||
labels, err := store.Labels(ctx, namespace)
|
|
||||||
if err != nil {
|
|
||||||
// In general, this should be unlikely, since we are holding a
|
|
||||||
// transaction to service this request.
|
|
||||||
return errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.Namespaces = append(resp.Namespaces, api.Namespace{
|
|
||||||
Name: namespace,
|
|
||||||
Labels: labels,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Create(ctx context.Context, req *api.CreateNamespaceRequest) (*api.CreateNamespaceResponse, error) {
|
func (s *service) Create(ctx context.Context, req *api.CreateNamespaceRequest) (*api.CreateNamespaceResponse, error) {
|
||||||
var resp api.CreateNamespaceResponse
|
return s.local.Create(ctx, req)
|
||||||
|
|
||||||
if err := s.withStoreUpdate(ctx, func(ctx context.Context, store namespaces.Store) error {
|
|
||||||
if err := store.Create(ctx, req.Namespace.Name, req.Namespace.Labels); err != nil {
|
|
||||||
return errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range req.Namespace.Labels {
|
|
||||||
if err := store.SetLabel(ctx, req.Namespace.Name, k, v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.Namespace = req.Namespace
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
return &resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.publisher.Publish(ctx, "/namespaces/create", &eventstypes.NamespaceCreate{
|
|
||||||
Name: req.Namespace.Name,
|
|
||||||
Labels: req.Namespace.Labels,
|
|
||||||
}); err != nil {
|
|
||||||
return &resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &resp, nil
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Update(ctx context.Context, req *api.UpdateNamespaceRequest) (*api.UpdateNamespaceResponse, error) {
|
func (s *service) Update(ctx context.Context, req *api.UpdateNamespaceRequest) (*api.UpdateNamespaceResponse, error) {
|
||||||
var resp api.UpdateNamespaceResponse
|
return s.local.Update(ctx, req)
|
||||||
if err := s.withStoreUpdate(ctx, func(ctx context.Context, store namespaces.Store) error {
|
|
||||||
if req.UpdateMask != nil && len(req.UpdateMask.Paths) > 0 {
|
|
||||||
for _, path := range req.UpdateMask.Paths {
|
|
||||||
switch {
|
|
||||||
case strings.HasPrefix(path, "labels."):
|
|
||||||
key := strings.TrimPrefix(path, "labels.")
|
|
||||||
if err := store.SetLabel(ctx, req.Namespace.Name, key, req.Namespace.Labels[key]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return status.Errorf(codes.InvalidArgument, "cannot update %q field", path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// clear out the existing labels and then set them to the incoming request.
|
|
||||||
// get current set of labels
|
|
||||||
labels, err := store.Labels(ctx, req.Namespace.Name)
|
|
||||||
if err != nil {
|
|
||||||
return errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for k := range labels {
|
|
||||||
if err := store.SetLabel(ctx, req.Namespace.Name, k, ""); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range req.Namespace.Labels {
|
|
||||||
if err := store.SetLabel(ctx, req.Namespace.Name, k, v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
return &resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.publisher.Publish(ctx, "/namespaces/update", &eventstypes.NamespaceUpdate{
|
|
||||||
Name: req.Namespace.Name,
|
|
||||||
Labels: req.Namespace.Labels,
|
|
||||||
}); err != nil {
|
|
||||||
return &resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &resp, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Delete(ctx context.Context, req *api.DeleteNamespaceRequest) (*ptypes.Empty, error) {
|
func (s *service) Delete(ctx context.Context, req *api.DeleteNamespaceRequest) (*ptypes.Empty, error) {
|
||||||
if err := s.withStoreUpdate(ctx, func(ctx context.Context, store namespaces.Store) error {
|
return s.local.Delete(ctx, req)
|
||||||
return errdefs.ToGRPC(store.Delete(ctx, req.Name))
|
|
||||||
}); err != nil {
|
|
||||||
return &ptypes.Empty{}, err
|
|
||||||
}
|
|
||||||
// set the namespace in the context before publishing the event
|
|
||||||
ctx = namespaces.WithNamespace(ctx, req.Name)
|
|
||||||
if err := s.publisher.Publish(ctx, "/namespaces/delete", &eventstypes.NamespaceDelete{
|
|
||||||
Name: req.Name,
|
|
||||||
}); err != nil {
|
|
||||||
return &ptypes.Empty{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &ptypes.Empty{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) withStore(ctx context.Context, fn func(ctx context.Context, store namespaces.Store) error) func(tx *bolt.Tx) error {
|
|
||||||
return func(tx *bolt.Tx) error { return fn(ctx, metadata.NewNamespaceStore(tx)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) withStoreView(ctx context.Context, fn func(ctx context.Context, store namespaces.Store) error) error {
|
|
||||||
return s.db.View(s.withStore(ctx, fn))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) withStoreUpdate(ctx context.Context, fn func(ctx context.Context, store namespaces.Store) error) error {
|
|
||||||
return s.db.Update(s.withStore(ctx, fn))
|
|
||||||
}
|
}
|
||||||
|
36
services/services.go
Normal file
36
services/services.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
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 services
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ContentService is id of content service.
|
||||||
|
ContentService = "content-service"
|
||||||
|
// SnapshotsService is id of snapshots service.
|
||||||
|
SnapshotsService = "snapshots-service"
|
||||||
|
// ImagesService is id of images service.
|
||||||
|
ImagesService = "images-service"
|
||||||
|
// ContainersService is id of containers service.
|
||||||
|
ContainersService = "containers-service"
|
||||||
|
// TasksService is id of tasks service.
|
||||||
|
TasksService = "tasks-service"
|
||||||
|
// NamespacesService is id of namespaces service.
|
||||||
|
NamespacesService = "namespaces-service"
|
||||||
|
// LeasesService is id of leases service.
|
||||||
|
LeasesService = "leases-service"
|
||||||
|
// DiffService is id of diff service.
|
||||||
|
DiffService = "diff-service"
|
||||||
|
)
|
@ -19,17 +19,16 @@ package snapshots
|
|||||||
import (
|
import (
|
||||||
gocontext "context"
|
gocontext "context"
|
||||||
|
|
||||||
eventstypes "github.com/containerd/containerd/api/events"
|
|
||||||
snapshotsapi "github.com/containerd/containerd/api/services/snapshots/v1"
|
snapshotsapi "github.com/containerd/containerd/api/services/snapshots/v1"
|
||||||
"github.com/containerd/containerd/api/types"
|
"github.com/containerd/containerd/api/types"
|
||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
"github.com/containerd/containerd/events"
|
|
||||||
"github.com/containerd/containerd/log"
|
"github.com/containerd/containerd/log"
|
||||||
"github.com/containerd/containerd/metadata"
|
|
||||||
"github.com/containerd/containerd/mount"
|
"github.com/containerd/containerd/mount"
|
||||||
"github.com/containerd/containerd/plugin"
|
"github.com/containerd/containerd/plugin"
|
||||||
|
"github.com/containerd/containerd/services"
|
||||||
"github.com/containerd/containerd/snapshots"
|
"github.com/containerd/containerd/snapshots"
|
||||||
ptypes "github.com/gogo/protobuf/types"
|
ptypes "github.com/gogo/protobuf/types"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
@ -39,7 +38,7 @@ func init() {
|
|||||||
Type: plugin.GRPCPlugin,
|
Type: plugin.GRPCPlugin,
|
||||||
ID: "snapshots",
|
ID: "snapshots",
|
||||||
Requires: []plugin.Type{
|
Requires: []plugin.Type{
|
||||||
plugin.MetadataPlugin,
|
plugin.ServicePlugin,
|
||||||
},
|
},
|
||||||
InitFn: newService,
|
InitFn: newService,
|
||||||
})
|
})
|
||||||
@ -48,20 +47,24 @@ func init() {
|
|||||||
var empty = &ptypes.Empty{}
|
var empty = &ptypes.Empty{}
|
||||||
|
|
||||||
type service struct {
|
type service struct {
|
||||||
db *metadata.DB
|
ss map[string]snapshots.Snapshotter
|
||||||
publisher events.Publisher
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newService(ic *plugin.InitContext) (interface{}, error) {
|
func newService(ic *plugin.InitContext) (interface{}, error) {
|
||||||
md, err := ic.Get(plugin.MetadataPlugin)
|
plugins, err := ic.GetByType(plugin.ServicePlugin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
p, ok := plugins[services.SnapshotsService]
|
||||||
return &service{
|
if !ok {
|
||||||
db: md.(*metadata.DB),
|
return nil, errors.New("snapshots service not found")
|
||||||
publisher: ic.Events,
|
}
|
||||||
}, nil
|
i, err := p.Instance()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ss := i.(map[string]snapshots.Snapshotter)
|
||||||
|
return &service{ss: ss}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) getSnapshotter(name string) (snapshots.Snapshotter, error) {
|
func (s *service) getSnapshotter(name string) (snapshots.Snapshotter, error) {
|
||||||
@ -69,7 +72,7 @@ func (s *service) getSnapshotter(name string) (snapshots.Snapshotter, error) {
|
|||||||
return nil, errdefs.ToGRPCf(errdefs.ErrInvalidArgument, "snapshotter argument missing")
|
return nil, errdefs.ToGRPCf(errdefs.ErrInvalidArgument, "snapshotter argument missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
sn := s.db.Snapshotter(name)
|
sn := s.ss[name]
|
||||||
if sn == nil {
|
if sn == nil {
|
||||||
return nil, errdefs.ToGRPCf(errdefs.ErrInvalidArgument, "snapshotter not loaded: %s", name)
|
return nil, errdefs.ToGRPCf(errdefs.ErrInvalidArgument, "snapshotter not loaded: %s", name)
|
||||||
}
|
}
|
||||||
@ -97,12 +100,6 @@ func (s *service) Prepare(ctx context.Context, pr *snapshotsapi.PrepareSnapshotR
|
|||||||
return nil, errdefs.ToGRPC(err)
|
return nil, errdefs.ToGRPC(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.publisher.Publish(ctx, "/snapshot/prepare", &eventstypes.SnapshotPrepare{
|
|
||||||
Key: pr.Key,
|
|
||||||
Parent: pr.Parent,
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &snapshotsapi.PrepareSnapshotResponse{
|
return &snapshotsapi.PrepareSnapshotResponse{
|
||||||
Mounts: fromMounts(mounts),
|
Mounts: fromMounts(mounts),
|
||||||
}, nil
|
}, nil
|
||||||
@ -158,12 +155,6 @@ func (s *service) Commit(ctx context.Context, cr *snapshotsapi.CommitSnapshotReq
|
|||||||
return nil, errdefs.ToGRPC(err)
|
return nil, errdefs.ToGRPC(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.publisher.Publish(ctx, "/snapshot/commit", &eventstypes.SnapshotCommit{
|
|
||||||
Key: cr.Key,
|
|
||||||
Name: cr.Name,
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return empty, nil
|
return empty, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,11 +169,6 @@ func (s *service) Remove(ctx context.Context, rr *snapshotsapi.RemoveSnapshotReq
|
|||||||
return nil, errdefs.ToGRPC(err)
|
return nil, errdefs.ToGRPC(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.publisher.Publish(ctx, "/snapshot/remove", &eventstypes.SnapshotRemove{
|
|
||||||
Key: rr.Key,
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return empty, nil
|
return empty, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
98
services/snapshots/snapshotters.go
Normal file
98
services/snapshots/snapshotters.go
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
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 snapshots
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
eventstypes "github.com/containerd/containerd/api/events"
|
||||||
|
"github.com/containerd/containerd/events"
|
||||||
|
"github.com/containerd/containerd/metadata"
|
||||||
|
"github.com/containerd/containerd/mount"
|
||||||
|
"github.com/containerd/containerd/plugin"
|
||||||
|
"github.com/containerd/containerd/services"
|
||||||
|
"github.com/containerd/containerd/snapshots"
|
||||||
|
)
|
||||||
|
|
||||||
|
// snapshotter wraps snapshots.Snapshotter with proper events published.
|
||||||
|
type snapshotter struct {
|
||||||
|
snapshots.Snapshotter
|
||||||
|
publisher events.Publisher
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
plugin.Register(&plugin.Registration{
|
||||||
|
Type: plugin.ServicePlugin,
|
||||||
|
ID: services.SnapshotsService,
|
||||||
|
Requires: []plugin.Type{
|
||||||
|
plugin.MetadataPlugin,
|
||||||
|
},
|
||||||
|
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
||||||
|
m, err := ic.Get(plugin.MetadataPlugin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
db := m.(*metadata.DB)
|
||||||
|
ss := make(map[string]snapshots.Snapshotter)
|
||||||
|
for n, sn := range db.Snapshotters() {
|
||||||
|
ss[n] = newSnapshotter(sn, ic.Events)
|
||||||
|
}
|
||||||
|
return ss, nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSnapshotter(sn snapshots.Snapshotter, publisher events.Publisher) snapshots.Snapshotter {
|
||||||
|
return &snapshotter{
|
||||||
|
Snapshotter: sn,
|
||||||
|
publisher: publisher,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *snapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
|
||||||
|
mounts, err := s.Snapshotter.Prepare(ctx, key, parent, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := s.publisher.Publish(ctx, "/snapshot/prepare", &eventstypes.SnapshotPrepare{
|
||||||
|
Key: key,
|
||||||
|
Parent: parent,
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return mounts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error {
|
||||||
|
if err := s.Snapshotter.Commit(ctx, name, key, opts...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.publisher.Publish(ctx, "/snapshot/commit", &eventstypes.SnapshotCommit{
|
||||||
|
Key: key,
|
||||||
|
Name: name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *snapshotter) Remove(ctx context.Context, key string) error {
|
||||||
|
if err := s.Snapshotter.Remove(ctx, key); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.publisher.Publish(ctx, "/snapshot/remove", &eventstypes.SnapshotRemove{
|
||||||
|
Key: key,
|
||||||
|
})
|
||||||
|
}
|
634
services/tasks/local.go
Normal file
634
services/tasks/local.go
Normal file
@ -0,0 +1,634 @@
|
|||||||
|
/*
|
||||||
|
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 tasks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/boltdb/bolt"
|
||||||
|
api "github.com/containerd/containerd/api/services/tasks/v1"
|
||||||
|
"github.com/containerd/containerd/api/types"
|
||||||
|
"github.com/containerd/containerd/api/types/task"
|
||||||
|
"github.com/containerd/containerd/archive"
|
||||||
|
"github.com/containerd/containerd/containers"
|
||||||
|
"github.com/containerd/containerd/content"
|
||||||
|
"github.com/containerd/containerd/errdefs"
|
||||||
|
"github.com/containerd/containerd/events"
|
||||||
|
"github.com/containerd/containerd/filters"
|
||||||
|
"github.com/containerd/containerd/images"
|
||||||
|
"github.com/containerd/containerd/log"
|
||||||
|
"github.com/containerd/containerd/metadata"
|
||||||
|
"github.com/containerd/containerd/mount"
|
||||||
|
"github.com/containerd/containerd/plugin"
|
||||||
|
"github.com/containerd/containerd/runtime"
|
||||||
|
"github.com/containerd/containerd/services"
|
||||||
|
"github.com/containerd/typeurl"
|
||||||
|
ptypes "github.com/gogo/protobuf/types"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ = (api.TasksClient)(&local{})
|
||||||
|
empty = &ptypes.Empty{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
plugin.Register(&plugin.Registration{
|
||||||
|
Type: plugin.ServicePlugin,
|
||||||
|
ID: services.TasksService,
|
||||||
|
Requires: []plugin.Type{
|
||||||
|
plugin.RuntimePlugin,
|
||||||
|
plugin.MetadataPlugin,
|
||||||
|
},
|
||||||
|
InitFn: initFunc,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func initFunc(ic *plugin.InitContext) (interface{}, error) {
|
||||||
|
rt, err := ic.GetByType(plugin.RuntimePlugin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := ic.Get(plugin.MetadataPlugin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cs := m.(*metadata.DB).ContentStore()
|
||||||
|
runtimes := make(map[string]runtime.Runtime)
|
||||||
|
for _, rr := range rt {
|
||||||
|
ri, err := rr.Instance()
|
||||||
|
if err != nil {
|
||||||
|
log.G(ic.Context).WithError(err).Warn("could not load runtime instance due to initialization error")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r := ri.(runtime.Runtime)
|
||||||
|
runtimes[r.ID()] = r
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(runtimes) == 0 {
|
||||||
|
return nil, errors.New("no runtimes available to create task service")
|
||||||
|
}
|
||||||
|
return &local{
|
||||||
|
runtimes: runtimes,
|
||||||
|
db: m.(*metadata.DB),
|
||||||
|
store: cs,
|
||||||
|
publisher: ic.Events,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type local struct {
|
||||||
|
runtimes map[string]runtime.Runtime
|
||||||
|
db *metadata.DB
|
||||||
|
store content.Store
|
||||||
|
publisher events.Publisher
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Create(ctx context.Context, r *api.CreateTaskRequest, _ ...grpc.CallOption) (*api.CreateTaskResponse, error) {
|
||||||
|
var (
|
||||||
|
checkpointPath string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if r.Checkpoint != nil {
|
||||||
|
checkpointPath, err = ioutil.TempDir("", "ctrd-checkpoint")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if r.Checkpoint.MediaType != images.MediaTypeContainerd1Checkpoint {
|
||||||
|
return nil, fmt.Errorf("unsupported checkpoint type %q", r.Checkpoint.MediaType)
|
||||||
|
}
|
||||||
|
reader, err := l.store.ReaderAt(ctx, r.Checkpoint.Digest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = archive.Apply(ctx, checkpointPath, content.NewReader(reader))
|
||||||
|
reader.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
container, err := l.getContainer(ctx, r.ContainerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
opts := runtime.CreateOpts{
|
||||||
|
Spec: container.Spec,
|
||||||
|
IO: runtime.IO{
|
||||||
|
Stdin: r.Stdin,
|
||||||
|
Stdout: r.Stdout,
|
||||||
|
Stderr: r.Stderr,
|
||||||
|
Terminal: r.Terminal,
|
||||||
|
},
|
||||||
|
Checkpoint: checkpointPath,
|
||||||
|
Options: r.Options,
|
||||||
|
}
|
||||||
|
for _, m := range r.Rootfs {
|
||||||
|
opts.Rootfs = append(opts.Rootfs, mount.Mount{
|
||||||
|
Type: m.Type,
|
||||||
|
Source: m.Source,
|
||||||
|
Options: m.Options,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
runtime, err := l.getRuntime(container.Runtime.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c, err := runtime.Create(ctx, r.ContainerID, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
state, err := c.State(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.G(ctx).Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &api.CreateTaskResponse{
|
||||||
|
ContainerID: r.ContainerID,
|
||||||
|
Pid: state.Pid,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Start(ctx context.Context, r *api.StartRequest, _ ...grpc.CallOption) (*api.StartResponse, error) {
|
||||||
|
t, err := l.getTask(ctx, r.ContainerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p := runtime.Process(t)
|
||||||
|
if r.ExecID != "" {
|
||||||
|
if p, err = t.Process(ctx, r.ExecID); err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := p.Start(ctx); err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
state, err := p.State(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
return &api.StartResponse{
|
||||||
|
Pid: state.Pid,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Delete(ctx context.Context, r *api.DeleteTaskRequest, _ ...grpc.CallOption) (*api.DeleteResponse, error) {
|
||||||
|
t, err := l.getTask(ctx, r.ContainerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
runtime, err := l.getRuntime(t.Info().Runtime)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
exit, err := runtime.Delete(ctx, t)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
return &api.DeleteResponse{
|
||||||
|
ExitStatus: exit.Status,
|
||||||
|
ExitedAt: exit.Timestamp,
|
||||||
|
Pid: exit.Pid,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) DeleteProcess(ctx context.Context, r *api.DeleteProcessRequest, _ ...grpc.CallOption) (*api.DeleteResponse, error) {
|
||||||
|
t, err := l.getTask(ctx, r.ContainerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
exit, err := t.DeleteProcess(ctx, r.ExecID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
return &api.DeleteResponse{
|
||||||
|
ID: r.ExecID,
|
||||||
|
ExitStatus: exit.Status,
|
||||||
|
ExitedAt: exit.Timestamp,
|
||||||
|
Pid: exit.Pid,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func processFromContainerd(ctx context.Context, p runtime.Process) (*task.Process, error) {
|
||||||
|
state, err := p.State(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var status task.Status
|
||||||
|
switch state.Status {
|
||||||
|
case runtime.CreatedStatus:
|
||||||
|
status = task.StatusCreated
|
||||||
|
case runtime.RunningStatus:
|
||||||
|
status = task.StatusRunning
|
||||||
|
case runtime.StoppedStatus:
|
||||||
|
status = task.StatusStopped
|
||||||
|
case runtime.PausedStatus:
|
||||||
|
status = task.StatusPaused
|
||||||
|
case runtime.PausingStatus:
|
||||||
|
status = task.StatusPausing
|
||||||
|
default:
|
||||||
|
log.G(ctx).WithField("status", state.Status).Warn("unknown status")
|
||||||
|
}
|
||||||
|
return &task.Process{
|
||||||
|
ID: p.ID(),
|
||||||
|
Pid: state.Pid,
|
||||||
|
Status: status,
|
||||||
|
Stdin: state.Stdin,
|
||||||
|
Stdout: state.Stdout,
|
||||||
|
Stderr: state.Stderr,
|
||||||
|
Terminal: state.Terminal,
|
||||||
|
ExitStatus: state.ExitStatus,
|
||||||
|
ExitedAt: state.ExitedAt,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Get(ctx context.Context, r *api.GetRequest, _ ...grpc.CallOption) (*api.GetResponse, error) {
|
||||||
|
task, err := l.getTask(ctx, r.ContainerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p := runtime.Process(task)
|
||||||
|
if r.ExecID != "" {
|
||||||
|
if p, err = task.Process(ctx, r.ExecID); err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t, err := processFromContainerd(ctx, p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
return &api.GetResponse{
|
||||||
|
Process: t,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) List(ctx context.Context, r *api.ListTasksRequest, _ ...grpc.CallOption) (*api.ListTasksResponse, error) {
|
||||||
|
resp := &api.ListTasksResponse{}
|
||||||
|
for _, r := range l.runtimes {
|
||||||
|
tasks, err := r.Tasks(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
addTasks(ctx, resp, tasks)
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addTasks(ctx context.Context, r *api.ListTasksResponse, tasks []runtime.Task) {
|
||||||
|
for _, t := range tasks {
|
||||||
|
tt, err := processFromContainerd(ctx, t)
|
||||||
|
if err != nil {
|
||||||
|
if !errdefs.IsNotFound(err) { // handle race with deletion
|
||||||
|
log.G(ctx).WithError(err).WithField("id", t.ID()).Error("converting task to protobuf")
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r.Tasks = append(r.Tasks, tt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Pause(ctx context.Context, r *api.PauseTaskRequest, _ ...grpc.CallOption) (*ptypes.Empty, error) {
|
||||||
|
t, err := l.getTask(ctx, r.ContainerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = t.Pause(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
return empty, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Resume(ctx context.Context, r *api.ResumeTaskRequest, _ ...grpc.CallOption) (*ptypes.Empty, error) {
|
||||||
|
t, err := l.getTask(ctx, r.ContainerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = t.Resume(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
return empty, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Kill(ctx context.Context, r *api.KillRequest, _ ...grpc.CallOption) (*ptypes.Empty, error) {
|
||||||
|
t, err := l.getTask(ctx, r.ContainerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p := runtime.Process(t)
|
||||||
|
if r.ExecID != "" {
|
||||||
|
if p, err = t.Process(ctx, r.ExecID); err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := p.Kill(ctx, r.Signal, r.All); err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
return empty, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) ListPids(ctx context.Context, r *api.ListPidsRequest, _ ...grpc.CallOption) (*api.ListPidsResponse, error) {
|
||||||
|
t, err := l.getTask(ctx, r.ContainerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
processList, err := t.Pids(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
var processes []*task.ProcessInfo
|
||||||
|
for _, p := range processList {
|
||||||
|
pInfo := task.ProcessInfo{
|
||||||
|
Pid: p.Pid,
|
||||||
|
}
|
||||||
|
if p.Info != nil {
|
||||||
|
a, err := typeurl.MarshalAny(p.Info)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to marshal process %d info", p.Pid)
|
||||||
|
}
|
||||||
|
pInfo.Info = a
|
||||||
|
}
|
||||||
|
processes = append(processes, &pInfo)
|
||||||
|
}
|
||||||
|
return &api.ListPidsResponse{
|
||||||
|
Processes: processes,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Exec(ctx context.Context, r *api.ExecProcessRequest, _ ...grpc.CallOption) (*ptypes.Empty, error) {
|
||||||
|
if r.ExecID == "" {
|
||||||
|
return nil, status.Errorf(codes.InvalidArgument, "exec id cannot be empty")
|
||||||
|
}
|
||||||
|
t, err := l.getTask(ctx, r.ContainerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := t.Exec(ctx, r.ExecID, runtime.ExecOpts{
|
||||||
|
Spec: r.Spec,
|
||||||
|
IO: runtime.IO{
|
||||||
|
Stdin: r.Stdin,
|
||||||
|
Stdout: r.Stdout,
|
||||||
|
Stderr: r.Stderr,
|
||||||
|
Terminal: r.Terminal,
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
return empty, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) ResizePty(ctx context.Context, r *api.ResizePtyRequest, _ ...grpc.CallOption) (*ptypes.Empty, error) {
|
||||||
|
t, err := l.getTask(ctx, r.ContainerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p := runtime.Process(t)
|
||||||
|
if r.ExecID != "" {
|
||||||
|
if p, err = t.Process(ctx, r.ExecID); err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := p.ResizePty(ctx, runtime.ConsoleSize{
|
||||||
|
Width: r.Width,
|
||||||
|
Height: r.Height,
|
||||||
|
}); err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
return empty, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) CloseIO(ctx context.Context, r *api.CloseIORequest, _ ...grpc.CallOption) (*ptypes.Empty, error) {
|
||||||
|
t, err := l.getTask(ctx, r.ContainerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p := runtime.Process(t)
|
||||||
|
if r.ExecID != "" {
|
||||||
|
if p, err = t.Process(ctx, r.ExecID); err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.Stdin {
|
||||||
|
if err := p.CloseIO(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return empty, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Checkpoint(ctx context.Context, r *api.CheckpointTaskRequest, _ ...grpc.CallOption) (*api.CheckpointTaskResponse, error) {
|
||||||
|
container, err := l.getContainer(ctx, r.ContainerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t, err := l.getTaskFromContainer(ctx, container)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
image, err := ioutil.TempDir("", "ctd-checkpoint")
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(image)
|
||||||
|
if err := t.Checkpoint(ctx, image, r.Options); err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
// write checkpoint to the content store
|
||||||
|
tar := archive.Diff(ctx, "", image)
|
||||||
|
cp, err := l.writeContent(ctx, images.MediaTypeContainerd1Checkpoint, image, tar)
|
||||||
|
// close tar first after write
|
||||||
|
if err := tar.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// write the config to the content store
|
||||||
|
data, err := container.Spec.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
spec := bytes.NewReader(data)
|
||||||
|
specD, err := l.writeContent(ctx, images.MediaTypeContainerd1CheckpointConfig, filepath.Join(image, "spec"), spec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
return &api.CheckpointTaskResponse{
|
||||||
|
Descriptors: []*types.Descriptor{
|
||||||
|
cp,
|
||||||
|
specD,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Update(ctx context.Context, r *api.UpdateTaskRequest, _ ...grpc.CallOption) (*ptypes.Empty, error) {
|
||||||
|
t, err := l.getTask(ctx, r.ContainerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := t.Update(ctx, r.Resources); err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
return empty, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Metrics(ctx context.Context, r *api.MetricsRequest, _ ...grpc.CallOption) (*api.MetricsResponse, error) {
|
||||||
|
filter, err := filters.ParseAll(r.Filters...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var resp api.MetricsResponse
|
||||||
|
for _, r := range l.runtimes {
|
||||||
|
tasks, err := r.Tasks(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
getTasksMetrics(ctx, filter, tasks, &resp)
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) Wait(ctx context.Context, r *api.WaitRequest, _ ...grpc.CallOption) (*api.WaitResponse, error) {
|
||||||
|
t, err := l.getTask(ctx, r.ContainerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p := runtime.Process(t)
|
||||||
|
if r.ExecID != "" {
|
||||||
|
if p, err = t.Process(ctx, r.ExecID); err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exit, err := p.Wait(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
return &api.WaitResponse{
|
||||||
|
ExitStatus: exit.Status,
|
||||||
|
ExitedAt: exit.Timestamp,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTasksMetrics(ctx context.Context, filter filters.Filter, tasks []runtime.Task, r *api.MetricsResponse) {
|
||||||
|
for _, tk := range tasks {
|
||||||
|
if !filter.Match(filters.AdapterFunc(func(fieldpath []string) (string, bool) {
|
||||||
|
t := tk
|
||||||
|
switch fieldpath[0] {
|
||||||
|
case "id":
|
||||||
|
return t.ID(), true
|
||||||
|
case "namespace":
|
||||||
|
return t.Info().Namespace, true
|
||||||
|
case "runtime":
|
||||||
|
return t.Info().Runtime, true
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
})) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
collected := time.Now()
|
||||||
|
metrics, err := tk.Metrics(ctx)
|
||||||
|
if err != nil {
|
||||||
|
if !errdefs.IsNotFound(err) {
|
||||||
|
log.G(ctx).WithError(err).Errorf("collecting metrics for %s", tk.ID())
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
data, err := typeurl.MarshalAny(metrics)
|
||||||
|
if err != nil {
|
||||||
|
log.G(ctx).WithError(err).Errorf("marshal metrics for %s", tk.ID())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r.Metrics = append(r.Metrics, &types.Metric{
|
||||||
|
ID: tk.ID(),
|
||||||
|
Timestamp: collected,
|
||||||
|
Data: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) writeContent(ctx context.Context, mediaType, ref string, r io.Reader) (*types.Descriptor, error) {
|
||||||
|
writer, err := l.store.Writer(ctx, ref, 0, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer writer.Close()
|
||||||
|
size, err := io.Copy(writer, r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := writer.Commit(ctx, 0, ""); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &types.Descriptor{
|
||||||
|
MediaType: mediaType,
|
||||||
|
Digest: writer.Digest(),
|
||||||
|
Size_: size,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) getContainer(ctx context.Context, id string) (*containers.Container, error) {
|
||||||
|
var container containers.Container
|
||||||
|
if err := l.db.View(func(tx *bolt.Tx) error {
|
||||||
|
store := metadata.NewContainerStore(tx)
|
||||||
|
var err error
|
||||||
|
container, err = store.Get(ctx, id)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
return &container, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) getTask(ctx context.Context, id string) (runtime.Task, error) {
|
||||||
|
container, err := l.getContainer(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return l.getTaskFromContainer(ctx, container)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) getTaskFromContainer(ctx context.Context, container *containers.Container) (runtime.Task, error) {
|
||||||
|
runtime, err := l.getRuntime(container.Runtime.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPCf(err, "runtime for task %s", container.Runtime.Name)
|
||||||
|
}
|
||||||
|
t, err := runtime.Get(ctx, container.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.NotFound, "task %v not found", container.ID)
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *local) getRuntime(name string) (runtime.Runtime, error) {
|
||||||
|
runtime, ok := l.runtimes[name]
|
||||||
|
if !ok {
|
||||||
|
return nil, status.Errorf(codes.NotFound, "unknown runtime %q", name)
|
||||||
|
}
|
||||||
|
return runtime, nil
|
||||||
|
}
|
@ -17,42 +17,17 @@
|
|||||||
package tasks
|
package tasks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/boltdb/bolt"
|
|
||||||
api "github.com/containerd/containerd/api/services/tasks/v1"
|
api "github.com/containerd/containerd/api/services/tasks/v1"
|
||||||
"github.com/containerd/containerd/api/types"
|
|
||||||
"github.com/containerd/containerd/api/types/task"
|
|
||||||
"github.com/containerd/containerd/archive"
|
|
||||||
"github.com/containerd/containerd/containers"
|
|
||||||
"github.com/containerd/containerd/content"
|
|
||||||
"github.com/containerd/containerd/errdefs"
|
|
||||||
"github.com/containerd/containerd/events"
|
|
||||||
"github.com/containerd/containerd/filters"
|
|
||||||
"github.com/containerd/containerd/images"
|
|
||||||
"github.com/containerd/containerd/log"
|
|
||||||
"github.com/containerd/containerd/metadata"
|
|
||||||
"github.com/containerd/containerd/mount"
|
|
||||||
"github.com/containerd/containerd/plugin"
|
"github.com/containerd/containerd/plugin"
|
||||||
"github.com/containerd/containerd/runtime"
|
"github.com/containerd/containerd/services"
|
||||||
"github.com/containerd/typeurl"
|
|
||||||
ptypes "github.com/gogo/protobuf/types"
|
ptypes "github.com/gogo/protobuf/types"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ = (api.TasksServer)(&service{})
|
_ = (api.TasksServer)(&service{})
|
||||||
empty = &ptypes.Empty{}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -60,51 +35,28 @@ func init() {
|
|||||||
Type: plugin.GRPCPlugin,
|
Type: plugin.GRPCPlugin,
|
||||||
ID: "tasks",
|
ID: "tasks",
|
||||||
Requires: []plugin.Type{
|
Requires: []plugin.Type{
|
||||||
plugin.RuntimePlugin,
|
plugin.ServicePlugin,
|
||||||
plugin.MetadataPlugin,
|
},
|
||||||
|
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
||||||
|
plugins, err := ic.GetByType(plugin.ServicePlugin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p, ok := plugins[services.TasksService]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("tasks service not found")
|
||||||
|
}
|
||||||
|
i, err := p.Instance()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &service{local: i.(api.TasksClient)}, nil
|
||||||
},
|
},
|
||||||
InitFn: initFunc,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func initFunc(ic *plugin.InitContext) (interface{}, error) {
|
|
||||||
rt, err := ic.GetByType(plugin.RuntimePlugin)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
m, err := ic.Get(plugin.MetadataPlugin)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
cs := m.(*metadata.DB).ContentStore()
|
|
||||||
runtimes := make(map[string]runtime.Runtime)
|
|
||||||
for _, rr := range rt {
|
|
||||||
ri, err := rr.Instance()
|
|
||||||
if err != nil {
|
|
||||||
log.G(ic.Context).WithError(err).Warn("could not load runtime instance due to initialization error")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
r := ri.(runtime.Runtime)
|
|
||||||
runtimes[r.ID()] = r
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(runtimes) == 0 {
|
|
||||||
return nil, errors.New("no runtimes available to create task service")
|
|
||||||
}
|
|
||||||
return &service{
|
|
||||||
runtimes: runtimes,
|
|
||||||
db: m.(*metadata.DB),
|
|
||||||
store: cs,
|
|
||||||
publisher: ic.Events,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type service struct {
|
type service struct {
|
||||||
runtimes map[string]runtime.Runtime
|
local api.TasksClient
|
||||||
db *metadata.DB
|
|
||||||
store content.Store
|
|
||||||
publisher events.Publisher
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Register(server *grpc.Server) error {
|
func (s *service) Register(server *grpc.Server) error {
|
||||||
@ -113,526 +65,69 @@ func (s *service) Register(server *grpc.Server) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Create(ctx context.Context, r *api.CreateTaskRequest) (*api.CreateTaskResponse, error) {
|
func (s *service) Create(ctx context.Context, r *api.CreateTaskRequest) (*api.CreateTaskResponse, error) {
|
||||||
var (
|
return s.local.Create(ctx, r)
|
||||||
checkpointPath string
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
if r.Checkpoint != nil {
|
|
||||||
checkpointPath, err = ioutil.TempDir("", "ctrd-checkpoint")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if r.Checkpoint.MediaType != images.MediaTypeContainerd1Checkpoint {
|
|
||||||
return nil, fmt.Errorf("unsupported checkpoint type %q", r.Checkpoint.MediaType)
|
|
||||||
}
|
|
||||||
reader, err := s.store.ReaderAt(ctx, r.Checkpoint.Digest)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
_, err = archive.Apply(ctx, checkpointPath, content.NewReader(reader))
|
|
||||||
reader.Close()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
container, err := s.getContainer(ctx, r.ContainerID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
opts := runtime.CreateOpts{
|
|
||||||
Spec: container.Spec,
|
|
||||||
IO: runtime.IO{
|
|
||||||
Stdin: r.Stdin,
|
|
||||||
Stdout: r.Stdout,
|
|
||||||
Stderr: r.Stderr,
|
|
||||||
Terminal: r.Terminal,
|
|
||||||
},
|
|
||||||
Checkpoint: checkpointPath,
|
|
||||||
Options: r.Options,
|
|
||||||
}
|
|
||||||
for _, m := range r.Rootfs {
|
|
||||||
opts.Rootfs = append(opts.Rootfs, mount.Mount{
|
|
||||||
Type: m.Type,
|
|
||||||
Source: m.Source,
|
|
||||||
Options: m.Options,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
runtime, err := s.getRuntime(container.Runtime.Name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
c, err := runtime.Create(ctx, r.ContainerID, opts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
state, err := c.State(ctx)
|
|
||||||
if err != nil {
|
|
||||||
log.G(ctx).Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &api.CreateTaskResponse{
|
|
||||||
ContainerID: r.ContainerID,
|
|
||||||
Pid: state.Pid,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Start(ctx context.Context, r *api.StartRequest) (*api.StartResponse, error) {
|
func (s *service) Start(ctx context.Context, r *api.StartRequest) (*api.StartResponse, error) {
|
||||||
t, err := s.getTask(ctx, r.ContainerID)
|
return s.local.Start(ctx, r)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
p := runtime.Process(t)
|
|
||||||
if r.ExecID != "" {
|
|
||||||
if p, err = t.Process(ctx, r.ExecID); err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := p.Start(ctx); err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
state, err := p.State(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
return &api.StartResponse{
|
|
||||||
Pid: state.Pid,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Delete(ctx context.Context, r *api.DeleteTaskRequest) (*api.DeleteResponse, error) {
|
func (s *service) Delete(ctx context.Context, r *api.DeleteTaskRequest) (*api.DeleteResponse, error) {
|
||||||
t, err := s.getTask(ctx, r.ContainerID)
|
return s.local.Delete(ctx, r)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
runtime, err := s.getRuntime(t.Info().Runtime)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
exit, err := runtime.Delete(ctx, t)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
return &api.DeleteResponse{
|
|
||||||
ExitStatus: exit.Status,
|
|
||||||
ExitedAt: exit.Timestamp,
|
|
||||||
Pid: exit.Pid,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) DeleteProcess(ctx context.Context, r *api.DeleteProcessRequest) (*api.DeleteResponse, error) {
|
func (s *service) DeleteProcess(ctx context.Context, r *api.DeleteProcessRequest) (*api.DeleteResponse, error) {
|
||||||
t, err := s.getTask(ctx, r.ContainerID)
|
return s.local.DeleteProcess(ctx, r)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
exit, err := t.DeleteProcess(ctx, r.ExecID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
return &api.DeleteResponse{
|
|
||||||
ID: r.ExecID,
|
|
||||||
ExitStatus: exit.Status,
|
|
||||||
ExitedAt: exit.Timestamp,
|
|
||||||
Pid: exit.Pid,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func processFromContainerd(ctx context.Context, p runtime.Process) (*task.Process, error) {
|
|
||||||
state, err := p.State(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var status task.Status
|
|
||||||
switch state.Status {
|
|
||||||
case runtime.CreatedStatus:
|
|
||||||
status = task.StatusCreated
|
|
||||||
case runtime.RunningStatus:
|
|
||||||
status = task.StatusRunning
|
|
||||||
case runtime.StoppedStatus:
|
|
||||||
status = task.StatusStopped
|
|
||||||
case runtime.PausedStatus:
|
|
||||||
status = task.StatusPaused
|
|
||||||
case runtime.PausingStatus:
|
|
||||||
status = task.StatusPausing
|
|
||||||
default:
|
|
||||||
log.G(ctx).WithField("status", state.Status).Warn("unknown status")
|
|
||||||
}
|
|
||||||
return &task.Process{
|
|
||||||
ID: p.ID(),
|
|
||||||
Pid: state.Pid,
|
|
||||||
Status: status,
|
|
||||||
Stdin: state.Stdin,
|
|
||||||
Stdout: state.Stdout,
|
|
||||||
Stderr: state.Stderr,
|
|
||||||
Terminal: state.Terminal,
|
|
||||||
ExitStatus: state.ExitStatus,
|
|
||||||
ExitedAt: state.ExitedAt,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Get(ctx context.Context, r *api.GetRequest) (*api.GetResponse, error) {
|
func (s *service) Get(ctx context.Context, r *api.GetRequest) (*api.GetResponse, error) {
|
||||||
task, err := s.getTask(ctx, r.ContainerID)
|
return s.local.Get(ctx, r)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
p := runtime.Process(task)
|
|
||||||
if r.ExecID != "" {
|
|
||||||
if p, err = task.Process(ctx, r.ExecID); err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t, err := processFromContainerd(ctx, p)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
return &api.GetResponse{
|
|
||||||
Process: t,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) List(ctx context.Context, r *api.ListTasksRequest) (*api.ListTasksResponse, error) {
|
func (s *service) List(ctx context.Context, r *api.ListTasksRequest) (*api.ListTasksResponse, error) {
|
||||||
resp := &api.ListTasksResponse{}
|
return s.local.List(ctx, r)
|
||||||
for _, r := range s.runtimes {
|
|
||||||
tasks, err := r.Tasks(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
addTasks(ctx, resp, tasks)
|
|
||||||
}
|
|
||||||
return resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func addTasks(ctx context.Context, r *api.ListTasksResponse, tasks []runtime.Task) {
|
|
||||||
for _, t := range tasks {
|
|
||||||
tt, err := processFromContainerd(ctx, t)
|
|
||||||
if err != nil {
|
|
||||||
if !errdefs.IsNotFound(err) { // handle race with deletion
|
|
||||||
log.G(ctx).WithError(err).WithField("id", t.ID()).Error("converting task to protobuf")
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
r.Tasks = append(r.Tasks, tt)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Pause(ctx context.Context, r *api.PauseTaskRequest) (*ptypes.Empty, error) {
|
func (s *service) Pause(ctx context.Context, r *api.PauseTaskRequest) (*ptypes.Empty, error) {
|
||||||
t, err := s.getTask(ctx, r.ContainerID)
|
return s.local.Pause(ctx, r)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = t.Pause(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
return empty, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Resume(ctx context.Context, r *api.ResumeTaskRequest) (*ptypes.Empty, error) {
|
func (s *service) Resume(ctx context.Context, r *api.ResumeTaskRequest) (*ptypes.Empty, error) {
|
||||||
t, err := s.getTask(ctx, r.ContainerID)
|
return s.local.Resume(ctx, r)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = t.Resume(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
return empty, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Kill(ctx context.Context, r *api.KillRequest) (*ptypes.Empty, error) {
|
func (s *service) Kill(ctx context.Context, r *api.KillRequest) (*ptypes.Empty, error) {
|
||||||
t, err := s.getTask(ctx, r.ContainerID)
|
return s.local.Kill(ctx, r)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
p := runtime.Process(t)
|
|
||||||
if r.ExecID != "" {
|
|
||||||
if p, err = t.Process(ctx, r.ExecID); err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := p.Kill(ctx, r.Signal, r.All); err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
return empty, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) ListPids(ctx context.Context, r *api.ListPidsRequest) (*api.ListPidsResponse, error) {
|
func (s *service) ListPids(ctx context.Context, r *api.ListPidsRequest) (*api.ListPidsResponse, error) {
|
||||||
t, err := s.getTask(ctx, r.ContainerID)
|
return s.local.ListPids(ctx, r)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
processList, err := t.Pids(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
var processes []*task.ProcessInfo
|
|
||||||
for _, p := range processList {
|
|
||||||
pInfo := task.ProcessInfo{
|
|
||||||
Pid: p.Pid,
|
|
||||||
}
|
|
||||||
if p.Info != nil {
|
|
||||||
a, err := typeurl.MarshalAny(p.Info)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrapf(err, "failed to marshal process %d info", p.Pid)
|
|
||||||
}
|
|
||||||
pInfo.Info = a
|
|
||||||
}
|
|
||||||
processes = append(processes, &pInfo)
|
|
||||||
}
|
|
||||||
return &api.ListPidsResponse{
|
|
||||||
Processes: processes,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Exec(ctx context.Context, r *api.ExecProcessRequest) (*ptypes.Empty, error) {
|
func (s *service) Exec(ctx context.Context, r *api.ExecProcessRequest) (*ptypes.Empty, error) {
|
||||||
if r.ExecID == "" {
|
return s.local.Exec(ctx, r)
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "exec id cannot be empty")
|
|
||||||
}
|
|
||||||
t, err := s.getTask(ctx, r.ContainerID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if _, err := t.Exec(ctx, r.ExecID, runtime.ExecOpts{
|
|
||||||
Spec: r.Spec,
|
|
||||||
IO: runtime.IO{
|
|
||||||
Stdin: r.Stdin,
|
|
||||||
Stdout: r.Stdout,
|
|
||||||
Stderr: r.Stderr,
|
|
||||||
Terminal: r.Terminal,
|
|
||||||
},
|
|
||||||
}); err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
return empty, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) ResizePty(ctx context.Context, r *api.ResizePtyRequest) (*ptypes.Empty, error) {
|
func (s *service) ResizePty(ctx context.Context, r *api.ResizePtyRequest) (*ptypes.Empty, error) {
|
||||||
t, err := s.getTask(ctx, r.ContainerID)
|
return s.local.ResizePty(ctx, r)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
p := runtime.Process(t)
|
|
||||||
if r.ExecID != "" {
|
|
||||||
if p, err = t.Process(ctx, r.ExecID); err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := p.ResizePty(ctx, runtime.ConsoleSize{
|
|
||||||
Width: r.Width,
|
|
||||||
Height: r.Height,
|
|
||||||
}); err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
return empty, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) CloseIO(ctx context.Context, r *api.CloseIORequest) (*ptypes.Empty, error) {
|
func (s *service) CloseIO(ctx context.Context, r *api.CloseIORequest) (*ptypes.Empty, error) {
|
||||||
t, err := s.getTask(ctx, r.ContainerID)
|
return s.local.CloseIO(ctx, r)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
p := runtime.Process(t)
|
|
||||||
if r.ExecID != "" {
|
|
||||||
if p, err = t.Process(ctx, r.ExecID); err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if r.Stdin {
|
|
||||||
if err := p.CloseIO(ctx); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return empty, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Checkpoint(ctx context.Context, r *api.CheckpointTaskRequest) (*api.CheckpointTaskResponse, error) {
|
func (s *service) Checkpoint(ctx context.Context, r *api.CheckpointTaskRequest) (*api.CheckpointTaskResponse, error) {
|
||||||
container, err := s.getContainer(ctx, r.ContainerID)
|
return s.local.Checkpoint(ctx, r)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
t, err := s.getTaskFromContainer(ctx, container)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
image, err := ioutil.TempDir("", "ctd-checkpoint")
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(image)
|
|
||||||
if err := t.Checkpoint(ctx, image, r.Options); err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
// write checkpoint to the content store
|
|
||||||
tar := archive.Diff(ctx, "", image)
|
|
||||||
cp, err := s.writeContent(ctx, images.MediaTypeContainerd1Checkpoint, image, tar)
|
|
||||||
// close tar first after write
|
|
||||||
if err := tar.Close(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// write the config to the content store
|
|
||||||
data, err := container.Spec.Marshal()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
spec := bytes.NewReader(data)
|
|
||||||
specD, err := s.writeContent(ctx, images.MediaTypeContainerd1CheckpointConfig, filepath.Join(image, "spec"), spec)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
return &api.CheckpointTaskResponse{
|
|
||||||
Descriptors: []*types.Descriptor{
|
|
||||||
cp,
|
|
||||||
specD,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Update(ctx context.Context, r *api.UpdateTaskRequest) (*ptypes.Empty, error) {
|
func (s *service) Update(ctx context.Context, r *api.UpdateTaskRequest) (*ptypes.Empty, error) {
|
||||||
t, err := s.getTask(ctx, r.ContainerID)
|
return s.local.Update(ctx, r)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := t.Update(ctx, r.Resources); err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
return empty, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Metrics(ctx context.Context, r *api.MetricsRequest) (*api.MetricsResponse, error) {
|
func (s *service) Metrics(ctx context.Context, r *api.MetricsRequest) (*api.MetricsResponse, error) {
|
||||||
filter, err := filters.ParseAll(r.Filters...)
|
return s.local.Metrics(ctx, r)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var resp api.MetricsResponse
|
|
||||||
for _, r := range s.runtimes {
|
|
||||||
tasks, err := r.Tasks(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
getTasksMetrics(ctx, filter, tasks, &resp)
|
|
||||||
}
|
|
||||||
return &resp, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Wait(ctx context.Context, r *api.WaitRequest) (*api.WaitResponse, error) {
|
func (s *service) Wait(ctx context.Context, r *api.WaitRequest) (*api.WaitResponse, error) {
|
||||||
t, err := s.getTask(ctx, r.ContainerID)
|
return s.local.Wait(ctx, r)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
p := runtime.Process(t)
|
|
||||||
if r.ExecID != "" {
|
|
||||||
if p, err = t.Process(ctx, r.ExecID); err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exit, err := p.Wait(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
return &api.WaitResponse{
|
|
||||||
ExitStatus: exit.Status,
|
|
||||||
ExitedAt: exit.Timestamp,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTasksMetrics(ctx context.Context, filter filters.Filter, tasks []runtime.Task, r *api.MetricsResponse) {
|
|
||||||
for _, tk := range tasks {
|
|
||||||
if !filter.Match(filters.AdapterFunc(func(fieldpath []string) (string, bool) {
|
|
||||||
t := tk
|
|
||||||
switch fieldpath[0] {
|
|
||||||
case "id":
|
|
||||||
return t.ID(), true
|
|
||||||
case "namespace":
|
|
||||||
return t.Info().Namespace, true
|
|
||||||
case "runtime":
|
|
||||||
return t.Info().Runtime, true
|
|
||||||
}
|
|
||||||
return "", false
|
|
||||||
})) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
collected := time.Now()
|
|
||||||
metrics, err := tk.Metrics(ctx)
|
|
||||||
if err != nil {
|
|
||||||
if !errdefs.IsNotFound(err) {
|
|
||||||
log.G(ctx).WithError(err).Errorf("collecting metrics for %s", tk.ID())
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
data, err := typeurl.MarshalAny(metrics)
|
|
||||||
if err != nil {
|
|
||||||
log.G(ctx).WithError(err).Errorf("marshal metrics for %s", tk.ID())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
r.Metrics = append(r.Metrics, &types.Metric{
|
|
||||||
ID: tk.ID(),
|
|
||||||
Timestamp: collected,
|
|
||||||
Data: data,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) writeContent(ctx context.Context, mediaType, ref string, r io.Reader) (*types.Descriptor, error) {
|
|
||||||
writer, err := s.store.Writer(ctx, ref, 0, "")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer writer.Close()
|
|
||||||
size, err := io.Copy(writer, r)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := writer.Commit(ctx, 0, ""); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &types.Descriptor{
|
|
||||||
MediaType: mediaType,
|
|
||||||
Digest: writer.Digest(),
|
|
||||||
Size_: size,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) getContainer(ctx context.Context, id string) (*containers.Container, error) {
|
|
||||||
var container containers.Container
|
|
||||||
if err := s.db.View(func(tx *bolt.Tx) error {
|
|
||||||
store := metadata.NewContainerStore(tx)
|
|
||||||
var err error
|
|
||||||
container, err = store.Get(ctx, id)
|
|
||||||
return err
|
|
||||||
}); err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
return &container, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) getTask(ctx context.Context, id string) (runtime.Task, error) {
|
|
||||||
container, err := s.getContainer(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return s.getTaskFromContainer(ctx, container)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) getTaskFromContainer(ctx context.Context, container *containers.Container) (runtime.Task, error) {
|
|
||||||
runtime, err := s.getRuntime(container.Runtime.Name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPCf(err, "runtime for task %s", container.Runtime.Name)
|
|
||||||
}
|
|
||||||
t, err := runtime.Get(ctx, container.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.NotFound, "task %v not found", container.ID)
|
|
||||||
}
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) getRuntime(name string) (runtime.Runtime, error) {
|
|
||||||
runtime, ok := s.runtimes[name]
|
|
||||||
if !ok {
|
|
||||||
return nil, status.Errorf(codes.NotFound, "unknown runtime %q", name)
|
|
||||||
}
|
|
||||||
return runtime, nil
|
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16
|
|||||||
github.com/gotestyourself/gotestyourself 44dbf532bbf5767611f6f2a61bded572e337010a
|
github.com/gotestyourself/gotestyourself 44dbf532bbf5767611f6f2a61bded572e337010a
|
||||||
github.com/google/go-cmp v0.1.0
|
github.com/google/go-cmp v0.1.0
|
||||||
# cri dependencies
|
# cri dependencies
|
||||||
github.com/containerd/cri-containerd c9081b2ec0eefc799f0f1caabbea29d516c72c44
|
github.com/containerd/cri-containerd dcc278739fb31c5369f927c69716275c084c3d53 https://github.com/Random-Liu/cri-containerd.git
|
||||||
github.com/blang/semver v3.1.0
|
github.com/blang/semver v3.1.0
|
||||||
github.com/containernetworking/cni v0.6.0
|
github.com/containernetworking/cni v0.6.0
|
||||||
github.com/containernetworking/plugins v0.6.0
|
github.com/containernetworking/plugins v0.6.0
|
||||||
|
5
vendor/github.com/containerd/cri-containerd/README.md
generated
vendored
5
vendor/github.com/containerd/cri-containerd/README.md
generated
vendored
@ -24,7 +24,8 @@ See [test dashboard](https://k8s-testgrid.appspot.com/sig-node-containerd)
|
|||||||
| CRI-Containerd Version | Kubernetes Version |
|
| CRI-Containerd Version | Kubernetes Version |
|
||||||
|:----------------------:|:------------------:|
|
|:----------------------:|:------------------:|
|
||||||
| v1.0.0-alpha.x | 1.7, 1.8 |
|
| v1.0.0-alpha.x | 1.7, 1.8 |
|
||||||
| v1.0.0-beta.x | 1.9+ |
|
| v1.0.0-beta.x | 1.9 |
|
||||||
|
| HEAD | 1.10+ |
|
||||||
## Production Quality Cluster on GCE
|
## Production Quality Cluster on GCE
|
||||||
For a production quality cluster on GCE brought up with `kube-up.sh` refer [here](docs/kube-up.md).
|
For a production quality cluster on GCE brought up with `kube-up.sh` refer [here](docs/kube-up.md).
|
||||||
## Installing with Ansible and Kubeadm
|
## Installing with Ansible and Kubeadm
|
||||||
@ -58,7 +59,7 @@ backport version of `libseccomp-dev` is required. See [travis.yml](.travis.yml)
|
|||||||
2. Install other dependencies:
|
2. Install other dependencies:
|
||||||
* **`nsenter`**: Required by CNI and portforward.
|
* **`nsenter`**: Required by CNI and portforward.
|
||||||
* **`socat`**: Required by portforward.
|
* **`socat`**: Required by portforward.
|
||||||
3. Install and setup a go 1.9.x development environment.
|
3. Install and setup a go 1.10 development environment.
|
||||||
4. Make a local clone of this repository.
|
4. Make a local clone of this repository.
|
||||||
5. Install binary dependencies by running the following command from your cloned `cri-containerd/` project directory:
|
5. Install binary dependencies by running the following command from your cloned `cri-containerd/` project directory:
|
||||||
```bash
|
```bash
|
||||||
|
269
vendor/github.com/containerd/cri-containerd/cmd/cri-containerd/options/options.go
generated
vendored
269
vendor/github.com/containerd/cri-containerd/cmd/cri-containerd/options/options.go
generated
vendored
@ -1,269 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes 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 options
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/BurntSushi/toml"
|
|
||||||
"github.com/containerd/containerd"
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// configFilePathArgName is the path to the config file.
|
|
||||||
configFilePathArgName = "config"
|
|
||||||
// defaultConfigFilePath is the default config file path.
|
|
||||||
defaultConfigFilePath = "/etc/cri-containerd/config.toml"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerdConfig contains toml config related to containerd
|
|
||||||
type ContainerdConfig struct {
|
|
||||||
// RootDir is the root directory path for containerd.
|
|
||||||
// TODO(random-liu): Remove this field when no longer support cri-containerd standalone mode.
|
|
||||||
RootDir string `toml:"root_dir" json:"rootDir,omitempty"`
|
|
||||||
// Snapshotter is the snapshotter used by containerd.
|
|
||||||
Snapshotter string `toml:"snapshotter" json:"snapshotter,omitempty"`
|
|
||||||
// Endpoint is the containerd endpoint path.
|
|
||||||
// TODO(random-liu): Remove this field when no longer support cri-containerd standalone mode.
|
|
||||||
Endpoint string `toml:"endpoint" json:"endpoint,omitempty"`
|
|
||||||
// Runtime is the runtime to use in containerd. We may support
|
|
||||||
// other runtimes in the future.
|
|
||||||
Runtime string `toml:"runtime" json:"runtime,omitempty"`
|
|
||||||
// RuntimeEngine is the name of the runtime engine used by containerd.
|
|
||||||
// Containerd default should be "runc"
|
|
||||||
// We may support other runtime engines in the future.
|
|
||||||
RuntimeEngine string `toml:"runtime_engine" json:"runtimeEngine,omitempty"`
|
|
||||||
// RuntimeRoot is the directory used by containerd for runtime state.
|
|
||||||
// Containerd default should be "/run/containerd/runc"
|
|
||||||
RuntimeRoot string `toml:"runtime_root" json:"runtimeRoot,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CniConfig contains toml config related to cni
|
|
||||||
type CniConfig struct {
|
|
||||||
// NetworkPluginBinDir is the directory in which the binaries for the plugin is kept.
|
|
||||||
NetworkPluginBinDir string `toml:"bin_dir" json:"binDir,omitempty"`
|
|
||||||
// NetworkPluginConfDir is the directory in which the admin places a CNI conf.
|
|
||||||
NetworkPluginConfDir string `toml:"conf_dir" json:"confDir,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// PluginConfig contains toml config related to CRI plugin,
|
|
||||||
// it is a subset of Config.
|
|
||||||
type PluginConfig struct {
|
|
||||||
// ContainerdConfig contains config related to containerd
|
|
||||||
ContainerdConfig `toml:"containerd" json:"containerd,omitempty"`
|
|
||||||
// CniConfig contains config related to cni
|
|
||||||
CniConfig `toml:"cni" json:"cni,omitempty"`
|
|
||||||
// Registry contains config related to the registry
|
|
||||||
Registry `toml:"registry" json:"registry,omitempty"`
|
|
||||||
// StreamServerAddress is the ip address streaming server is listening on.
|
|
||||||
StreamServerAddress string `toml:"stream_server_address" json:"streamServerAddress,omitempty"`
|
|
||||||
// StreamServerPort is the port streaming server is listening on.
|
|
||||||
StreamServerPort string `toml:"stream_server_port" json:"streamServerPort,omitempty"`
|
|
||||||
// EnableSelinux indicates to enable the selinux support.
|
|
||||||
EnableSelinux bool `toml:"enable_selinux" json:"enableSelinux,omitempty"`
|
|
||||||
// SandboxImage is the image used by sandbox container.
|
|
||||||
SandboxImage string `toml:"sandbox_image" json:"sandboxImage,omitempty"`
|
|
||||||
// StatsCollectPeriod is the period (in seconds) of snapshots stats collection.
|
|
||||||
StatsCollectPeriod int `toml:"stats_collect_period" json:"statsCollectPeriod,omitempty"`
|
|
||||||
// SystemdCgroup enables systemd cgroup support.
|
|
||||||
SystemdCgroup bool `toml:"systemd_cgroup" json:"systemdCgroup,omitempty"`
|
|
||||||
// EnableIPv6DAD enables IPv6 DAD.
|
|
||||||
// TODO(random-liu): Use optimistic_dad when it's GA.
|
|
||||||
EnableIPv6DAD bool `toml:"enable_ipv6_dad" json:"enableIPv6DAD,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Config contains toml config related cri-containerd daemon.
|
|
||||||
// TODO(random-liu): Make this an internal config object when we no longer support cri-containerd
|
|
||||||
// standalone mode. At that time, we can clean this up.
|
|
||||||
type Config struct {
|
|
||||||
// PluginConfig is the config for CRI plugin.
|
|
||||||
PluginConfig
|
|
||||||
// ContainerdRootDir is the root directory path for containerd.
|
|
||||||
ContainerdRootDir string `toml:"-" json:"containerdRootDir,omitempty"`
|
|
||||||
// ContainerdEndpoint is the containerd endpoint path.
|
|
||||||
ContainerdEndpoint string `toml:"-" json:"containerdEndpoint,omitempty"`
|
|
||||||
// SocketPath is the path to the socket which cri-containerd serves on.
|
|
||||||
// TODO(random-liu): Remove SocketPath when no longer support cri-containerd
|
|
||||||
// standalone mode.
|
|
||||||
SocketPath string `toml:"socket_path" json:"socketPath,omitempty"`
|
|
||||||
// RootDir is the root directory path for managing cri-containerd files
|
|
||||||
// (metadata checkpoint etc.)
|
|
||||||
RootDir string `toml:"root_dir" json:"rootDir,omitempty"`
|
|
||||||
// TODO(random-liu): Remove following fields when we no longer support cri-containerd
|
|
||||||
// standalone mode.
|
|
||||||
// CgroupPath is the path for the cgroup that cri-containerd is placed in.
|
|
||||||
CgroupPath string `toml:"cgroup_path" json:"cgroupPath,omitempty"`
|
|
||||||
// OOMScore adjust the cri-containerd's oom score
|
|
||||||
OOMScore int `toml:"oom_score" json:"oomScore,omitempty"`
|
|
||||||
// EnableProfiling is used for enable profiling via host:port/debug/pprof/
|
|
||||||
EnableProfiling bool `toml:"profiling" json:"enableProfiling,omitempty"`
|
|
||||||
// ProfilingPort is the port for profiling via host:port/debug/pprof/
|
|
||||||
ProfilingPort string `toml:"profiling_port" json:"profilingPort,omitempty"`
|
|
||||||
// ProfilingAddress is address for profiling via host:port/debug/pprof/
|
|
||||||
ProfilingAddress string `toml:"profiling_addr" json:"profilingAddress,omitempty"`
|
|
||||||
// LogLevel is the logrus log level.
|
|
||||||
LogLevel string `toml:"log_level" json:"logLevel,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CRIContainerdOptions contains cri-containerd command line and toml options.
|
|
||||||
type CRIContainerdOptions struct {
|
|
||||||
// Config contains cri-containerd toml config
|
|
||||||
Config
|
|
||||||
// ConfigFilePath is the path to the TOML config file.
|
|
||||||
ConfigFilePath string `toml:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCRIContainerdOptions returns a reference to CRIContainerdOptions
|
|
||||||
func NewCRIContainerdOptions() *CRIContainerdOptions {
|
|
||||||
return &CRIContainerdOptions{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddFlags adds cri-containerd command line options to pflag.
|
|
||||||
func (c *CRIContainerdOptions) AddFlags(fs *pflag.FlagSet) {
|
|
||||||
defaults := DefaultConfig()
|
|
||||||
fs.StringVar(&c.ConfigFilePath, configFilePathArgName,
|
|
||||||
defaultConfigFilePath, "Path to the config file.")
|
|
||||||
fs.StringVar(&c.LogLevel, "log-level",
|
|
||||||
defaults.LogLevel, "Set the logging level [trace, debug, info, warn, error, fatal, panic].")
|
|
||||||
fs.StringVar(&c.SocketPath, "socket-path",
|
|
||||||
defaults.SocketPath, "Path to the socket which cri-containerd serves on.")
|
|
||||||
fs.StringVar(&c.RootDir, "root-dir",
|
|
||||||
defaults.RootDir, "Root directory path for cri-containerd managed files (metadata checkpoint etc).")
|
|
||||||
fs.StringVar(&c.ContainerdRootDir, "containerd-root-dir",
|
|
||||||
defaults.ContainerdRootDir, "Root directory path where containerd stores persistent data.")
|
|
||||||
fs.StringVar(&c.ContainerdEndpoint, "containerd-endpoint",
|
|
||||||
defaults.ContainerdEndpoint, "Path to the containerd endpoint.")
|
|
||||||
fs.StringVar(&c.ContainerdConfig.Snapshotter, "containerd-snapshotter",
|
|
||||||
defaults.ContainerdConfig.Snapshotter, "The snapshotter used by containerd.")
|
|
||||||
fs.StringVar(&c.ContainerdConfig.Runtime, "containerd-runtime",
|
|
||||||
defaults.ContainerdConfig.Runtime, "The runtime used by containerd.")
|
|
||||||
fs.StringVar(&c.ContainerdConfig.RuntimeEngine, "containerd-runtime-engine",
|
|
||||||
defaults.ContainerdConfig.RuntimeEngine, "Runtime engine used by containerd. Defaults to containerd's default if not specified.")
|
|
||||||
fs.StringVar(&c.ContainerdConfig.RuntimeRoot, "containerd-runtime-root",
|
|
||||||
defaults.ContainerdConfig.RuntimeRoot, "The directory used by containerd for runtime state. Defaults to containerd's default if not specified.")
|
|
||||||
fs.StringVar(&c.NetworkPluginBinDir, "network-bin-dir",
|
|
||||||
defaults.NetworkPluginBinDir, "The directory for putting network binaries.")
|
|
||||||
fs.StringVar(&c.NetworkPluginConfDir, "network-conf-dir",
|
|
||||||
defaults.NetworkPluginConfDir, "The directory for putting network plugin configuration files.")
|
|
||||||
fs.StringVar(&c.StreamServerAddress, "stream-addr",
|
|
||||||
defaults.StreamServerAddress, "The ip address streaming server is listening on. The default host interface is used if not specified.")
|
|
||||||
fs.StringVar(&c.StreamServerPort, "stream-port",
|
|
||||||
defaults.StreamServerPort, "The port streaming server is listening on.")
|
|
||||||
fs.StringVar(&c.CgroupPath, "cgroup-path",
|
|
||||||
defaults.CgroupPath, "The cgroup that cri-containerd is part of. Cri-containerd is not placed in a cgroup if none is specified.")
|
|
||||||
fs.BoolVar(&c.EnableSelinux, "enable-selinux",
|
|
||||||
defaults.EnableSelinux, "Enable selinux support. By default not enabled.")
|
|
||||||
fs.StringVar(&c.SandboxImage, "sandbox-image",
|
|
||||||
defaults.SandboxImage, "The image used by sandbox container.")
|
|
||||||
fs.IntVar(&c.StatsCollectPeriod, "stats-collect-period",
|
|
||||||
defaults.StatsCollectPeriod, "The period (in seconds) of snapshots stats collection.")
|
|
||||||
fs.BoolVar(&c.SystemdCgroup, "systemd-cgroup",
|
|
||||||
defaults.SystemdCgroup, "Enables systemd cgroup support. By default not enabled.")
|
|
||||||
fs.IntVar(&c.OOMScore, "oom-score",
|
|
||||||
defaults.OOMScore, "Adjust the cri-containerd's oom score.")
|
|
||||||
fs.BoolVar(&c.EnableProfiling, "profiling",
|
|
||||||
defaults.EnableProfiling, "Enable profiling via web interface host:port/debug/pprof/.")
|
|
||||||
fs.StringVar(&c.ProfilingPort, "profiling-port",
|
|
||||||
defaults.ProfilingPort, "Profiling port for web interface host:port/debug/pprof/.")
|
|
||||||
fs.StringVar(&c.ProfilingAddress, "profiling-addr",
|
|
||||||
defaults.ProfilingAddress, "Profiling address for web interface host:port/debug/pprof/.")
|
|
||||||
fs.BoolVar(&c.EnableIPv6DAD, "enable-ipv6-dad",
|
|
||||||
defaults.EnableIPv6DAD, "Enable IPv6 DAD (duplicate address detection) for pod sandbox network. Enabling this will increase pod sandbox start latency by several seconds.")
|
|
||||||
fs.Var(&c.Registry, "registry",
|
|
||||||
"Registry config for image pull eg --registry=myregistry.io=https://mymirror.io/ --registry=myregistry2.io=https://mymirror2.io/")
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitFlags load configurations from config file, and then overwrite with flags.
|
|
||||||
// This function must be called inside `Run`, at that time flags should have been
|
|
||||||
// parsed once.
|
|
||||||
// precedence: commandline > configfile > default
|
|
||||||
func (c *CRIContainerdOptions) InitFlags(fs *pflag.FlagSet) error {
|
|
||||||
// Load default config file if none provided
|
|
||||||
if _, err := toml.DecodeFile(c.ConfigFilePath, &c.Config); err != nil {
|
|
||||||
// the absence of default config file is normal case.
|
|
||||||
if !fs.Changed(configFilePathArgName) && os.IsNotExist(err) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Add this for backward compatibility.
|
|
||||||
// TODO(random-liu): Remove this when we no longer support cri-containerd standalone mode.
|
|
||||||
c.ContainerdRootDir = c.ContainerdConfig.RootDir
|
|
||||||
c.ContainerdEndpoint = c.ContainerdConfig.Endpoint
|
|
||||||
|
|
||||||
// What is the reason for applying the command line twice?
|
|
||||||
// Because the values from command line have the highest priority.
|
|
||||||
// The path of toml configuration file if from the command line,
|
|
||||||
// and triggers the first parse.
|
|
||||||
// The first parse generates the default value and the value from command line at the same time.
|
|
||||||
// But the priority of the toml config value is higher than the default value,
|
|
||||||
// Without a way to insert the toml config value between the default value and the command line value.
|
|
||||||
// We parse twice one for default value, one for commandline value.
|
|
||||||
return fs.Parse(os.Args[1:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrintDefaultTomlConfig print default toml config of cri-containerd.
|
|
||||||
func PrintDefaultTomlConfig() {
|
|
||||||
if err := toml.NewEncoder(os.Stdout).Encode(DefaultConfig()); err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultConfig returns default configurations of cri-containerd.
|
|
||||||
func DefaultConfig() Config {
|
|
||||||
return Config{
|
|
||||||
PluginConfig: PluginConfig{
|
|
||||||
CniConfig: CniConfig{
|
|
||||||
NetworkPluginBinDir: "/opt/cni/bin",
|
|
||||||
NetworkPluginConfDir: "/etc/cni/net.d",
|
|
||||||
},
|
|
||||||
ContainerdConfig: ContainerdConfig{
|
|
||||||
Snapshotter: containerd.DefaultSnapshotter,
|
|
||||||
Runtime: "io.containerd.runtime.v1.linux",
|
|
||||||
RuntimeEngine: "",
|
|
||||||
RuntimeRoot: "",
|
|
||||||
},
|
|
||||||
StreamServerAddress: "",
|
|
||||||
StreamServerPort: "10010",
|
|
||||||
EnableSelinux: false,
|
|
||||||
SandboxImage: "gcr.io/google_containers/pause:3.0",
|
|
||||||
StatsCollectPeriod: 10,
|
|
||||||
SystemdCgroup: false,
|
|
||||||
EnableIPv6DAD: false,
|
|
||||||
Registry: Registry{
|
|
||||||
Mirrors: map[string]Mirror{
|
|
||||||
"docker.io": {
|
|
||||||
Endpoints: []string{"https://registry-1.docker.io"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ContainerdRootDir: "/var/lib/containerd",
|
|
||||||
ContainerdEndpoint: "/run/containerd/containerd.sock",
|
|
||||||
SocketPath: "/var/run/cri-containerd.sock",
|
|
||||||
RootDir: "/var/lib/cri-containerd",
|
|
||||||
CgroupPath: "",
|
|
||||||
OOMScore: -999,
|
|
||||||
EnableProfiling: true,
|
|
||||||
ProfilingPort: "10011",
|
|
||||||
ProfilingAddress: "127.0.0.1",
|
|
||||||
LogLevel: "info",
|
|
||||||
}
|
|
||||||
}
|
|
88
vendor/github.com/containerd/cri-containerd/cmd/cri-containerd/options/registry.go
generated
vendored
88
vendor/github.com/containerd/cri-containerd/cmd/cri-containerd/options/registry.go
generated
vendored
@ -1,88 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2018 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 options
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Mirror contains the config related to the registry mirror
|
|
||||||
type Mirror struct {
|
|
||||||
Endpoints []string `toml:"endpoint" json:"endpoint,omitempty"`
|
|
||||||
// TODO (Abhi) We might need to add auth per namespace. Looks like
|
|
||||||
// image auth information is passed by kube itself.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Registry is registry settings configured
|
|
||||||
type Registry struct {
|
|
||||||
Mirrors map[string]Mirror `toml:"mirrors" json:"mirrors,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the string format of registry type
|
|
||||||
func (r *Registry) String() string {
|
|
||||||
// Its not used hence return empty string
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set validates and converts into the internal registry struct
|
|
||||||
func (r *Registry) Set(s string) error {
|
|
||||||
// --registry docker.io=https://mymirror.io,http://mymirror2.io
|
|
||||||
// If no option is set then return format error
|
|
||||||
if len(s) == 0 {
|
|
||||||
return fmt.Errorf("incomplete registry mirror option")
|
|
||||||
}
|
|
||||||
var mirrors []string
|
|
||||||
host := "docker.io"
|
|
||||||
opt := strings.Split(s, "=")
|
|
||||||
if len(opt) > 1 {
|
|
||||||
// If option is set in the format "mynamespace.io=https://mymirror.io,https://mymirror2.io"
|
|
||||||
// Then associate the mirror urls for the namespace only"
|
|
||||||
host = opt[0]
|
|
||||||
mirrors = strings.Split(opt[1], ",")
|
|
||||||
} else {
|
|
||||||
// If option is set in the format "https://mymirror.io,https://mymirror.io"
|
|
||||||
// Then associate mirror against default docker.io namespace
|
|
||||||
mirrors = strings.Split(opt[0], ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the format of the urls passed
|
|
||||||
for _, u := range mirrors {
|
|
||||||
_, err := url.Parse(u)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("invalid registry mirror url format %v: %v", u, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Mirrors == nil {
|
|
||||||
r.Mirrors = make(map[string]Mirror)
|
|
||||||
}
|
|
||||||
if _, ok := r.Mirrors[host]; !ok {
|
|
||||||
r.Mirrors[host] = Mirror{}
|
|
||||||
}
|
|
||||||
m := r.Mirrors[host]
|
|
||||||
m.Endpoints = append(m.Endpoints, mirrors...)
|
|
||||||
r.Mirrors[host] = m
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type returns a string name for the option type
|
|
||||||
func (r *Registry) Type() string {
|
|
||||||
return "list"
|
|
||||||
}
|
|
45
vendor/github.com/containerd/cri-containerd/cri.go
generated
vendored
45
vendor/github.com/containerd/cri-containerd/cri.go
generated
vendored
@ -17,20 +17,27 @@ limitations under the License.
|
|||||||
package cri
|
package cri
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"flag"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/containerd/containerd/log"
|
"github.com/containerd/containerd/log"
|
||||||
|
"github.com/containerd/containerd/platforms"
|
||||||
"github.com/containerd/containerd/plugin"
|
"github.com/containerd/containerd/plugin"
|
||||||
|
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/containerd/cri-containerd/cmd/cri-containerd/options"
|
criconfig "github.com/containerd/cri-containerd/pkg/config"
|
||||||
"github.com/containerd/cri-containerd/pkg/server"
|
"github.com/containerd/cri-containerd/pkg/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// criVersion is the CRI version supported by the CRI plugin.
|
||||||
|
const criVersion = "v1alpha2"
|
||||||
|
|
||||||
// TODO(random-liu): Use github.com/pkg/errors for our errors.
|
// TODO(random-liu): Use github.com/pkg/errors for our errors.
|
||||||
// Register CRI service plugin
|
// Register CRI service plugin
|
||||||
func init() {
|
func init() {
|
||||||
config := options.DefaultConfig().PluginConfig
|
config := criconfig.DefaultConfig()
|
||||||
plugin.Register(&plugin.Registration{
|
plugin.Register(&plugin.Registration{
|
||||||
Type: plugin.GRPCPlugin,
|
Type: plugin.GRPCPlugin,
|
||||||
ID: "cri",
|
ID: "cri",
|
||||||
@ -49,9 +56,11 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func initCRIService(ic *plugin.InitContext) (interface{}, error) {
|
func initCRIService(ic *plugin.InitContext) (interface{}, error) {
|
||||||
|
ic.Meta.Platforms = []imagespec.Platform{platforms.DefaultSpec()}
|
||||||
|
ic.Meta.Exports = map[string]string{"CRIVersion": criVersion}
|
||||||
ctx := ic.Context
|
ctx := ic.Context
|
||||||
pluginConfig := ic.Config.(*options.PluginConfig)
|
pluginConfig := ic.Config.(*criconfig.PluginConfig)
|
||||||
c := options.Config{
|
c := criconfig.Config{
|
||||||
PluginConfig: *pluginConfig,
|
PluginConfig: *pluginConfig,
|
||||||
// This is a hack. We assume that containerd root directory
|
// This is a hack. We assume that containerd root directory
|
||||||
// is one level above plugin directory.
|
// is one level above plugin directory.
|
||||||
@ -62,6 +71,10 @@ func initCRIService(ic *plugin.InitContext) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
log.G(ctx).Infof("Start cri plugin with config %+v", c)
|
log.G(ctx).Infof("Start cri plugin with config %+v", c)
|
||||||
|
|
||||||
|
if err := setGLogLevel(); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to set glog level")
|
||||||
|
}
|
||||||
|
|
||||||
s, err := server.NewCRIContainerdService(c)
|
s, err := server.NewCRIContainerdService(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to create CRI service")
|
return nil, errors.Wrap(err, "failed to create CRI service")
|
||||||
@ -70,10 +83,32 @@ func initCRIService(ic *plugin.InitContext) (interface{}, error) {
|
|||||||
// Use a goroutine to initialize cri service. The reason is that currently
|
// Use a goroutine to initialize cri service. The reason is that currently
|
||||||
// cri service requires containerd to be initialize.
|
// cri service requires containerd to be initialize.
|
||||||
go func() {
|
go func() {
|
||||||
if err := s.Run(false); err != nil {
|
if err := s.Run(); err != nil {
|
||||||
log.G(ctx).WithError(err).Fatal("Failed to run CRI service")
|
log.G(ctx).WithError(err).Fatal("Failed to run CRI service")
|
||||||
}
|
}
|
||||||
// TODO(random-liu): Whether and how we can stop containerd.
|
// TODO(random-liu): Whether and how we can stop containerd.
|
||||||
}()
|
}()
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set glog level.
|
||||||
|
func setGLogLevel() error {
|
||||||
|
l := logrus.GetLevel()
|
||||||
|
if err := flag.Set("logtostderr", "true"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch l {
|
||||||
|
case log.TraceLevel:
|
||||||
|
return flag.Set("v", "5")
|
||||||
|
case logrus.DebugLevel:
|
||||||
|
return flag.Set("v", "4")
|
||||||
|
case logrus.InfoLevel:
|
||||||
|
return flag.Set("v", "2")
|
||||||
|
// glog doesn't support following filters. Defaults to v=0.
|
||||||
|
case logrus.WarnLevel:
|
||||||
|
case logrus.ErrorLevel:
|
||||||
|
case logrus.FatalLevel:
|
||||||
|
case logrus.PanicLevel:
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
127
vendor/github.com/containerd/cri-containerd/pkg/config/config.go
generated
vendored
Normal file
127
vendor/github.com/containerd/cri-containerd/pkg/config/config.go
generated
vendored
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes 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 config
|
||||||
|
|
||||||
|
import "github.com/containerd/containerd"
|
||||||
|
|
||||||
|
// ContainerdConfig contains toml config related to containerd
|
||||||
|
type ContainerdConfig struct {
|
||||||
|
// Snapshotter is the snapshotter used by containerd.
|
||||||
|
Snapshotter string `toml:"snapshotter" json:"snapshotter,omitempty"`
|
||||||
|
// Runtime is the runtime to use in containerd. We may support
|
||||||
|
// other runtimes in the future.
|
||||||
|
Runtime string `toml:"runtime" json:"runtime,omitempty"`
|
||||||
|
// RuntimeEngine is the name of the runtime engine used by containerd.
|
||||||
|
// Containerd default should be "runc"
|
||||||
|
// We may support other runtime engines in the future.
|
||||||
|
RuntimeEngine string `toml:"runtime_engine" json:"runtimeEngine,omitempty"`
|
||||||
|
// RuntimeRoot is the directory used by containerd for runtime state.
|
||||||
|
// Containerd default should be "/run/containerd/runc"
|
||||||
|
RuntimeRoot string `toml:"runtime_root" json:"runtimeRoot,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CniConfig contains toml config related to cni
|
||||||
|
type CniConfig struct {
|
||||||
|
// NetworkPluginBinDir is the directory in which the binaries for the plugin is kept.
|
||||||
|
NetworkPluginBinDir string `toml:"bin_dir" json:"binDir,omitempty"`
|
||||||
|
// NetworkPluginConfDir is the directory in which the admin places a CNI conf.
|
||||||
|
NetworkPluginConfDir string `toml:"conf_dir" json:"confDir,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mirror contains the config related to the registry mirror
|
||||||
|
type Mirror struct {
|
||||||
|
// Endpoints are endpoints for a namespace. CRI plugin will try the endpoints
|
||||||
|
// one by one until a working one is found.
|
||||||
|
Endpoints []string `toml:"endpoint" json:"endpoint,omitempty"`
|
||||||
|
// TODO (Abhi) We might need to add auth per namespace. Looks like
|
||||||
|
// image auth information is passed by kube itself.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registry is registry settings configured
|
||||||
|
type Registry struct {
|
||||||
|
// Mirrors are namespace to mirror mapping for all namespaces.
|
||||||
|
Mirrors map[string]Mirror `toml:"mirrors" json:"mirrors,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginConfig contains toml config related to CRI plugin,
|
||||||
|
// it is a subset of Config.
|
||||||
|
type PluginConfig struct {
|
||||||
|
// ContainerdConfig contains config related to containerd
|
||||||
|
ContainerdConfig `toml:"containerd" json:"containerd,omitempty"`
|
||||||
|
// CniConfig contains config related to cni
|
||||||
|
CniConfig `toml:"cni" json:"cni,omitempty"`
|
||||||
|
// Registry contains config related to the registry
|
||||||
|
Registry `toml:"registry" json:"registry,omitempty"`
|
||||||
|
// StreamServerAddress is the ip address streaming server is listening on.
|
||||||
|
StreamServerAddress string `toml:"stream_server_address" json:"streamServerAddress,omitempty"`
|
||||||
|
// StreamServerPort is the port streaming server is listening on.
|
||||||
|
StreamServerPort string `toml:"stream_server_port" json:"streamServerPort,omitempty"`
|
||||||
|
// EnableSelinux indicates to enable the selinux support.
|
||||||
|
EnableSelinux bool `toml:"enable_selinux" json:"enableSelinux,omitempty"`
|
||||||
|
// SandboxImage is the image used by sandbox container.
|
||||||
|
SandboxImage string `toml:"sandbox_image" json:"sandboxImage,omitempty"`
|
||||||
|
// StatsCollectPeriod is the period (in seconds) of snapshots stats collection.
|
||||||
|
StatsCollectPeriod int `toml:"stats_collect_period" json:"statsCollectPeriod,omitempty"`
|
||||||
|
// SystemdCgroup enables systemd cgroup support.
|
||||||
|
SystemdCgroup bool `toml:"systemd_cgroup" json:"systemdCgroup,omitempty"`
|
||||||
|
// EnableIPv6DAD enables IPv6 DAD.
|
||||||
|
// TODO(random-liu): Use optimistic_dad when it's GA.
|
||||||
|
EnableIPv6DAD bool `toml:"enable_ipv6_dad" json:"enableIPv6DAD,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config contains all configurations for cri server.
|
||||||
|
type Config struct {
|
||||||
|
// PluginConfig is the config for CRI plugin.
|
||||||
|
PluginConfig
|
||||||
|
// ContainerdRootDir is the root directory path for containerd.
|
||||||
|
ContainerdRootDir string `json:"containerdRootDir,omitempty"`
|
||||||
|
// ContainerdEndpoint is the containerd endpoint path.
|
||||||
|
ContainerdEndpoint string `json:"containerdEndpoint,omitempty"`
|
||||||
|
// RootDir is the root directory path for managing cri-containerd files
|
||||||
|
// (metadata checkpoint etc.)
|
||||||
|
RootDir string `json:"rootDir,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultConfig returns default configurations of cri plugin.
|
||||||
|
func DefaultConfig() PluginConfig {
|
||||||
|
return PluginConfig{
|
||||||
|
CniConfig: CniConfig{
|
||||||
|
NetworkPluginBinDir: "/opt/cni/bin",
|
||||||
|
NetworkPluginConfDir: "/etc/cni/net.d",
|
||||||
|
},
|
||||||
|
ContainerdConfig: ContainerdConfig{
|
||||||
|
Snapshotter: containerd.DefaultSnapshotter,
|
||||||
|
Runtime: "io.containerd.runtime.v1.linux",
|
||||||
|
RuntimeEngine: "",
|
||||||
|
RuntimeRoot: "",
|
||||||
|
},
|
||||||
|
StreamServerAddress: "",
|
||||||
|
StreamServerPort: "10010",
|
||||||
|
EnableSelinux: false,
|
||||||
|
SandboxImage: "gcr.io/google_containers/pause:3.0",
|
||||||
|
StatsCollectPeriod: 10,
|
||||||
|
SystemdCgroup: false,
|
||||||
|
EnableIPv6DAD: false,
|
||||||
|
Registry: Registry{
|
||||||
|
Mirrors: map[string]Mirror{
|
||||||
|
"docker.io": {
|
||||||
|
Endpoints: []string{"https://registry-1.docker.io"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
8
vendor/github.com/containerd/cri-containerd/pkg/containerd/importer/importer.go
generated
vendored
8
vendor/github.com/containerd/cri-containerd/pkg/containerd/importer/importer.go
generated
vendored
@ -36,6 +36,7 @@ import (
|
|||||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
ctrdutil "github.com/containerd/cri-containerd/pkg/containerd/util"
|
||||||
"github.com/containerd/cri-containerd/pkg/util"
|
"github.com/containerd/cri-containerd/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -80,6 +81,7 @@ func Import(ctx context.Context, client *containerd.Client, reader io.Reader) (_
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// TODO(random-liu): Fix this after containerd client is fixed (containerd/containerd#2193)
|
||||||
defer done() // nolint: errcheck
|
defer done() // nolint: errcheck
|
||||||
|
|
||||||
cs := client.ContentStore()
|
cs := client.ContentStore()
|
||||||
@ -134,9 +136,13 @@ func Import(ctx context.Context, client *containerd.Client, reader io.Reader) (_
|
|||||||
// TODO(random-liu): Consider whether we should keep images already imported
|
// TODO(random-liu): Consider whether we should keep images already imported
|
||||||
// even when there is an error.
|
// even when there is an error.
|
||||||
for _, ref := range refs {
|
for _, ref := range refs {
|
||||||
if err := is.Delete(ctx, ref); err != nil {
|
func() {
|
||||||
|
deferCtx, deferCancel := ctrdutil.DeferContext()
|
||||||
|
defer deferCancel()
|
||||||
|
if err := is.Delete(deferCtx, ref); err != nil {
|
||||||
log.G(ctx).WithError(err).Errorf("Failed to remove image %q", ref)
|
log.G(ctx).WithError(err).Errorf("Failed to remove image %q", ref)
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
for _, mfst := range mfsts {
|
for _, mfst := range mfsts {
|
||||||
|
2
vendor/github.com/containerd/cri-containerd/pkg/containerd/resolver/resolver.go
generated
vendored
2
vendor/github.com/containerd/cri-containerd/pkg/containerd/resolver/resolver.go
generated
vendored
@ -501,7 +501,7 @@ func (r *dockerBase) fetchTokenWithOAuth(ctx context.Context, to tokenOptions) (
|
|||||||
|
|
||||||
// Registries without support for POST may return 404 for POST /v2/token.
|
// Registries without support for POST may return 404 for POST /v2/token.
|
||||||
// As of September 2017, GCR is known to return 404.
|
// As of September 2017, GCR is known to return 404.
|
||||||
if (resp.StatusCode == 405 && r.username != "") || resp.StatusCode == 404 {
|
if (resp.StatusCode == 405 && r.username != "") || resp.StatusCode == 404 || resp.StatusCode == 401 {
|
||||||
return r.getToken(ctx, to)
|
return r.getToken(ctx, to)
|
||||||
} else if resp.StatusCode < 200 || resp.StatusCode >= 400 {
|
} else if resp.StatusCode < 200 || resp.StatusCode >= 400 {
|
||||||
b, _ := ioutil.ReadAll(io.LimitReader(resp.Body, 64000)) // 64KB
|
b, _ := ioutil.ReadAll(io.LimitReader(resp.Body, 64000)) // 64KB
|
||||||
|
35
vendor/github.com/containerd/cri-containerd/pkg/containerd/util/util.go
generated
vendored
Normal file
35
vendor/github.com/containerd/cri-containerd/pkg/containerd/util/util.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 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 util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// deferCleanupTimeout is the default timeout for containerd cleanup operations
|
||||||
|
// in defer.
|
||||||
|
const deferCleanupTimeout = 1 * time.Minute
|
||||||
|
|
||||||
|
// DeferContext returns a context for containerd cleanup operations in defer.
|
||||||
|
// A default timeout is applied to avoid cleanup operation pending forever.
|
||||||
|
// TODO(random-liu): Add namespace after local services are used.
|
||||||
|
// (containerd/containerd#2183)
|
||||||
|
func DeferContext() (context.Context, context.CancelFunc) {
|
||||||
|
return context.WithTimeout(context.Background(), deferCleanupTimeout)
|
||||||
|
}
|
5
vendor/github.com/containerd/cri-containerd/pkg/server/container_create.go
generated
vendored
5
vendor/github.com/containerd/cri-containerd/pkg/server/container_create.go
generated
vendored
@ -47,6 +47,7 @@ import (
|
|||||||
|
|
||||||
"github.com/containerd/cri-containerd/pkg/annotations"
|
"github.com/containerd/cri-containerd/pkg/annotations"
|
||||||
customopts "github.com/containerd/cri-containerd/pkg/containerd/opts"
|
customopts "github.com/containerd/cri-containerd/pkg/containerd/opts"
|
||||||
|
ctrdutil "github.com/containerd/cri-containerd/pkg/containerd/util"
|
||||||
cio "github.com/containerd/cri-containerd/pkg/server/io"
|
cio "github.com/containerd/cri-containerd/pkg/server/io"
|
||||||
containerstore "github.com/containerd/cri-containerd/pkg/store/container"
|
containerstore "github.com/containerd/cri-containerd/pkg/store/container"
|
||||||
"github.com/containerd/cri-containerd/pkg/util"
|
"github.com/containerd/cri-containerd/pkg/util"
|
||||||
@ -240,7 +241,9 @@ func (c *criContainerdService) CreateContainer(ctx context.Context, r *runtime.C
|
|||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if retErr != nil {
|
if retErr != nil {
|
||||||
if err := cntr.Delete(ctx, containerd.WithSnapshotCleanup); err != nil {
|
deferCtx, deferCancel := ctrdutil.DeferContext()
|
||||||
|
defer deferCancel()
|
||||||
|
if err := cntr.Delete(deferCtx, containerd.WithSnapshotCleanup); err != nil {
|
||||||
logrus.WithError(err).Errorf("Failed to delete containerd container %q", id)
|
logrus.WithError(err).Errorf("Failed to delete containerd container %q", id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
5
vendor/github.com/containerd/cri-containerd/pkg/server/container_execsync.go
generated
vendored
5
vendor/github.com/containerd/cri-containerd/pkg/server/container_execsync.go
generated
vendored
@ -31,6 +31,7 @@ import (
|
|||||||
"k8s.io/client-go/tools/remotecommand"
|
"k8s.io/client-go/tools/remotecommand"
|
||||||
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||||
|
|
||||||
|
ctrdutil "github.com/containerd/cri-containerd/pkg/containerd/util"
|
||||||
cioutil "github.com/containerd/cri-containerd/pkg/ioutil"
|
cioutil "github.com/containerd/cri-containerd/pkg/ioutil"
|
||||||
cio "github.com/containerd/cri-containerd/pkg/server/io"
|
cio "github.com/containerd/cri-containerd/pkg/server/io"
|
||||||
"github.com/containerd/cri-containerd/pkg/util"
|
"github.com/containerd/cri-containerd/pkg/util"
|
||||||
@ -128,7 +129,9 @@ func (c *criContainerdService) execInContainer(ctx context.Context, id string, o
|
|||||||
return nil, fmt.Errorf("failed to create exec %q: %v", execID, err)
|
return nil, fmt.Errorf("failed to create exec %q: %v", execID, err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if _, err := process.Delete(ctx); err != nil {
|
deferCtx, deferCancel := ctrdutil.DeferContext()
|
||||||
|
defer deferCancel()
|
||||||
|
if _, err := process.Delete(deferCtx); err != nil {
|
||||||
logrus.WithError(err).Errorf("Failed to delete exec process %q for container %q", execID, id)
|
logrus.WithError(err).Errorf("Failed to delete exec process %q for container %q", execID, id)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
5
vendor/github.com/containerd/cri-containerd/pkg/server/container_start.go
generated
vendored
5
vendor/github.com/containerd/cri-containerd/pkg/server/container_start.go
generated
vendored
@ -28,6 +28,7 @@ import (
|
|||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||||
|
|
||||||
|
ctrdutil "github.com/containerd/cri-containerd/pkg/containerd/util"
|
||||||
cio "github.com/containerd/cri-containerd/pkg/server/io"
|
cio "github.com/containerd/cri-containerd/pkg/server/io"
|
||||||
containerstore "github.com/containerd/cri-containerd/pkg/store/container"
|
containerstore "github.com/containerd/cri-containerd/pkg/store/container"
|
||||||
sandboxstore "github.com/containerd/cri-containerd/pkg/store/sandbox"
|
sandboxstore "github.com/containerd/cri-containerd/pkg/store/sandbox"
|
||||||
@ -121,8 +122,10 @@ func (c *criContainerdService) startContainer(ctx context.Context,
|
|||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if retErr != nil {
|
if retErr != nil {
|
||||||
|
deferCtx, deferCancel := ctrdutil.DeferContext()
|
||||||
|
defer deferCancel()
|
||||||
// It's possible that task is deleted by event monitor.
|
// It's possible that task is deleted by event monitor.
|
||||||
if _, err := task.Delete(ctx, containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) {
|
if _, err := task.Delete(deferCtx, containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) {
|
||||||
logrus.WithError(err).Errorf("Failed to delete containerd task %q", id)
|
logrus.WithError(err).Errorf("Failed to delete containerd task %q", id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
5
vendor/github.com/containerd/cri-containerd/pkg/server/container_update_resources.go
generated
vendored
5
vendor/github.com/containerd/cri-containerd/pkg/server/container_update_resources.go
generated
vendored
@ -29,6 +29,7 @@ import (
|
|||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||||
|
|
||||||
|
ctrdutil "github.com/containerd/cri-containerd/pkg/containerd/util"
|
||||||
containerstore "github.com/containerd/cri-containerd/pkg/store/container"
|
containerstore "github.com/containerd/cri-containerd/pkg/store/container"
|
||||||
"github.com/containerd/cri-containerd/pkg/util"
|
"github.com/containerd/cri-containerd/pkg/util"
|
||||||
)
|
)
|
||||||
@ -78,8 +79,10 @@ func (c *criContainerdService) updateContainerResources(ctx context.Context,
|
|||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if retErr != nil {
|
if retErr != nil {
|
||||||
|
deferCtx, deferCancel := ctrdutil.DeferContext()
|
||||||
|
defer deferCancel()
|
||||||
// Reset spec on error.
|
// Reset spec on error.
|
||||||
if err := updateContainerSpec(ctx, cntr.Container, oldSpec); err != nil {
|
if err := updateContainerSpec(deferCtx, cntr.Container, oldSpec); err != nil {
|
||||||
logrus.WithError(err).Errorf("Failed to update spec %+v for container %q", oldSpec, id)
|
logrus.WithError(err).Errorf("Failed to update spec %+v for container %q", oldSpec, id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3
vendor/github.com/containerd/cri-containerd/pkg/server/events.go
generated
vendored
3
vendor/github.com/containerd/cri-containerd/pkg/server/events.go
generated
vendored
@ -21,9 +21,9 @@ import (
|
|||||||
|
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
eventtypes "github.com/containerd/containerd/api/events"
|
eventtypes "github.com/containerd/containerd/api/events"
|
||||||
"github.com/containerd/containerd/api/services/events/v1"
|
|
||||||
containerdio "github.com/containerd/containerd/cio"
|
containerdio "github.com/containerd/containerd/cio"
|
||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
|
"github.com/containerd/containerd/events"
|
||||||
"github.com/containerd/typeurl"
|
"github.com/containerd/typeurl"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
@ -137,6 +137,7 @@ func (em *eventMonitor) handleEvent(evt *events.Envelope) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
logrus.WithError(err).Errorf("Failed to get container %q", e.ContainerID)
|
logrus.WithError(err).Errorf("Failed to get container %q", e.ContainerID)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
err = cntr.Status.UpdateSync(func(status containerstore.Status) (containerstore.Status, error) {
|
err = cntr.Status.UpdateSync(func(status containerstore.Status) (containerstore.Status, error) {
|
||||||
status.Reason = oomExitReason
|
status.Reason = oomExitReason
|
||||||
|
9
vendor/github.com/containerd/cri-containerd/pkg/server/sandbox_run.go
generated
vendored
9
vendor/github.com/containerd/cri-containerd/pkg/server/sandbox_run.go
generated
vendored
@ -37,6 +37,7 @@ import (
|
|||||||
|
|
||||||
"github.com/containerd/cri-containerd/pkg/annotations"
|
"github.com/containerd/cri-containerd/pkg/annotations"
|
||||||
customopts "github.com/containerd/cri-containerd/pkg/containerd/opts"
|
customopts "github.com/containerd/cri-containerd/pkg/containerd/opts"
|
||||||
|
ctrdutil "github.com/containerd/cri-containerd/pkg/containerd/util"
|
||||||
"github.com/containerd/cri-containerd/pkg/log"
|
"github.com/containerd/cri-containerd/pkg/log"
|
||||||
sandboxstore "github.com/containerd/cri-containerd/pkg/store/sandbox"
|
sandboxstore "github.com/containerd/cri-containerd/pkg/store/sandbox"
|
||||||
"github.com/containerd/cri-containerd/pkg/util"
|
"github.com/containerd/cri-containerd/pkg/util"
|
||||||
@ -193,7 +194,9 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run
|
|||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if retErr != nil {
|
if retErr != nil {
|
||||||
if err := container.Delete(ctx, containerd.WithSnapshotCleanup); err != nil {
|
deferCtx, deferCancel := ctrdutil.DeferContext()
|
||||||
|
defer deferCancel()
|
||||||
|
if err := container.Delete(deferCtx, containerd.WithSnapshotCleanup); err != nil {
|
||||||
logrus.WithError(err).Errorf("Failed to delete containerd container %q", id)
|
logrus.WithError(err).Errorf("Failed to delete containerd container %q", id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -288,9 +291,11 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run
|
|||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if retErr != nil {
|
if retErr != nil {
|
||||||
|
deferCtx, deferCancel := ctrdutil.DeferContext()
|
||||||
|
defer deferCancel()
|
||||||
// Cleanup the sandbox container if an error is returned.
|
// Cleanup the sandbox container if an error is returned.
|
||||||
// It's possible that task is deleted by event monitor.
|
// It's possible that task is deleted by event monitor.
|
||||||
if _, err := task.Delete(ctx, containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) {
|
if _, err := task.Delete(deferCtx, containerd.WithProcessKill); err != nil && !errdefs.IsNotFound(err) {
|
||||||
logrus.WithError(err).Errorf("Failed to delete sandbox container %q", id)
|
logrus.WithError(err).Errorf("Failed to delete sandbox container %q", id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
65
vendor/github.com/containerd/cri-containerd/pkg/server/service.go
generated
vendored
65
vendor/github.com/containerd/cri-containerd/pkg/server/service.go
generated
vendored
@ -19,10 +19,7 @@ package server
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
@ -37,9 +34,9 @@ import (
|
|||||||
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/server/streaming"
|
"k8s.io/kubernetes/pkg/kubelet/server/streaming"
|
||||||
|
|
||||||
"github.com/containerd/cri-containerd/cmd/cri-containerd/options"
|
|
||||||
api "github.com/containerd/cri-containerd/pkg/api/v1"
|
api "github.com/containerd/cri-containerd/pkg/api/v1"
|
||||||
"github.com/containerd/cri-containerd/pkg/atomic"
|
"github.com/containerd/cri-containerd/pkg/atomic"
|
||||||
|
criconfig "github.com/containerd/cri-containerd/pkg/config"
|
||||||
osinterface "github.com/containerd/cri-containerd/pkg/os"
|
osinterface "github.com/containerd/cri-containerd/pkg/os"
|
||||||
"github.com/containerd/cri-containerd/pkg/registrar"
|
"github.com/containerd/cri-containerd/pkg/registrar"
|
||||||
containerstore "github.com/containerd/cri-containerd/pkg/store/container"
|
containerstore "github.com/containerd/cri-containerd/pkg/store/container"
|
||||||
@ -48,12 +45,8 @@ import (
|
|||||||
snapshotstore "github.com/containerd/cri-containerd/pkg/store/snapshot"
|
snapshotstore "github.com/containerd/cri-containerd/pkg/store/snapshot"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// k8sContainerdNamespace is the namespace we use to connect containerd.
|
||||||
// k8sContainerdNamespace is the namespace we use to connect containerd.
|
const k8sContainerdNamespace = "k8s.io"
|
||||||
k8sContainerdNamespace = "k8s.io"
|
|
||||||
// unixProtocol is the network protocol of unix socket.
|
|
||||||
unixProtocol = "unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
// grpcServices are all the grpc services provided by cri containerd.
|
// grpcServices are all the grpc services provided by cri containerd.
|
||||||
type grpcServices interface {
|
type grpcServices interface {
|
||||||
@ -64,7 +57,7 @@ type grpcServices interface {
|
|||||||
|
|
||||||
// CRIContainerdService is the interface implement CRI remote service server.
|
// CRIContainerdService is the interface implement CRI remote service server.
|
||||||
type CRIContainerdService interface {
|
type CRIContainerdService interface {
|
||||||
Run(bool) error
|
Run() error
|
||||||
// io.Closer is used by containerd to gracefully stop cri service.
|
// io.Closer is used by containerd to gracefully stop cri service.
|
||||||
io.Closer
|
io.Closer
|
||||||
plugin.Service
|
plugin.Service
|
||||||
@ -74,15 +67,13 @@ type CRIContainerdService interface {
|
|||||||
// criContainerdService implements CRIContainerdService.
|
// criContainerdService implements CRIContainerdService.
|
||||||
type criContainerdService struct {
|
type criContainerdService struct {
|
||||||
// config contains all configurations.
|
// config contains all configurations.
|
||||||
config options.Config
|
config criconfig.Config
|
||||||
// imageFSPath is the path to image filesystem.
|
// imageFSPath is the path to image filesystem.
|
||||||
imageFSPath string
|
imageFSPath string
|
||||||
// apparmorEnabled indicates whether apparmor is enabled.
|
// apparmorEnabled indicates whether apparmor is enabled.
|
||||||
apparmorEnabled bool
|
apparmorEnabled bool
|
||||||
// seccompEnabled indicates whether seccomp is enabled.
|
// seccompEnabled indicates whether seccomp is enabled.
|
||||||
seccompEnabled bool
|
seccompEnabled bool
|
||||||
// server is the grpc server.
|
|
||||||
server *grpc.Server
|
|
||||||
// os is an interface for all required os operations.
|
// os is an interface for all required os operations.
|
||||||
os osinterface.OS
|
os osinterface.OS
|
||||||
// sandboxStore stores all resources associated with sandboxes.
|
// sandboxStore stores all resources associated with sandboxes.
|
||||||
@ -113,7 +104,7 @@ type criContainerdService struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewCRIContainerdService returns a new instance of CRIContainerdService
|
// NewCRIContainerdService returns a new instance of CRIContainerdService
|
||||||
func NewCRIContainerdService(config options.Config) (CRIContainerdService, error) {
|
func NewCRIContainerdService(config criconfig.Config) (CRIContainerdService, error) {
|
||||||
var err error
|
var err error
|
||||||
c := &criContainerdService{
|
c := &criContainerdService{
|
||||||
config: config,
|
config: config,
|
||||||
@ -153,12 +144,6 @@ func NewCRIContainerdService(config options.Config) (CRIContainerdService, error
|
|||||||
|
|
||||||
c.eventMonitor = newEventMonitor(c.containerStore, c.sandboxStore)
|
c.eventMonitor = newEventMonitor(c.containerStore, c.sandboxStore)
|
||||||
|
|
||||||
// To avoid race condition between `Run` and `Stop`, still create grpc server
|
|
||||||
// although we may not use it. It's just a small in-memory data structure.
|
|
||||||
// TODO(random-liu): Get rid of the grpc server when completely switch
|
|
||||||
// to plugin mode.
|
|
||||||
c.server = grpc.NewServer()
|
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,11 +157,8 @@ func (c *criContainerdService) Register(s *grpc.Server) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run starts the cri-containerd service. startGRPC specifies
|
// Run starts the cri-containerd service.
|
||||||
// whether to start grpc server in this function.
|
func (c *criContainerdService) Run() error {
|
||||||
// TODO(random-liu): Remove `startRPC=true` case when we no longer support cri-containerd
|
|
||||||
// standalone mode.
|
|
||||||
func (c *criContainerdService) Run(startGRPC bool) error {
|
|
||||||
logrus.Info("Start cri-containerd service")
|
logrus.Info("Start cri-containerd service")
|
||||||
|
|
||||||
// Connect containerd service here, to get rid of the containerd dependency
|
// Connect containerd service here, to get rid of the containerd dependency
|
||||||
@ -226,35 +208,10 @@ func (c *criContainerdService) Run(startGRPC bool) error {
|
|||||||
// Set the server as initialized. GRPC services could start serving traffic.
|
// Set the server as initialized. GRPC services could start serving traffic.
|
||||||
c.initialized.Set()
|
c.initialized.Set()
|
||||||
|
|
||||||
grpcServerCloseCh := make(chan struct{})
|
|
||||||
if startGRPC {
|
|
||||||
// Create the grpc server and register runtime and image services.
|
|
||||||
c.Register(c.server) // nolint: errcheck
|
|
||||||
// Start grpc server.
|
|
||||||
// Unlink to cleanup the previous socket file.
|
|
||||||
logrus.Info("Start grpc server")
|
|
||||||
err := syscall.Unlink(c.config.SocketPath)
|
|
||||||
if err != nil && !os.IsNotExist(err) {
|
|
||||||
return fmt.Errorf("failed to unlink socket file %q: %v", c.config.SocketPath, err)
|
|
||||||
}
|
|
||||||
l, err := net.Listen(unixProtocol, c.config.SocketPath)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to listen on %q: %v", c.config.SocketPath, err)
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
if err := c.server.Serve(l); err != nil {
|
|
||||||
logrus.WithError(err).Error("Failed to serve grpc request")
|
|
||||||
}
|
|
||||||
close(grpcServerCloseCh)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
// Keep grpcServerCloseCh open if grpc server is not started.
|
|
||||||
|
|
||||||
// Stop the whole cri-containerd service if any of the critical service exits.
|
// Stop the whole cri-containerd service if any of the critical service exits.
|
||||||
select {
|
select {
|
||||||
case <-eventMonitorCloseCh:
|
case <-eventMonitorCloseCh:
|
||||||
case <-streamServerCloseCh:
|
case <-streamServerCloseCh:
|
||||||
case <-grpcServerCloseCh:
|
|
||||||
}
|
}
|
||||||
if err := c.Close(); err != nil {
|
if err := c.Close(); err != nil {
|
||||||
return fmt.Errorf("failed to stop cri service: %v", err)
|
return fmt.Errorf("failed to stop cri service: %v", err)
|
||||||
@ -277,11 +234,6 @@ func (c *criContainerdService) Run(startGRPC bool) error {
|
|||||||
case <-time.After(streamServerStopTimeout):
|
case <-time.After(streamServerStopTimeout):
|
||||||
logrus.Errorf("Stream server is not stopped in %q", streamServerStopTimeout)
|
logrus.Errorf("Stream server is not stopped in %q", streamServerStopTimeout)
|
||||||
}
|
}
|
||||||
if startGRPC {
|
|
||||||
// Only wait for grpc server close channel when grpc server is started.
|
|
||||||
<-grpcServerCloseCh
|
|
||||||
logrus.Info("GRPC server stopped")
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,7 +245,6 @@ func (c *criContainerdService) Close() error {
|
|||||||
if err := c.streamServer.Stop(); err != nil {
|
if err := c.streamServer.Stop(); err != nil {
|
||||||
return fmt.Errorf("failed to stop stream server: %v", err)
|
return fmt.Errorf("failed to stop stream server: %v", err)
|
||||||
}
|
}
|
||||||
c.server.Stop()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
24
vendor/github.com/containerd/cri-containerd/pkg/server/version.go
generated
vendored
24
vendor/github.com/containerd/cri-containerd/pkg/server/version.go
generated
vendored
@ -17,32 +17,30 @@ limitations under the License.
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||||
|
|
||||||
"github.com/containerd/cri-containerd/pkg/version"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// For now, containerd and runc are bundled with cri-containerd, cri-containerd
|
containerName = "containerd"
|
||||||
// version is more important to us.
|
|
||||||
// TODO(random-liu): Figure out how to package cri-containerd and containerd,
|
|
||||||
// and how to version it. We still prefer calling the container runtime "containerd",
|
|
||||||
// but we care both the cri-containerd version and containerd version.
|
|
||||||
containerName = "cri-containerd"
|
|
||||||
containerdAPIVersion = "0.0.0"
|
|
||||||
// kubeAPIVersion is the api version of kubernetes.
|
// kubeAPIVersion is the api version of kubernetes.
|
||||||
|
// TODO(random-liu): Change this to actual CRI version.
|
||||||
kubeAPIVersion = "0.1.0"
|
kubeAPIVersion = "0.1.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version returns the runtime name, runtime version and runtime API version.
|
// Version returns the runtime name, runtime version and runtime API version.
|
||||||
// TODO(random-liu): Return containerd version since we are going to merge 2 daemons.
|
|
||||||
func (c *criContainerdService) Version(ctx context.Context, r *runtime.VersionRequest) (*runtime.VersionResponse, error) {
|
func (c *criContainerdService) Version(ctx context.Context, r *runtime.VersionRequest) (*runtime.VersionResponse, error) {
|
||||||
|
resp, err := c.client.Version(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get containerd version: %v", err)
|
||||||
|
}
|
||||||
return &runtime.VersionResponse{
|
return &runtime.VersionResponse{
|
||||||
Version: kubeAPIVersion,
|
Version: kubeAPIVersion,
|
||||||
RuntimeName: containerName,
|
RuntimeName: containerName,
|
||||||
RuntimeVersion: version.CRIContainerdVersion,
|
RuntimeVersion: resp.Version,
|
||||||
// Containerd doesn't have an api version now.
|
// Containerd doesn't have an api version use version instead.
|
||||||
RuntimeApiVersion: containerdAPIVersion,
|
RuntimeApiVersion: resp.Version,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
16
vendor/github.com/containerd/cri-containerd/pkg/store/util.go
generated
vendored
16
vendor/github.com/containerd/cri-containerd/pkg/store/util.go
generated
vendored
@ -20,28 +20,20 @@ import "sync"
|
|||||||
|
|
||||||
// StopCh is used to propagate the stop information of a container.
|
// StopCh is used to propagate the stop information of a container.
|
||||||
type StopCh struct {
|
type StopCh struct {
|
||||||
mu sync.Mutex
|
|
||||||
ch chan struct{}
|
ch chan struct{}
|
||||||
closed bool
|
once sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStopCh creates a stop channel. The channel is open by default.
|
// NewStopCh creates a stop channel. The channel is open by default.
|
||||||
func NewStopCh() *StopCh {
|
func NewStopCh() *StopCh {
|
||||||
return &StopCh{
|
return &StopCh{ch: make(chan struct{})}
|
||||||
ch: make(chan struct{}),
|
|
||||||
closed: false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop close stopCh of the container.
|
// Stop close stopCh of the container.
|
||||||
func (s *StopCh) Stop() {
|
func (s *StopCh) Stop() {
|
||||||
s.mu.Lock()
|
s.once.Do(func() {
|
||||||
defer s.mu.Unlock()
|
|
||||||
if s.closed {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
close(s.ch)
|
close(s.ch)
|
||||||
s.closed = true
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stopped return the stopCh of the container as a readonly channel.
|
// Stopped return the stopCh of the container as a readonly channel.
|
||||||
|
43
vendor/github.com/containerd/cri-containerd/pkg/version/version.go
generated
vendored
43
vendor/github.com/containerd/cri-containerd/pkg/version/version.go
generated
vendored
@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes 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 version
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/blang/semver"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CRIContainerdVersion is the version of the cri-containerd.
|
|
||||||
var CRIContainerdVersion = "UNKNOWN"
|
|
||||||
|
|
||||||
func validateSemver(sv string) error {
|
|
||||||
_, err := semver.Parse(sv)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("couldn't parse cri-containerd version %q: %v", sv, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrintVersion outputs the release version of cri-containerd
|
|
||||||
func PrintVersion() {
|
|
||||||
err := validateSemver(CRIContainerdVersion)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
fmt.Println(CRIContainerdVersion)
|
|
||||||
}
|
|
2
vendor/github.com/containerd/cri-containerd/vendor.conf
generated
vendored
2
vendor/github.com/containerd/cri-containerd/vendor.conf
generated
vendored
@ -4,7 +4,7 @@ github.com/boltdb/bolt e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd
|
|||||||
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
|
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
|
||||||
github.com/containerd/cgroups c0710c92e8b3a44681d1321dcfd1360fc5c6c089
|
github.com/containerd/cgroups c0710c92e8b3a44681d1321dcfd1360fc5c6c089
|
||||||
github.com/containerd/console 84eeaae905fa414d03e07bcd6c8d3f19e7cf180e
|
github.com/containerd/console 84eeaae905fa414d03e07bcd6c8d3f19e7cf180e
|
||||||
github.com/containerd/containerd 129167132c5e0dbd1b031badae201a432d1bd681
|
github.com/containerd/containerd 25c403415aa99d0f3a609043429f3d24c8b70c0c
|
||||||
github.com/containerd/continuity d8fb8589b0e8e85b8c8bbaa8840226d0dfeb7371
|
github.com/containerd/continuity d8fb8589b0e8e85b8c8bbaa8840226d0dfeb7371
|
||||||
github.com/containerd/fifo fbfb6a11ec671efbe94ad1c12c2e98773f19e1e6
|
github.com/containerd/fifo fbfb6a11ec671efbe94ad1c12c2e98773f19e1e6
|
||||||
github.com/containerd/go-runc 4f6e87ae043f859a38255247b49c9abc262d002f
|
github.com/containerd/go-runc 4f6e87ae043f859a38255247b49c9abc262d002f
|
||||||
|
Loading…
Reference in New Issue
Block a user