github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/libpod/storage.go (about)

     1  package libpod
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	istorage "github.com/containers/image/v5/storage"
     8  	"github.com/containers/image/v5/types"
     9  	"github.com/containers/libpod/libpod/define"
    10  	"github.com/containers/storage"
    11  	"github.com/containers/storage/pkg/idtools"
    12  	v1 "github.com/opencontainers/image-spec/specs-go/v1"
    13  	"github.com/opentracing/opentracing-go"
    14  	"github.com/pkg/errors"
    15  	"github.com/sirupsen/logrus"
    16  )
    17  
    18  type storageService struct {
    19  	store storage.Store
    20  }
    21  
    22  // getStorageService returns a storageService which can create container root
    23  // filesystems from images
    24  func getStorageService(store storage.Store) (*storageService, error) {
    25  	return &storageService{store: store}, nil
    26  }
    27  
    28  // ContainerInfo wraps a subset of information about a container: the locations
    29  // of its nonvolatile and volatile per-container directories, along with a copy
    30  // of the configuration blob from the image that was used to create the
    31  // container, if the image had a configuration.
    32  // It also returns the ProcessLabel and MountLabel selected for the container
    33  type ContainerInfo struct {
    34  	Dir          string
    35  	RunDir       string
    36  	Config       *v1.Image
    37  	ProcessLabel string
    38  	MountLabel   string
    39  	UIDMap       []idtools.IDMap
    40  	GIDMap       []idtools.IDMap
    41  }
    42  
    43  // RuntimeContainerMetadata is the structure that we encode as JSON and store
    44  // in the metadata field of storage.Container objects.  It is used for
    45  // specifying attributes containers when they are being created, and allows a
    46  // container's MountLabel, and possibly other values, to be modified in one
    47  // read/write cycle via calls to storageService.ContainerMetadata,
    48  // RuntimeContainerMetadata.SetMountLabel, and
    49  // storageService.SetContainerMetadata.
    50  type RuntimeContainerMetadata struct {
    51  	// The provided name and the ID of the image that was used to
    52  	// instantiate the container.
    53  	ImageName string `json:"image-name"` // Applicable to both PodSandboxes and Containers
    54  	ImageID   string `json:"image-id"`   // Applicable to both PodSandboxes and Containers
    55  	// The container's name, which for an infrastructure container is usually PodName + "-infra".
    56  	ContainerName string `json:"name"`                 // Applicable to both PodSandboxes and Containers, mandatory
    57  	CreatedAt     int64  `json:"created-at"`           // Applicable to both PodSandboxes and Containers
    58  	MountLabel    string `json:"mountlabel,omitempty"` // Applicable to both PodSandboxes and Containers
    59  }
    60  
    61  // SetMountLabel updates the mount label held by a RuntimeContainerMetadata
    62  // object.
    63  func (metadata *RuntimeContainerMetadata) SetMountLabel(mountLabel string) {
    64  	metadata.MountLabel = mountLabel
    65  }
    66  
    67  // CreateContainerStorage creates the storage end of things.  We already have the container spec created
    68  // TO-DO We should be passing in an Image object in the future.
    69  func (r *storageService) CreateContainerStorage(ctx context.Context, systemContext *types.SystemContext, imageName, imageID, containerName, containerID string, options storage.ContainerOptions) (cinfo ContainerInfo, err error) {
    70  	span, _ := opentracing.StartSpanFromContext(ctx, "createContainerStorage")
    71  	span.SetTag("type", "storageService")
    72  	defer span.Finish()
    73  
    74  	var imageConfig *v1.Image
    75  	if imageName != "" {
    76  		var ref types.ImageReference
    77  		if containerName == "" {
    78  			return ContainerInfo{}, define.ErrEmptyID
    79  		}
    80  		// Check if we have the specified image.
    81  		ref, err := istorage.Transport.ParseStoreReference(r.store, imageID)
    82  		if err != nil {
    83  			return ContainerInfo{}, err
    84  		}
    85  		img, err := istorage.Transport.GetStoreImage(r.store, ref)
    86  		if err != nil {
    87  			return ContainerInfo{}, err
    88  		}
    89  		// Pull out a copy of the image's configuration.
    90  		image, err := ref.NewImage(ctx, systemContext)
    91  		if err != nil {
    92  			return ContainerInfo{}, err
    93  		}
    94  		defer image.Close()
    95  
    96  		// Get OCI configuration of image
    97  		imageConfig, err = image.OCIConfig(ctx)
    98  		if err != nil {
    99  			return ContainerInfo{}, err
   100  		}
   101  
   102  		// Update the image name and ID.
   103  		if imageName == "" && len(img.Names) > 0 {
   104  			imageName = img.Names[0]
   105  		}
   106  		imageID = img.ID
   107  	}
   108  
   109  	// Build metadata to store with the container.
   110  	metadata := RuntimeContainerMetadata{
   111  		ImageName:     imageName,
   112  		ImageID:       imageID,
   113  		ContainerName: containerName,
   114  		CreatedAt:     time.Now().Unix(),
   115  	}
   116  	mdata, err := json.Marshal(&metadata)
   117  	if err != nil {
   118  		return ContainerInfo{}, err
   119  	}
   120  
   121  	// Build the container.
   122  	names := []string{containerName}
   123  
   124  	container, err := r.store.CreateContainer(containerID, names, imageID, "", string(mdata), &options)
   125  	if err != nil {
   126  		logrus.Debugf("failed to create container %s(%s): %v", metadata.ContainerName, containerID, err)
   127  
   128  		return ContainerInfo{}, err
   129  	}
   130  	logrus.Debugf("created container %q", container.ID)
   131  
   132  	// If anything fails after this point, we need to delete the incomplete
   133  	// container before returning.
   134  	defer func() {
   135  		if err != nil {
   136  			if err2 := r.store.DeleteContainer(container.ID); err2 != nil {
   137  				logrus.Infof("%v deleting partially-created container %q", err2, container.ID)
   138  
   139  				return
   140  			}
   141  			logrus.Infof("deleted partially-created container %q", container.ID)
   142  		}
   143  	}()
   144  
   145  	// Add a name to the container's layer so that it's easier to follow
   146  	// what's going on if we're just looking at the storage-eye view of things.
   147  	layerName := metadata.ContainerName + "-layer"
   148  	names, err = r.store.Names(container.LayerID)
   149  	if err != nil {
   150  		return ContainerInfo{}, err
   151  	}
   152  	names = append(names, layerName)
   153  	err = r.store.SetNames(container.LayerID, names)
   154  	if err != nil {
   155  		return ContainerInfo{}, err
   156  	}
   157  
   158  	// Find out where the container work directories are, so that we can return them.
   159  	containerDir, err := r.store.ContainerDirectory(container.ID)
   160  	if err != nil {
   161  		return ContainerInfo{}, err
   162  	}
   163  	logrus.Debugf("container %q has work directory %q", container.ID, containerDir)
   164  
   165  	containerRunDir, err := r.store.ContainerRunDirectory(container.ID)
   166  	if err != nil {
   167  		return ContainerInfo{}, err
   168  	}
   169  	logrus.Debugf("container %q has run directory %q", container.ID, containerRunDir)
   170  
   171  	return ContainerInfo{
   172  		UIDMap:       options.UIDMap,
   173  		GIDMap:       options.GIDMap,
   174  		Dir:          containerDir,
   175  		RunDir:       containerRunDir,
   176  		Config:       imageConfig,
   177  		ProcessLabel: container.ProcessLabel(),
   178  		MountLabel:   container.MountLabel(),
   179  	}, nil
   180  }
   181  
   182  func (r *storageService) DeleteContainer(idOrName string) error {
   183  	if idOrName == "" {
   184  		return define.ErrEmptyID
   185  	}
   186  	container, err := r.store.Container(idOrName)
   187  	if err != nil {
   188  		return err
   189  	}
   190  	err = r.store.DeleteContainer(container.ID)
   191  	if err != nil {
   192  		logrus.Debugf("failed to delete container %q: %v", container.ID, err)
   193  		return err
   194  	}
   195  	return nil
   196  }
   197  
   198  func (r *storageService) SetContainerMetadata(idOrName string, metadata RuntimeContainerMetadata) error {
   199  	mdata, err := json.Marshal(&metadata)
   200  	if err != nil {
   201  		logrus.Debugf("failed to encode metadata for %q: %v", idOrName, err)
   202  		return err
   203  	}
   204  	return r.store.SetMetadata(idOrName, string(mdata))
   205  }
   206  
   207  func (r *storageService) GetContainerMetadata(idOrName string) (RuntimeContainerMetadata, error) {
   208  	metadata := RuntimeContainerMetadata{}
   209  	mdata, err := r.store.Metadata(idOrName)
   210  	if err != nil {
   211  		return metadata, err
   212  	}
   213  	if err = json.Unmarshal([]byte(mdata), &metadata); err != nil {
   214  		return metadata, err
   215  	}
   216  	return metadata, nil
   217  }
   218  
   219  func (r *storageService) MountContainerImage(idOrName string) (string, error) {
   220  	container, err := r.store.Container(idOrName)
   221  	if err != nil {
   222  		if errors.Cause(err) == storage.ErrContainerUnknown {
   223  			return "", define.ErrNoSuchCtr
   224  		}
   225  		return "", err
   226  	}
   227  	metadata := RuntimeContainerMetadata{}
   228  	if err = json.Unmarshal([]byte(container.Metadata), &metadata); err != nil {
   229  		return "", err
   230  	}
   231  	mountPoint, err := r.store.Mount(container.ID, metadata.MountLabel)
   232  	if err != nil {
   233  		logrus.Debugf("failed to mount container %q: %v", container.ID, err)
   234  		return "", err
   235  	}
   236  	logrus.Debugf("mounted container %q at %q", container.ID, mountPoint)
   237  	return mountPoint, nil
   238  }
   239  
   240  func (r *storageService) UnmountContainerImage(idOrName string, force bool) (bool, error) {
   241  	if idOrName == "" {
   242  		return false, define.ErrEmptyID
   243  	}
   244  	container, err := r.store.Container(idOrName)
   245  	if err != nil {
   246  		return false, err
   247  	}
   248  
   249  	if !force {
   250  		mounted, err := r.store.Mounted(container.ID)
   251  		if err != nil {
   252  			return false, err
   253  		}
   254  		if mounted == 0 {
   255  			return false, storage.ErrLayerNotMounted
   256  		}
   257  	}
   258  	mounted, err := r.store.Unmount(container.ID, force)
   259  	if err != nil {
   260  		logrus.Debugf("failed to unmount container %q: %v", container.ID, err)
   261  		return false, err
   262  	}
   263  	logrus.Debugf("unmounted container %q", container.ID)
   264  	return mounted, nil
   265  }
   266  
   267  func (r *storageService) MountedContainerImage(idOrName string) (int, error) {
   268  	if idOrName == "" {
   269  		return 0, define.ErrEmptyID
   270  	}
   271  	container, err := r.store.Container(idOrName)
   272  	if err != nil {
   273  		return 0, err
   274  	}
   275  	mounted, err := r.store.Mounted(container.ID)
   276  	if err != nil {
   277  		return 0, err
   278  	}
   279  	return mounted, nil
   280  }
   281  
   282  func (r *storageService) GetMountpoint(id string) (string, error) {
   283  	container, err := r.store.Container(id)
   284  	if err != nil {
   285  		if errors.Cause(err) == storage.ErrContainerUnknown {
   286  			return "", define.ErrNoSuchCtr
   287  		}
   288  		return "", err
   289  	}
   290  	layer, err := r.store.Layer(container.LayerID)
   291  	if err != nil {
   292  		return "", err
   293  	}
   294  
   295  	return layer.MountPoint, nil
   296  }
   297  
   298  func (r *storageService) GetWorkDir(id string) (string, error) {
   299  	container, err := r.store.Container(id)
   300  	if err != nil {
   301  		if errors.Cause(err) == storage.ErrContainerUnknown {
   302  			return "", define.ErrNoSuchCtr
   303  		}
   304  		return "", err
   305  	}
   306  	return r.store.ContainerDirectory(container.ID)
   307  }
   308  
   309  func (r *storageService) GetRunDir(id string) (string, error) {
   310  	container, err := r.store.Container(id)
   311  	if err != nil {
   312  		if errors.Cause(err) == storage.ErrContainerUnknown {
   313  			return "", define.ErrNoSuchCtr
   314  		}
   315  		return "", err
   316  	}
   317  	return r.store.ContainerRunDirectory(container.ID)
   318  }