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" 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.GRPCPlugin, ID: "images", 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 NewService(metadata.NewImageStore(m.(*metadata.DB)), ic.Events, g.(gcScheduler)), nil }, }) } type gcScheduler interface { ScheduleAndWait(gocontext.Context) (gc.Stats, error) } type service struct { store images.Store gc gcScheduler publisher events.Publisher } // NewService returns the GRPC image server 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 { imagesapi.RegisterImagesServer(server, s) return nil } func (s *service) Get(ctx context.Context, req *imagesapi.GetImageRequest) (*imagesapi.GetImageResponse, error) { image, err := s.store.Get(ctx, req.Name) 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) { images, err := s.store.List(ctx, req.Filters...) 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) { 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 := 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) { 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 := 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) { log.G(ctx).WithField("name", req.Name).Debugf("delete image") 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 }