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