github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/metadata/leases.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 "fmt" 22 "strings" 23 "sync/atomic" 24 "time" 25 26 "github.com/containerd/containerd/errdefs" 27 "github.com/containerd/containerd/filters" 28 "github.com/containerd/containerd/leases" 29 "github.com/containerd/containerd/metadata/boltutil" 30 "github.com/containerd/containerd/namespaces" 31 digest "github.com/opencontainers/go-digest" 32 "github.com/pkg/errors" 33 bolt "go.etcd.io/bbolt" 34 ) 35 36 // LeaseManager manages the create/delete lifecycle of leases 37 // and also returns existing leases 38 type LeaseManager struct { 39 db *DB 40 } 41 42 // NewLeaseManager creates a new lease manager for managing leases using 43 // the provided database transaction. 44 func NewLeaseManager(db *DB) *LeaseManager { 45 return &LeaseManager{ 46 db: db, 47 } 48 } 49 50 // Create creates a new lease using the provided lease 51 func (lm *LeaseManager) Create(ctx context.Context, opts ...leases.Opt) (leases.Lease, error) { 52 var l leases.Lease 53 for _, opt := range opts { 54 if err := opt(&l); err != nil { 55 return leases.Lease{}, err 56 } 57 } 58 if l.ID == "" { 59 return leases.Lease{}, errors.New("lease id must be provided") 60 } 61 62 namespace, err := namespaces.NamespaceRequired(ctx) 63 if err != nil { 64 return leases.Lease{}, err 65 } 66 67 if err := update(ctx, lm.db, func(tx *bolt.Tx) error { 68 topbkt, err := createBucketIfNotExists(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases) 69 if err != nil { 70 return err 71 } 72 73 txbkt, err := topbkt.CreateBucket([]byte(l.ID)) 74 if err != nil { 75 if err == bolt.ErrBucketExists { 76 err = errdefs.ErrAlreadyExists 77 } 78 return errors.Wrapf(err, "lease %q", l.ID) 79 } 80 81 t := time.Now().UTC() 82 createdAt, err := t.MarshalBinary() 83 if err != nil { 84 return err 85 } 86 if err := txbkt.Put(bucketKeyCreatedAt, createdAt); err != nil { 87 return err 88 } 89 90 if l.Labels != nil { 91 if err := boltutil.WriteLabels(txbkt, l.Labels); err != nil { 92 return err 93 } 94 } 95 l.CreatedAt = t 96 97 return nil 98 }); err != nil { 99 return leases.Lease{}, err 100 } 101 return l, nil 102 } 103 104 // Delete deletes the lease with the provided lease ID 105 func (lm *LeaseManager) Delete(ctx context.Context, lease leases.Lease, _ ...leases.DeleteOpt) error { 106 namespace, err := namespaces.NamespaceRequired(ctx) 107 if err != nil { 108 return err 109 } 110 111 return update(ctx, lm.db, func(tx *bolt.Tx) error { 112 topbkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases) 113 if topbkt == nil { 114 return errors.Wrapf(errdefs.ErrNotFound, "lease %q", lease.ID) 115 } 116 if err := topbkt.DeleteBucket([]byte(lease.ID)); err != nil { 117 if err == bolt.ErrBucketNotFound { 118 err = errors.Wrapf(errdefs.ErrNotFound, "lease %q", lease.ID) 119 } 120 return err 121 } 122 123 atomic.AddUint32(&lm.db.dirty, 1) 124 125 return nil 126 }) 127 } 128 129 // List lists all active leases 130 func (lm *LeaseManager) List(ctx context.Context, fs ...string) ([]leases.Lease, error) { 131 namespace, err := namespaces.NamespaceRequired(ctx) 132 if err != nil { 133 return nil, err 134 } 135 136 filter, err := filters.ParseAll(fs...) 137 if err != nil { 138 return nil, errors.Wrap(errdefs.ErrInvalidArgument, err.Error()) 139 } 140 141 var ll []leases.Lease 142 143 if err := view(ctx, lm.db, func(tx *bolt.Tx) error { 144 topbkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases) 145 if topbkt == nil { 146 return nil 147 } 148 149 return topbkt.ForEach(func(k, v []byte) error { 150 if v != nil { 151 return nil 152 } 153 txbkt := topbkt.Bucket(k) 154 155 l := leases.Lease{ 156 ID: string(k), 157 } 158 159 if v := txbkt.Get(bucketKeyCreatedAt); v != nil { 160 t := &l.CreatedAt 161 if err := t.UnmarshalBinary(v); err != nil { 162 return err 163 } 164 } 165 166 labels, err := boltutil.ReadLabels(txbkt) 167 if err != nil { 168 return err 169 } 170 l.Labels = labels 171 172 if filter.Match(adaptLease(l)) { 173 ll = append(ll, l) 174 } 175 176 return nil 177 }) 178 }); err != nil { 179 return nil, err 180 } 181 182 return ll, nil 183 } 184 185 // AddResource references the resource by the provided lease. 186 func (lm *LeaseManager) AddResource(ctx context.Context, lease leases.Lease, r leases.Resource) error { 187 namespace, err := namespaces.NamespaceRequired(ctx) 188 if err != nil { 189 return err 190 } 191 192 return update(ctx, lm.db, func(tx *bolt.Tx) error { 193 topbkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lease.ID)) 194 if topbkt == nil { 195 return errors.Wrapf(errdefs.ErrNotFound, "lease %q", lease.ID) 196 } 197 198 keys, ref, err := parseLeaseResource(r) 199 if err != nil { 200 return err 201 } 202 203 bkt := topbkt 204 for _, key := range keys { 205 bkt, err = bkt.CreateBucketIfNotExists([]byte(key)) 206 if err != nil { 207 return err 208 } 209 } 210 return bkt.Put([]byte(ref), nil) 211 }) 212 } 213 214 // DeleteResource dereferences the resource by the provided lease. 215 func (lm *LeaseManager) DeleteResource(ctx context.Context, lease leases.Lease, r leases.Resource) error { 216 namespace, err := namespaces.NamespaceRequired(ctx) 217 if err != nil { 218 return err 219 } 220 221 return update(ctx, lm.db, func(tx *bolt.Tx) error { 222 topbkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lease.ID)) 223 if topbkt == nil { 224 return errors.Wrapf(errdefs.ErrNotFound, "lease %q", lease.ID) 225 } 226 227 keys, ref, err := parseLeaseResource(r) 228 if err != nil { 229 return err 230 } 231 232 bkt := topbkt 233 for _, key := range keys { 234 if bkt == nil { 235 break 236 } 237 bkt = bkt.Bucket([]byte(key)) 238 } 239 240 if bkt != nil { 241 if err := bkt.Delete([]byte(ref)); err != nil { 242 return err 243 } 244 } 245 246 atomic.AddUint32(&lm.db.dirty, 1) 247 248 return nil 249 }) 250 } 251 252 // ListResources lists all the resources referenced by the lease. 253 func (lm *LeaseManager) ListResources(ctx context.Context, lease leases.Lease) ([]leases.Resource, error) { 254 namespace, err := namespaces.NamespaceRequired(ctx) 255 if err != nil { 256 return nil, err 257 } 258 259 var rs []leases.Resource 260 261 if err := view(ctx, lm.db, func(tx *bolt.Tx) error { 262 263 topbkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lease.ID)) 264 if topbkt == nil { 265 return errors.Wrapf(errdefs.ErrNotFound, "lease %q", lease.ID) 266 } 267 268 // content resources 269 if cbkt := topbkt.Bucket(bucketKeyObjectContent); cbkt != nil { 270 if err := cbkt.ForEach(func(k, _ []byte) error { 271 rs = append(rs, leases.Resource{ 272 ID: string(k), 273 Type: string(bucketKeyObjectContent), 274 }) 275 276 return nil 277 }); err != nil { 278 return err 279 } 280 } 281 282 // ingest resources 283 if lbkt := topbkt.Bucket(bucketKeyObjectIngests); lbkt != nil { 284 if err := lbkt.ForEach(func(k, _ []byte) error { 285 rs = append(rs, leases.Resource{ 286 ID: string(k), 287 Type: string(bucketKeyObjectIngests), 288 }) 289 290 return nil 291 }); err != nil { 292 return err 293 } 294 } 295 296 // snapshot resources 297 if sbkt := topbkt.Bucket(bucketKeyObjectSnapshots); sbkt != nil { 298 if err := sbkt.ForEach(func(sk, sv []byte) error { 299 if sv != nil { 300 return nil 301 } 302 303 snbkt := sbkt.Bucket(sk) 304 return snbkt.ForEach(func(k, _ []byte) error { 305 rs = append(rs, leases.Resource{ 306 ID: string(k), 307 Type: fmt.Sprintf("%s/%s", bucketKeyObjectSnapshots, sk), 308 }) 309 return nil 310 }) 311 }); err != nil { 312 return err 313 } 314 } 315 316 return nil 317 }); err != nil { 318 return nil, err 319 } 320 return rs, nil 321 } 322 323 func addSnapshotLease(ctx context.Context, tx *bolt.Tx, snapshotter, key string) error { 324 lid, ok := leases.FromContext(ctx) 325 if !ok { 326 return nil 327 } 328 329 namespace, ok := namespaces.Namespace(ctx) 330 if !ok { 331 panic("namespace must already be checked") 332 } 333 334 bkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lid)) 335 if bkt == nil { 336 return errors.Wrap(errdefs.ErrNotFound, "lease does not exist") 337 } 338 339 bkt, err := bkt.CreateBucketIfNotExists(bucketKeyObjectSnapshots) 340 if err != nil { 341 return err 342 } 343 344 bkt, err = bkt.CreateBucketIfNotExists([]byte(snapshotter)) 345 if err != nil { 346 return err 347 } 348 349 return bkt.Put([]byte(key), nil) 350 } 351 352 func removeSnapshotLease(ctx context.Context, tx *bolt.Tx, snapshotter, key string) error { 353 lid, ok := leases.FromContext(ctx) 354 if !ok { 355 return nil 356 } 357 358 namespace, ok := namespaces.Namespace(ctx) 359 if !ok { 360 panic("namespace must already be checked") 361 } 362 363 bkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lid), bucketKeyObjectSnapshots, []byte(snapshotter)) 364 if bkt == nil { 365 // Key does not exist so we return nil 366 return nil 367 } 368 369 return bkt.Delete([]byte(key)) 370 } 371 372 func addContentLease(ctx context.Context, tx *bolt.Tx, dgst digest.Digest) error { 373 lid, ok := leases.FromContext(ctx) 374 if !ok { 375 return nil 376 } 377 378 namespace, ok := namespaces.Namespace(ctx) 379 if !ok { 380 panic("namespace must already be required") 381 } 382 383 bkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lid)) 384 if bkt == nil { 385 return errors.Wrap(errdefs.ErrNotFound, "lease does not exist") 386 } 387 388 bkt, err := bkt.CreateBucketIfNotExists(bucketKeyObjectContent) 389 if err != nil { 390 return err 391 } 392 393 return bkt.Put([]byte(dgst.String()), nil) 394 } 395 396 func removeContentLease(ctx context.Context, tx *bolt.Tx, dgst digest.Digest) error { 397 lid, ok := leases.FromContext(ctx) 398 if !ok { 399 return nil 400 } 401 402 namespace, ok := namespaces.Namespace(ctx) 403 if !ok { 404 panic("namespace must already be checked") 405 } 406 407 bkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lid), bucketKeyObjectContent) 408 if bkt == nil { 409 // Key does not exist so we return nil 410 return nil 411 } 412 413 return bkt.Delete([]byte(dgst.String())) 414 } 415 416 func addIngestLease(ctx context.Context, tx *bolt.Tx, ref string) (bool, error) { 417 lid, ok := leases.FromContext(ctx) 418 if !ok { 419 return false, nil 420 } 421 422 namespace, ok := namespaces.Namespace(ctx) 423 if !ok { 424 panic("namespace must already be required") 425 } 426 427 bkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lid)) 428 if bkt == nil { 429 return false, errors.Wrap(errdefs.ErrNotFound, "lease does not exist") 430 } 431 432 bkt, err := bkt.CreateBucketIfNotExists(bucketKeyObjectIngests) 433 if err != nil { 434 return false, err 435 } 436 437 if err := bkt.Put([]byte(ref), nil); err != nil { 438 return false, err 439 } 440 441 return true, nil 442 } 443 444 func removeIngestLease(ctx context.Context, tx *bolt.Tx, ref string) error { 445 lid, ok := leases.FromContext(ctx) 446 if !ok { 447 return nil 448 } 449 450 namespace, ok := namespaces.Namespace(ctx) 451 if !ok { 452 panic("namespace must already be checked") 453 } 454 455 bkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lid), bucketKeyObjectIngests) 456 if bkt == nil { 457 // Key does not exist so we return nil 458 return nil 459 } 460 461 return bkt.Delete([]byte(ref)) 462 } 463 464 func parseLeaseResource(r leases.Resource) ([]string, string, error) { 465 var ( 466 ref = r.ID 467 typ = r.Type 468 keys = strings.Split(typ, "/") 469 ) 470 471 switch k := keys[0]; k { 472 case string(bucketKeyObjectContent), 473 string(bucketKeyObjectIngests): 474 475 if len(keys) != 1 { 476 return nil, "", errors.Wrapf(errdefs.ErrInvalidArgument, "invalid resource type %s", typ) 477 } 478 479 if k == string(bucketKeyObjectContent) { 480 dgst, err := digest.Parse(ref) 481 if err != nil { 482 return nil, "", errors.Wrapf(errdefs.ErrInvalidArgument, "invalid content resource id %s: %v", ref, err) 483 } 484 ref = dgst.String() 485 } 486 case string(bucketKeyObjectSnapshots): 487 if len(keys) != 2 { 488 return nil, "", errors.Wrapf(errdefs.ErrInvalidArgument, "invalid snapshot resource type %s", typ) 489 } 490 default: 491 return nil, "", errors.Wrapf(errdefs.ErrNotImplemented, "resource type %s not supported yet", typ) 492 } 493 494 return keys, ref, nil 495 }