1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
11
"github.com/juju/errors"
12
"github.com/juju/utils"
13
"gopkg.in/juju/names.v2"
15
"github.com/juju/juju/storage"
19
TmpfsProviderType = storage.ProviderType("tmpfs")
22
// tmpfsProviders create storage sources which provide access to filesystems.
23
type tmpfsProvider struct {
24
// run is a function type used for running commands on the local machine.
29
_ storage.Provider = (*tmpfsProvider)(nil)
32
// ValidateConfig is defined on the Provider interface.
33
func (p *tmpfsProvider) ValidateConfig(cfg *storage.Config) error {
34
// Tmpfs provider has no configuration.
38
// validateFullConfig validates a fully-constructed storage config,
39
// combining the user-specified config and any internally specified
41
func (p *tmpfsProvider) validateFullConfig(cfg *storage.Config) error {
42
if err := p.ValidateConfig(cfg); err != nil {
45
storageDir, ok := cfg.ValueString(storage.ConfigStorageDir)
46
if !ok || storageDir == "" {
47
return errors.New("storage directory not specified")
52
// VolumeSource is defined on the Provider interface.
53
func (p *tmpfsProvider) VolumeSource(providerConfig *storage.Config) (storage.VolumeSource, error) {
54
return nil, errors.NotSupportedf("volumes")
57
// FilesystemSource is defined on the Provider interface.
58
func (p *tmpfsProvider) FilesystemSource(sourceConfig *storage.Config) (storage.FilesystemSource, error) {
59
if err := p.validateFullConfig(sourceConfig); err != nil {
62
// storageDir is validated by validateFullConfig.
63
storageDir, _ := sourceConfig.ValueString(storage.ConfigStorageDir)
64
return &tmpfsFilesystemSource{
71
// Supports is defined on the Provider interface.
72
func (*tmpfsProvider) Supports(k storage.StorageKind) bool {
73
return k == storage.StorageKindFilesystem
76
// Scope is defined on the Provider interface.
77
func (*tmpfsProvider) Scope() storage.Scope {
78
return storage.ScopeMachine
81
// Dynamic is defined on the Provider interface.
82
func (*tmpfsProvider) Dynamic() bool {
86
// DefaultPools is defined on the Provider interface.
87
func (*tmpfsProvider) DefaultPools() []*storage.Config {
91
type tmpfsFilesystemSource struct {
97
var _ storage.FilesystemSource = (*tmpfsFilesystemSource)(nil)
99
// ValidateFilesystemParams is defined on the FilesystemSource interface.
100
func (s *tmpfsFilesystemSource) ValidateFilesystemParams(params storage.FilesystemParams) error {
101
// ValidateFilesystemParams may be called on a machine other than the
102
// machine where the filesystem will be mounted, so we cannot check
103
// available size until we get to createFilesystem.
107
// CreateFilesystems is defined on the FilesystemSource interface.
108
func (s *tmpfsFilesystemSource) CreateFilesystems(args []storage.FilesystemParams) ([]storage.CreateFilesystemsResult, error) {
109
results := make([]storage.CreateFilesystemsResult, len(args))
110
for i, arg := range args {
111
filesystem, err := s.createFilesystem(arg)
113
results[i].Error = err
116
results[i].Filesystem = filesystem
121
var getpagesize = os.Getpagesize
123
func (s *tmpfsFilesystemSource) createFilesystem(params storage.FilesystemParams) (*storage.Filesystem, error) {
124
if err := s.ValidateFilesystemParams(params); err != nil {
125
return nil, errors.Trace(err)
127
// Align size to the page size in MiB.
128
sizeInMiB := params.Size
129
pageSizeInMiB := uint64(getpagesize()) / (1024 * 1024)
130
if pageSizeInMiB > 0 {
131
x := (sizeInMiB + pageSizeInMiB - 1)
132
sizeInMiB = x - x%pageSizeInMiB
135
info := storage.FilesystemInfo{
136
FilesystemId: params.Tag.String(),
140
// Creating the mount is the responsibility of AttachFilesystems.
141
// AttachFilesystems needs to know the size so it can pass it onto
142
// "mount"; write the size of the filesystem to a file in the
143
// storage directory.
144
if err := s.writeFilesystemInfo(params.Tag, info); err != nil {
148
return &storage.Filesystem{params.Tag, params.Volume, info}, nil
151
// DestroyFilesystems is defined on the FilesystemSource interface.
152
func (s *tmpfsFilesystemSource) DestroyFilesystems(filesystemIds []string) ([]error, error) {
153
// DestroyFilesystems is a no-op; there is nothing to destroy,
154
// since the filesystem is ephemeral and disappears once
156
return make([]error, len(filesystemIds)), nil
159
// AttachFilesystems is defined on the FilesystemSource interface.
160
func (s *tmpfsFilesystemSource) AttachFilesystems(args []storage.FilesystemAttachmentParams) ([]storage.AttachFilesystemsResult, error) {
161
results := make([]storage.AttachFilesystemsResult, len(args))
162
for i, arg := range args {
163
attachment, err := s.attachFilesystem(arg)
165
results[i].Error = err
168
results[i].FilesystemAttachment = attachment
173
func (s *tmpfsFilesystemSource) attachFilesystem(arg storage.FilesystemAttachmentParams) (*storage.FilesystemAttachment, error) {
176
return nil, errNoMountPoint
178
info, err := s.readFilesystemInfo(arg.Filesystem)
182
if err := ensureDir(s.dirFuncs, path); err != nil {
183
return nil, errors.Trace(err)
186
// Check if the mount already exists.
187
source, err := s.dirFuncs.mountPointSource(path)
189
return nil, errors.Trace(err)
191
if source != arg.Filesystem.String() {
192
if err := ensureEmptyDir(s.dirFuncs, path); err != nil {
195
options := fmt.Sprintf("size=%dm", info.Size)
200
"mount", "-t", "tmpfs", arg.Filesystem.String(), path, "-o", options,
203
return nil, errors.Annotate(err, "cannot mount tmpfs")
207
return &storage.FilesystemAttachment{
210
storage.FilesystemAttachmentInfo{
212
ReadOnly: arg.ReadOnly,
217
// DetachFilesystems is defined on the FilesystemSource interface.
218
func (s *tmpfsFilesystemSource) DetachFilesystems(args []storage.FilesystemAttachmentParams) ([]error, error) {
219
results := make([]error, len(args))
220
for i, arg := range args {
221
if err := maybeUnmount(s.run, s.dirFuncs, arg.Path); err != nil {
228
func (s *tmpfsFilesystemSource) writeFilesystemInfo(tag names.FilesystemTag, info storage.FilesystemInfo) error {
229
filename := s.filesystemInfoFile(tag)
230
if _, err := os.Stat(filename); err == nil {
231
return errors.Errorf("filesystem %v already exists", tag.Id())
233
if err := ensureDir(s.dirFuncs, filepath.Dir(filename)); err != nil {
234
return errors.Trace(err)
236
err := utils.WriteYaml(filename, filesystemInfo{&info.Size})
238
return errors.Annotate(err, "writing filesystem info to disk")
243
func (s *tmpfsFilesystemSource) readFilesystemInfo(tag names.FilesystemTag) (storage.FilesystemInfo, error) {
244
var info filesystemInfo
245
if err := utils.ReadYaml(s.filesystemInfoFile(tag), &info); err != nil {
246
return storage.FilesystemInfo{}, errors.Annotate(err, "reading filesystem info from disk")
248
if info.Size == nil {
249
return storage.FilesystemInfo{}, errors.New("invalid filesystem info: missing size")
251
return storage.FilesystemInfo{
252
FilesystemId: tag.String(),
257
func (s *tmpfsFilesystemSource) filesystemInfoFile(tag names.FilesystemTag) string {
258
return filepath.Join(s.storageDir, tag.Id()+".info")
261
type filesystemInfo struct {
262
Size *uint64 `yaml:"size,omitempty"`