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 }