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  }