github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/storage/provider/tmpfs.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package provider
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/names"
    13  	"github.com/juju/utils"
    14  
    15  	"github.com/juju/juju/environs/config"
    16  	"github.com/juju/juju/storage"
    17  )
    18  
    19  const (
    20  	TmpfsProviderType = storage.ProviderType("tmpfs")
    21  )
    22  
    23  // tmpfsProviders create storage sources which provide access to filesystems.
    24  type tmpfsProvider struct {
    25  	// run is a function type used for running commands on the local machine.
    26  	run runCommandFunc
    27  }
    28  
    29  var (
    30  	_ storage.Provider = (*tmpfsProvider)(nil)
    31  )
    32  
    33  // ValidateConfig is defined on the Provider interface.
    34  func (p *tmpfsProvider) ValidateConfig(cfg *storage.Config) error {
    35  	// Tmpfs provider has no configuration.
    36  	return nil
    37  }
    38  
    39  // validateFullConfig validates a fully-constructed storage config,
    40  // combining the user-specified config and any internally specified
    41  // config.
    42  func (p *tmpfsProvider) validateFullConfig(cfg *storage.Config) error {
    43  	if err := p.ValidateConfig(cfg); err != nil {
    44  		return err
    45  	}
    46  	storageDir, ok := cfg.ValueString(storage.ConfigStorageDir)
    47  	if !ok || storageDir == "" {
    48  		return errors.New("storage directory not specified")
    49  	}
    50  	return nil
    51  }
    52  
    53  // VolumeSource is defined on the Provider interface.
    54  func (p *tmpfsProvider) VolumeSource(environConfig *config.Config, providerConfig *storage.Config) (storage.VolumeSource, error) {
    55  	return nil, errors.NotSupportedf("volumes")
    56  }
    57  
    58  // FilesystemSource is defined on the Provider interface.
    59  func (p *tmpfsProvider) FilesystemSource(environConfig *config.Config, sourceConfig *storage.Config) (storage.FilesystemSource, error) {
    60  	if err := p.validateFullConfig(sourceConfig); err != nil {
    61  		return nil, err
    62  	}
    63  	// storageDir is validated by validateFullConfig.
    64  	storageDir, _ := sourceConfig.ValueString(storage.ConfigStorageDir)
    65  	return &tmpfsFilesystemSource{
    66  		&osDirFuncs{p.run},
    67  		p.run,
    68  		storageDir,
    69  	}, nil
    70  }
    71  
    72  // Supports is defined on the Provider interface.
    73  func (*tmpfsProvider) Supports(k storage.StorageKind) bool {
    74  	return k == storage.StorageKindFilesystem
    75  }
    76  
    77  // Scope is defined on the Provider interface.
    78  func (*tmpfsProvider) Scope() storage.Scope {
    79  	return storage.ScopeMachine
    80  }
    81  
    82  // Dynamic is defined on the Provider interface.
    83  func (*tmpfsProvider) Dynamic() bool {
    84  	return true
    85  }
    86  
    87  type tmpfsFilesystemSource struct {
    88  	dirFuncs   dirFuncs
    89  	run        runCommandFunc
    90  	storageDir string
    91  }
    92  
    93  var _ storage.FilesystemSource = (*tmpfsFilesystemSource)(nil)
    94  
    95  // ValidateFilesystemParams is defined on the FilesystemSource interface.
    96  func (s *tmpfsFilesystemSource) ValidateFilesystemParams(params storage.FilesystemParams) error {
    97  	// ValidateFilesystemParams may be called on a machine other than the
    98  	// machine where the filesystem will be mounted, so we cannot check
    99  	// available size until we get to createFilesystem.
   100  	return nil
   101  }
   102  
   103  // CreateFilesystems is defined on the FilesystemSource interface.
   104  func (s *tmpfsFilesystemSource) CreateFilesystems(args []storage.FilesystemParams) ([]storage.Filesystem, error) {
   105  	filesystems := make([]storage.Filesystem, 0, len(args))
   106  	for _, arg := range args {
   107  		filesystem, err := s.createFilesystem(arg)
   108  		if err != nil {
   109  			return nil, errors.Annotate(err, "creating filesystem")
   110  		}
   111  		filesystems = append(filesystems, filesystem)
   112  	}
   113  	return filesystems, nil
   114  }
   115  
   116  var getpagesize = os.Getpagesize
   117  
   118  func (s *tmpfsFilesystemSource) createFilesystem(params storage.FilesystemParams) (storage.Filesystem, error) {
   119  	if err := s.ValidateFilesystemParams(params); err != nil {
   120  		return storage.Filesystem{}, errors.Trace(err)
   121  	}
   122  	// Align size to the page size in MiB.
   123  	sizeInMiB := params.Size
   124  	pageSizeInMiB := uint64(getpagesize()) / (1024 * 1024)
   125  	if pageSizeInMiB > 0 {
   126  		x := (sizeInMiB + pageSizeInMiB - 1)
   127  		sizeInMiB = x - x%pageSizeInMiB
   128  	}
   129  
   130  	info := storage.FilesystemInfo{
   131  		FilesystemId: params.Tag.String(),
   132  		Size:         sizeInMiB,
   133  	}
   134  
   135  	// Creating the mount is the responsibility of AttachFilesystems.
   136  	// AttachFilesystems needs to know the size so it can pass it onto
   137  	// "mount"; write the size of the filesystem to a file in the
   138  	// storage directory.
   139  	if err := s.writeFilesystemInfo(params.Tag, info); err != nil {
   140  		return storage.Filesystem{}, err
   141  	}
   142  
   143  	return storage.Filesystem{params.Tag, params.Volume, info}, nil
   144  }
   145  
   146  // DestroyFilesystems is defined on the FilesystemSource interface.
   147  func (s *tmpfsFilesystemSource) DestroyFilesystems(filesystemIds []string) []error {
   148  	// DestroyFilesystems is a no-op; there is nothing to destroy,
   149  	// since the filesystem is ephemeral and disappears once
   150  	// detached.
   151  	return make([]error, len(filesystemIds))
   152  }
   153  
   154  // AttachFilesystems is defined on the FilesystemSource interface.
   155  func (s *tmpfsFilesystemSource) AttachFilesystems(args []storage.FilesystemAttachmentParams) ([]storage.FilesystemAttachment, error) {
   156  	attachments := make([]storage.FilesystemAttachment, len(args))
   157  	for i, arg := range args {
   158  		attachment, err := s.attachFilesystem(arg)
   159  		if err != nil {
   160  			return nil, errors.Annotatef(err, "attaching %s", names.ReadableString(arg.Filesystem))
   161  		}
   162  		attachments[i] = attachment
   163  	}
   164  	return attachments, nil
   165  }
   166  
   167  func (s *tmpfsFilesystemSource) attachFilesystem(arg storage.FilesystemAttachmentParams) (storage.FilesystemAttachment, error) {
   168  	path := arg.Path
   169  	if path == "" {
   170  		return storage.FilesystemAttachment{}, errNoMountPoint
   171  	}
   172  	info, err := s.readFilesystemInfo(arg.Filesystem)
   173  	if err != nil {
   174  		return storage.FilesystemAttachment{}, err
   175  	}
   176  	if err := ensureDir(s.dirFuncs, path); err != nil {
   177  		return storage.FilesystemAttachment{}, errors.Trace(err)
   178  	}
   179  
   180  	// Check if the mount already exists.
   181  	source, err := s.dirFuncs.mountPointSource(path)
   182  	if err != nil {
   183  		return storage.FilesystemAttachment{}, errors.Trace(err)
   184  	}
   185  	if source != arg.Filesystem.String() {
   186  		if err := ensureEmptyDir(s.dirFuncs, path); err != nil {
   187  			return storage.FilesystemAttachment{}, err
   188  		}
   189  		options := fmt.Sprintf("size=%dm", info.Size)
   190  		if arg.ReadOnly {
   191  			options += ",ro"
   192  		}
   193  		if _, err := s.run(
   194  			"mount", "-t", "tmpfs", arg.Filesystem.String(), path, "-o", options,
   195  		); err != nil {
   196  			os.Remove(path)
   197  			return storage.FilesystemAttachment{}, errors.Annotate(err, "cannot mount tmpfs")
   198  		}
   199  	}
   200  
   201  	return storage.FilesystemAttachment{
   202  		arg.Filesystem,
   203  		arg.Machine,
   204  		storage.FilesystemAttachmentInfo{
   205  			Path:     path,
   206  			ReadOnly: arg.ReadOnly,
   207  		},
   208  	}, nil
   209  }
   210  
   211  // DetachFilesystems is defined on the FilesystemSource interface.
   212  func (s *tmpfsFilesystemSource) DetachFilesystems(args []storage.FilesystemAttachmentParams) error {
   213  	for _, arg := range args {
   214  		if err := maybeUnmount(s.run, s.dirFuncs, arg.Path); err != nil {
   215  			return errors.Annotatef(err, "detaching filesystem %s", arg.Filesystem.Id())
   216  		}
   217  	}
   218  	return nil
   219  }
   220  
   221  func (s *tmpfsFilesystemSource) writeFilesystemInfo(tag names.FilesystemTag, info storage.FilesystemInfo) error {
   222  	filename := s.filesystemInfoFile(tag)
   223  	if _, err := os.Stat(filename); err == nil {
   224  		return errors.Errorf("filesystem %v already exists", tag.Id())
   225  	}
   226  	if err := ensureDir(s.dirFuncs, filepath.Dir(filename)); err != nil {
   227  		return errors.Trace(err)
   228  	}
   229  	err := utils.WriteYaml(filename, filesystemInfo{&info.Size})
   230  	if err != nil {
   231  		return errors.Annotate(err, "writing filesystem info to disk")
   232  	}
   233  	return err
   234  }
   235  
   236  func (s *tmpfsFilesystemSource) readFilesystemInfo(tag names.FilesystemTag) (storage.FilesystemInfo, error) {
   237  	var info filesystemInfo
   238  	if err := utils.ReadYaml(s.filesystemInfoFile(tag), &info); err != nil {
   239  		return storage.FilesystemInfo{}, errors.Annotate(err, "reading filesystem info from disk")
   240  	}
   241  	if info.Size == nil {
   242  		return storage.FilesystemInfo{}, errors.New("invalid filesystem info: missing size")
   243  	}
   244  	return storage.FilesystemInfo{
   245  		FilesystemId: tag.String(),
   246  		Size:         *info.Size,
   247  	}, nil
   248  }
   249  
   250  func (s *tmpfsFilesystemSource) filesystemInfoFile(tag names.FilesystemTag) string {
   251  	return filepath.Join(s.storageDir, tag.Id()+".info")
   252  }
   253  
   254  type filesystemInfo struct {
   255  	Size *uint64 `yaml:"size,omitempty"`
   256  }