github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/metadata/containers.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 metadata 18 19 import ( 20 "context" 21 "strings" 22 "sync/atomic" 23 "time" 24 25 "github.com/containerd/containerd/containers" 26 "github.com/containerd/containerd/errdefs" 27 "github.com/containerd/containerd/filters" 28 "github.com/containerd/containerd/identifiers" 29 "github.com/containerd/containerd/labels" 30 "github.com/containerd/containerd/metadata/boltutil" 31 "github.com/containerd/containerd/namespaces" 32 "github.com/gogo/protobuf/proto" 33 "github.com/gogo/protobuf/types" 34 "github.com/pkg/errors" 35 bolt "go.etcd.io/bbolt" 36 ) 37 38 type containerStore struct { 39 db *DB 40 } 41 42 // NewContainerStore returns a Store backed by an underlying bolt DB 43 func NewContainerStore(db *DB) containers.Store { 44 return &containerStore{ 45 db: db, 46 } 47 } 48 49 func (s *containerStore) Get(ctx context.Context, id string) (containers.Container, error) { 50 namespace, err := namespaces.NamespaceRequired(ctx) 51 if err != nil { 52 return containers.Container{}, err 53 } 54 55 container := containers.Container{ID: id} 56 57 if err := view(ctx, s.db, func(tx *bolt.Tx) error { 58 bkt := getContainerBucket(tx, namespace, id) 59 if bkt == nil { 60 return errors.Wrapf(errdefs.ErrNotFound, "container %q in namespace %q", id, namespace) 61 } 62 63 if err := readContainer(&container, bkt); err != nil { 64 return errors.Wrapf(err, "failed to read container %q", id) 65 } 66 67 return nil 68 }); err != nil { 69 return containers.Container{}, err 70 } 71 72 return container, nil 73 } 74 75 func (s *containerStore) List(ctx context.Context, fs ...string) ([]containers.Container, error) { 76 namespace, err := namespaces.NamespaceRequired(ctx) 77 if err != nil { 78 return nil, err 79 } 80 81 filter, err := filters.ParseAll(fs...) 82 if err != nil { 83 return nil, errors.Wrap(errdefs.ErrInvalidArgument, err.Error()) 84 } 85 86 var m []containers.Container 87 88 if err := view(ctx, s.db, func(tx *bolt.Tx) error { 89 bkt := getContainersBucket(tx, namespace) 90 if bkt == nil { 91 return nil // empty store 92 } 93 94 return bkt.ForEach(func(k, v []byte) error { 95 cbkt := bkt.Bucket(k) 96 if cbkt == nil { 97 return nil 98 } 99 container := containers.Container{ID: string(k)} 100 101 if err := readContainer(&container, cbkt); err != nil { 102 return errors.Wrapf(err, "failed to read container %q", string(k)) 103 } 104 105 if filter.Match(adaptContainer(container)) { 106 m = append(m, container) 107 } 108 return nil 109 }) 110 }); err != nil { 111 return nil, err 112 } 113 114 return m, nil 115 } 116 117 func (s *containerStore) Create(ctx context.Context, container containers.Container) (containers.Container, error) { 118 namespace, err := namespaces.NamespaceRequired(ctx) 119 if err != nil { 120 return containers.Container{}, err 121 } 122 123 if err := validateContainer(&container); err != nil { 124 return containers.Container{}, errors.Wrap(err, "create container failed validation") 125 } 126 127 if err := update(ctx, s.db, func(tx *bolt.Tx) error { 128 bkt, err := createContainersBucket(tx, namespace) 129 if err != nil { 130 return err 131 } 132 133 cbkt, err := bkt.CreateBucket([]byte(container.ID)) 134 if err != nil { 135 if err == bolt.ErrBucketExists { 136 err = errors.Wrapf(errdefs.ErrAlreadyExists, "container %q", container.ID) 137 } 138 return err 139 } 140 141 container.CreatedAt = time.Now().UTC() 142 container.UpdatedAt = container.CreatedAt 143 if err := writeContainer(cbkt, &container); err != nil { 144 return errors.Wrapf(err, "failed to write container %q", container.ID) 145 } 146 147 return nil 148 }); err != nil { 149 return containers.Container{}, err 150 } 151 152 return container, nil 153 } 154 155 func (s *containerStore) Update(ctx context.Context, container containers.Container, fieldpaths ...string) (containers.Container, error) { 156 namespace, err := namespaces.NamespaceRequired(ctx) 157 if err != nil { 158 return containers.Container{}, err 159 } 160 161 if container.ID == "" { 162 return containers.Container{}, errors.Wrapf(errdefs.ErrInvalidArgument, "must specify a container id") 163 } 164 165 var updated containers.Container 166 if err := update(ctx, s.db, func(tx *bolt.Tx) error { 167 bkt := getContainersBucket(tx, namespace) 168 if bkt == nil { 169 return errors.Wrapf(errdefs.ErrNotFound, "cannot update container %q in namespace %q", container.ID, namespace) 170 } 171 172 cbkt := bkt.Bucket([]byte(container.ID)) 173 if cbkt == nil { 174 return errors.Wrapf(errdefs.ErrNotFound, "container %q", container.ID) 175 } 176 177 if err := readContainer(&updated, cbkt); err != nil { 178 return errors.Wrapf(err, "failed to read container %q", container.ID) 179 } 180 createdat := updated.CreatedAt 181 updated.ID = container.ID 182 183 if len(fieldpaths) == 0 { 184 // only allow updates to these field on full replace. 185 fieldpaths = []string{"labels", "spec", "extensions", "image", "snapshotkey"} 186 187 // Fields that are immutable must cause an error when no field paths 188 // are provided. This allows these fields to become mutable in the 189 // future. 190 if updated.Snapshotter != container.Snapshotter { 191 return errors.Wrapf(errdefs.ErrInvalidArgument, "container.Snapshotter field is immutable") 192 } 193 194 if updated.Runtime.Name != container.Runtime.Name { 195 return errors.Wrapf(errdefs.ErrInvalidArgument, "container.Runtime.Name field is immutable") 196 } 197 } 198 199 // apply the field mask. If you update this code, you better follow the 200 // field mask rules in field_mask.proto. If you don't know what this 201 // is, do not update this code. 202 for _, path := range fieldpaths { 203 if strings.HasPrefix(path, "labels.") { 204 if updated.Labels == nil { 205 updated.Labels = map[string]string{} 206 } 207 key := strings.TrimPrefix(path, "labels.") 208 updated.Labels[key] = container.Labels[key] 209 continue 210 } 211 212 if strings.HasPrefix(path, "extensions.") { 213 if updated.Extensions == nil { 214 updated.Extensions = map[string]types.Any{} 215 } 216 key := strings.TrimPrefix(path, "extensions.") 217 updated.Extensions[key] = container.Extensions[key] 218 continue 219 } 220 221 switch path { 222 case "labels": 223 updated.Labels = container.Labels 224 case "spec": 225 updated.Spec = container.Spec 226 case "extensions": 227 updated.Extensions = container.Extensions 228 case "image": 229 updated.Image = container.Image 230 case "snapshotkey": 231 updated.SnapshotKey = container.SnapshotKey 232 default: 233 return errors.Wrapf(errdefs.ErrInvalidArgument, "cannot update %q field on %q", path, container.ID) 234 } 235 } 236 237 if err := validateContainer(&updated); err != nil { 238 return errors.Wrap(err, "update failed validation") 239 } 240 241 updated.CreatedAt = createdat 242 updated.UpdatedAt = time.Now().UTC() 243 if err := writeContainer(cbkt, &updated); err != nil { 244 return errors.Wrapf(err, "failed to write container %q", container.ID) 245 } 246 247 return nil 248 }); err != nil { 249 return containers.Container{}, err 250 } 251 252 return updated, nil 253 } 254 255 func (s *containerStore) Delete(ctx context.Context, id string) error { 256 namespace, err := namespaces.NamespaceRequired(ctx) 257 if err != nil { 258 return err 259 } 260 261 return update(ctx, s.db, func(tx *bolt.Tx) error { 262 bkt := getContainersBucket(tx, namespace) 263 if bkt == nil { 264 return errors.Wrapf(errdefs.ErrNotFound, "cannot delete container %q in namespace %q", id, namespace) 265 } 266 267 if err := bkt.DeleteBucket([]byte(id)); err != nil { 268 if err == bolt.ErrBucketNotFound { 269 err = errors.Wrapf(errdefs.ErrNotFound, "container %v", id) 270 } 271 return err 272 } 273 274 atomic.AddUint32(&s.db.dirty, 1) 275 276 return nil 277 }) 278 } 279 280 func validateContainer(container *containers.Container) error { 281 if err := identifiers.Validate(container.ID); err != nil { 282 return errors.Wrap(err, "container.ID") 283 } 284 285 for k := range container.Extensions { 286 if k == "" { 287 return errors.Wrapf(errdefs.ErrInvalidArgument, "container.Extension keys must not be zero-length") 288 } 289 } 290 291 // image has no validation 292 for k, v := range container.Labels { 293 if err := labels.Validate(k, v); err == nil { 294 return errors.Wrapf(err, "containers.Labels") 295 } 296 } 297 298 if container.Runtime.Name == "" { 299 return errors.Wrapf(errdefs.ErrInvalidArgument, "container.Runtime.Name must be set") 300 } 301 302 if container.Spec == nil { 303 return errors.Wrapf(errdefs.ErrInvalidArgument, "container.Spec must be set") 304 } 305 306 if container.SnapshotKey != "" && container.Snapshotter == "" { 307 return errors.Wrapf(errdefs.ErrInvalidArgument, "container.Snapshotter must be set if container.SnapshotKey is set") 308 } 309 310 return nil 311 } 312 313 func readContainer(container *containers.Container, bkt *bolt.Bucket) error { 314 labels, err := boltutil.ReadLabels(bkt) 315 if err != nil { 316 return err 317 } 318 container.Labels = labels 319 320 if err := boltutil.ReadTimestamps(bkt, &container.CreatedAt, &container.UpdatedAt); err != nil { 321 return err 322 } 323 324 return bkt.ForEach(func(k, v []byte) error { 325 switch string(k) { 326 case string(bucketKeyImage): 327 container.Image = string(v) 328 case string(bucketKeyRuntime): 329 rbkt := bkt.Bucket(bucketKeyRuntime) 330 if rbkt == nil { 331 return nil // skip runtime. should be an error? 332 } 333 334 n := rbkt.Get(bucketKeyName) 335 if n != nil { 336 container.Runtime.Name = string(n) 337 } 338 339 obkt := rbkt.Get(bucketKeyOptions) 340 if obkt == nil { 341 return nil 342 } 343 344 var any types.Any 345 if err := proto.Unmarshal(obkt, &any); err != nil { 346 return err 347 } 348 container.Runtime.Options = &any 349 case string(bucketKeySpec): 350 var any types.Any 351 if err := proto.Unmarshal(v, &any); err != nil { 352 return err 353 } 354 container.Spec = &any 355 case string(bucketKeySnapshotKey): 356 container.SnapshotKey = string(v) 357 case string(bucketKeySnapshotter): 358 container.Snapshotter = string(v) 359 case string(bucketKeyExtensions): 360 ebkt := bkt.Bucket(bucketKeyExtensions) 361 if ebkt == nil { 362 return nil 363 } 364 365 extensions := make(map[string]types.Any) 366 if err := ebkt.ForEach(func(k, v []byte) error { 367 var a types.Any 368 if err := proto.Unmarshal(v, &a); err != nil { 369 return err 370 } 371 372 extensions[string(k)] = a 373 return nil 374 }); err != nil { 375 376 return err 377 } 378 379 container.Extensions = extensions 380 } 381 382 return nil 383 }) 384 } 385 386 func writeContainer(bkt *bolt.Bucket, container *containers.Container) error { 387 if err := boltutil.WriteTimestamps(bkt, container.CreatedAt, container.UpdatedAt); err != nil { 388 return err 389 } 390 391 if container.Spec != nil { 392 spec, err := container.Spec.Marshal() 393 if err != nil { 394 return err 395 } 396 397 if err := bkt.Put(bucketKeySpec, spec); err != nil { 398 return err 399 } 400 } 401 402 for _, v := range [][2][]byte{ 403 {bucketKeyImage, []byte(container.Image)}, 404 {bucketKeySnapshotter, []byte(container.Snapshotter)}, 405 {bucketKeySnapshotKey, []byte(container.SnapshotKey)}, 406 } { 407 if err := bkt.Put(v[0], v[1]); err != nil { 408 return err 409 } 410 } 411 412 if rbkt := bkt.Bucket(bucketKeyRuntime); rbkt != nil { 413 if err := bkt.DeleteBucket(bucketKeyRuntime); err != nil { 414 return err 415 } 416 } 417 418 rbkt, err := bkt.CreateBucket(bucketKeyRuntime) 419 if err != nil { 420 return err 421 } 422 423 if err := rbkt.Put(bucketKeyName, []byte(container.Runtime.Name)); err != nil { 424 return err 425 } 426 427 if len(container.Extensions) > 0 { 428 ebkt, err := bkt.CreateBucketIfNotExists(bucketKeyExtensions) 429 if err != nil { 430 return err 431 } 432 433 for name, ext := range container.Extensions { 434 p, err := proto.Marshal(&ext) 435 if err != nil { 436 return err 437 } 438 439 if err := ebkt.Put([]byte(name), p); err != nil { 440 return err 441 } 442 } 443 } 444 445 if container.Runtime.Options != nil { 446 data, err := proto.Marshal(container.Runtime.Options) 447 if err != nil { 448 return err 449 } 450 451 if err := rbkt.Put(bucketKeyOptions, data); err != nil { 452 return err 453 } 454 } 455 456 return boltutil.WriteLabels(bkt, container.Labels) 457 }