github.com/containerd/Containerd@v1.4.13/services/containers/local.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package containers 18 19 import ( 20 "context" 21 "io" 22 23 eventstypes "github.com/containerd/containerd/api/events" 24 api "github.com/containerd/containerd/api/services/containers/v1" 25 "github.com/containerd/containerd/containers" 26 "github.com/containerd/containerd/errdefs" 27 "github.com/containerd/containerd/events" 28 "github.com/containerd/containerd/metadata" 29 "github.com/containerd/containerd/plugin" 30 "github.com/containerd/containerd/services" 31 ptypes "github.com/gogo/protobuf/types" 32 bolt "go.etcd.io/bbolt" 33 "google.golang.org/grpc" 34 "google.golang.org/grpc/codes" 35 grpcm "google.golang.org/grpc/metadata" 36 "google.golang.org/grpc/status" 37 ) 38 39 func init() { 40 plugin.Register(&plugin.Registration{ 41 Type: plugin.ServicePlugin, 42 ID: services.ContainersService, 43 Requires: []plugin.Type{ 44 plugin.MetadataPlugin, 45 }, 46 InitFn: func(ic *plugin.InitContext) (interface{}, error) { 47 m, err := ic.Get(plugin.MetadataPlugin) 48 if err != nil { 49 return nil, err 50 } 51 52 db := m.(*metadata.DB) 53 return &local{ 54 Store: metadata.NewContainerStore(db), 55 db: db, 56 publisher: ic.Events, 57 }, nil 58 }, 59 }) 60 } 61 62 type local struct { 63 containers.Store 64 db *metadata.DB 65 publisher events.Publisher 66 } 67 68 var _ api.ContainersClient = &local{} 69 70 func (l *local) Get(ctx context.Context, req *api.GetContainerRequest, _ ...grpc.CallOption) (*api.GetContainerResponse, error) { 71 var resp api.GetContainerResponse 72 73 return &resp, errdefs.ToGRPC(l.withStoreView(ctx, func(ctx context.Context) error { 74 container, err := l.Store.Get(ctx, req.ID) 75 if err != nil { 76 return err 77 } 78 containerpb := containerToProto(&container) 79 resp.Container = containerpb 80 81 return nil 82 })) 83 } 84 85 func (l *local) List(ctx context.Context, req *api.ListContainersRequest, _ ...grpc.CallOption) (*api.ListContainersResponse, error) { 86 var resp api.ListContainersResponse 87 return &resp, errdefs.ToGRPC(l.withStoreView(ctx, func(ctx context.Context) error { 88 containers, err := l.Store.List(ctx, req.Filters...) 89 if err != nil { 90 return err 91 } 92 resp.Containers = containersToProto(containers) 93 return nil 94 })) 95 } 96 97 func (l *local) ListStream(ctx context.Context, req *api.ListContainersRequest, _ ...grpc.CallOption) (api.Containers_ListStreamClient, error) { 98 stream := &localStream{ 99 ctx: ctx, 100 } 101 return stream, errdefs.ToGRPC(l.withStoreView(ctx, func(ctx context.Context) error { 102 containers, err := l.Store.List(ctx, req.Filters...) 103 if err != nil { 104 return err 105 } 106 stream.containers = containersToProto(containers) 107 return nil 108 })) 109 } 110 111 func (l *local) Create(ctx context.Context, req *api.CreateContainerRequest, _ ...grpc.CallOption) (*api.CreateContainerResponse, error) { 112 var resp api.CreateContainerResponse 113 114 if err := l.withStoreUpdate(ctx, func(ctx context.Context) error { 115 container := containerFromProto(&req.Container) 116 117 created, err := l.Store.Create(ctx, container) 118 if err != nil { 119 return err 120 } 121 122 resp.Container = containerToProto(&created) 123 124 return nil 125 }); err != nil { 126 return &resp, errdefs.ToGRPC(err) 127 } 128 if err := l.publisher.Publish(ctx, "/containers/create", &eventstypes.ContainerCreate{ 129 ID: resp.Container.ID, 130 Image: resp.Container.Image, 131 Runtime: &eventstypes.ContainerCreate_Runtime{ 132 Name: resp.Container.Runtime.Name, 133 Options: resp.Container.Runtime.Options, 134 }, 135 }); err != nil { 136 return &resp, err 137 } 138 139 return &resp, nil 140 } 141 142 func (l *local) Update(ctx context.Context, req *api.UpdateContainerRequest, _ ...grpc.CallOption) (*api.UpdateContainerResponse, error) { 143 if req.Container.ID == "" { 144 return nil, status.Errorf(codes.InvalidArgument, "Container.ID required") 145 } 146 var ( 147 resp api.UpdateContainerResponse 148 container = containerFromProto(&req.Container) 149 ) 150 151 if err := l.withStoreUpdate(ctx, func(ctx context.Context) error { 152 var fieldpaths []string 153 if req.UpdateMask != nil && len(req.UpdateMask.Paths) > 0 { 154 fieldpaths = append(fieldpaths, req.UpdateMask.Paths...) 155 } 156 157 updated, err := l.Store.Update(ctx, container, fieldpaths...) 158 if err != nil { 159 return err 160 } 161 162 resp.Container = containerToProto(&updated) 163 return nil 164 }); err != nil { 165 return &resp, errdefs.ToGRPC(err) 166 } 167 168 if err := l.publisher.Publish(ctx, "/containers/update", &eventstypes.ContainerUpdate{ 169 ID: resp.Container.ID, 170 Image: resp.Container.Image, 171 Labels: resp.Container.Labels, 172 SnapshotKey: resp.Container.SnapshotKey, 173 }); err != nil { 174 return &resp, err 175 } 176 177 return &resp, nil 178 } 179 180 func (l *local) Delete(ctx context.Context, req *api.DeleteContainerRequest, _ ...grpc.CallOption) (*ptypes.Empty, error) { 181 if err := l.withStoreUpdate(ctx, func(ctx context.Context) error { 182 return l.Store.Delete(ctx, req.ID) 183 }); err != nil { 184 return &ptypes.Empty{}, errdefs.ToGRPC(err) 185 } 186 187 if err := l.publisher.Publish(ctx, "/containers/delete", &eventstypes.ContainerDelete{ 188 ID: req.ID, 189 }); err != nil { 190 return &ptypes.Empty{}, err 191 } 192 193 return &ptypes.Empty{}, nil 194 } 195 196 func (l *local) withStore(ctx context.Context, fn func(ctx context.Context) error) func(tx *bolt.Tx) error { 197 return func(tx *bolt.Tx) error { 198 return fn(metadata.WithTransactionContext(ctx, tx)) 199 } 200 } 201 202 func (l *local) withStoreView(ctx context.Context, fn func(ctx context.Context) error) error { 203 return l.db.View(l.withStore(ctx, fn)) 204 } 205 206 func (l *local) withStoreUpdate(ctx context.Context, fn func(ctx context.Context) error) error { 207 return l.db.Update(l.withStore(ctx, fn)) 208 } 209 210 type localStream struct { 211 ctx context.Context 212 containers []api.Container 213 i int 214 } 215 216 func (s *localStream) Recv() (*api.ListContainerMessage, error) { 217 if s.i >= len(s.containers) { 218 return nil, io.EOF 219 } 220 c := s.containers[s.i] 221 s.i++ 222 return &api.ListContainerMessage{ 223 Container: &c, 224 }, nil 225 } 226 227 func (s *localStream) Context() context.Context { 228 return s.ctx 229 } 230 231 func (s *localStream) CloseSend() error { 232 return nil 233 } 234 235 func (s *localStream) Header() (grpcm.MD, error) { 236 return nil, nil 237 } 238 239 func (s *localStream) Trailer() grpcm.MD { 240 return nil 241 } 242 243 func (s *localStream) SendMsg(m interface{}) error { 244 return nil 245 } 246 247 func (s *localStream) RecvMsg(m interface{}) error { 248 return nil 249 }