~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/storage/provider/tmpfs.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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/utils"
 
13
        "gopkg.in/juju/names.v2"
 
14
 
 
15
        "github.com/juju/juju/storage"
 
16
)
 
17
 
 
18
const (
 
19
        TmpfsProviderType = storage.ProviderType("tmpfs")
 
20
)
 
21
 
 
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.
 
25
        run runCommandFunc
 
26
}
 
27
 
 
28
var (
 
29
        _ storage.Provider = (*tmpfsProvider)(nil)
 
30
)
 
31
 
 
32
// ValidateConfig is defined on the Provider interface.
 
33
func (p *tmpfsProvider) ValidateConfig(cfg *storage.Config) error {
 
34
        // Tmpfs provider has no configuration.
 
35
        return nil
 
36
}
 
37
 
 
38
// validateFullConfig validates a fully-constructed storage config,
 
39
// combining the user-specified config and any internally specified
 
40
// config.
 
41
func (p *tmpfsProvider) validateFullConfig(cfg *storage.Config) error {
 
42
        if err := p.ValidateConfig(cfg); err != nil {
 
43
                return err
 
44
        }
 
45
        storageDir, ok := cfg.ValueString(storage.ConfigStorageDir)
 
46
        if !ok || storageDir == "" {
 
47
                return errors.New("storage directory not specified")
 
48
        }
 
49
        return nil
 
50
}
 
51
 
 
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")
 
55
}
 
56
 
 
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 {
 
60
                return nil, err
 
61
        }
 
62
        // storageDir is validated by validateFullConfig.
 
63
        storageDir, _ := sourceConfig.ValueString(storage.ConfigStorageDir)
 
64
        return &tmpfsFilesystemSource{
 
65
                &osDirFuncs{p.run},
 
66
                p.run,
 
67
                storageDir,
 
68
        }, nil
 
69
}
 
70
 
 
71
// Supports is defined on the Provider interface.
 
72
func (*tmpfsProvider) Supports(k storage.StorageKind) bool {
 
73
        return k == storage.StorageKindFilesystem
 
74
}
 
75
 
 
76
// Scope is defined on the Provider interface.
 
77
func (*tmpfsProvider) Scope() storage.Scope {
 
78
        return storage.ScopeMachine
 
79
}
 
80
 
 
81
// Dynamic is defined on the Provider interface.
 
82
func (*tmpfsProvider) Dynamic() bool {
 
83
        return true
 
84
}
 
85
 
 
86
// DefaultPools is defined on the Provider interface.
 
87
func (*tmpfsProvider) DefaultPools() []*storage.Config {
 
88
        return nil
 
89
}
 
90
 
 
91
type tmpfsFilesystemSource struct {
 
92
        dirFuncs   dirFuncs
 
93
        run        runCommandFunc
 
94
        storageDir string
 
95
}
 
96
 
 
97
var _ storage.FilesystemSource = (*tmpfsFilesystemSource)(nil)
 
98
 
 
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.
 
104
        return nil
 
105
}
 
106
 
 
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)
 
112
                if err != nil {
 
113
                        results[i].Error = err
 
114
                        continue
 
115
                }
 
116
                results[i].Filesystem = filesystem
 
117
        }
 
118
        return results, nil
 
119
}
 
120
 
 
121
var getpagesize = os.Getpagesize
 
122
 
 
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)
 
126
        }
 
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
 
133
        }
 
134
 
 
135
        info := storage.FilesystemInfo{
 
136
                FilesystemId: params.Tag.String(),
 
137
                Size:         sizeInMiB,
 
138
        }
 
139
 
 
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 {
 
145
                return nil, err
 
146
        }
 
147
 
 
148
        return &storage.Filesystem{params.Tag, params.Volume, info}, nil
 
149
}
 
150
 
 
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
 
155
        // detached.
 
156
        return make([]error, len(filesystemIds)), nil
 
157
}
 
158
 
 
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)
 
164
                if err != nil {
 
165
                        results[i].Error = err
 
166
                        continue
 
167
                }
 
168
                results[i].FilesystemAttachment = attachment
 
169
        }
 
170
        return results, nil
 
171
}
 
172
 
 
173
func (s *tmpfsFilesystemSource) attachFilesystem(arg storage.FilesystemAttachmentParams) (*storage.FilesystemAttachment, error) {
 
174
        path := arg.Path
 
175
        if path == "" {
 
176
                return nil, errNoMountPoint
 
177
        }
 
178
        info, err := s.readFilesystemInfo(arg.Filesystem)
 
179
        if err != nil {
 
180
                return nil, err
 
181
        }
 
182
        if err := ensureDir(s.dirFuncs, path); err != nil {
 
183
                return nil, errors.Trace(err)
 
184
        }
 
185
 
 
186
        // Check if the mount already exists.
 
187
        source, err := s.dirFuncs.mountPointSource(path)
 
188
        if err != nil {
 
189
                return nil, errors.Trace(err)
 
190
        }
 
191
        if source != arg.Filesystem.String() {
 
192
                if err := ensureEmptyDir(s.dirFuncs, path); err != nil {
 
193
                        return nil, err
 
194
                }
 
195
                options := fmt.Sprintf("size=%dm", info.Size)
 
196
                if arg.ReadOnly {
 
197
                        options += ",ro"
 
198
                }
 
199
                if _, err := s.run(
 
200
                        "mount", "-t", "tmpfs", arg.Filesystem.String(), path, "-o", options,
 
201
                ); err != nil {
 
202
                        os.Remove(path)
 
203
                        return nil, errors.Annotate(err, "cannot mount tmpfs")
 
204
                }
 
205
        }
 
206
 
 
207
        return &storage.FilesystemAttachment{
 
208
                arg.Filesystem,
 
209
                arg.Machine,
 
210
                storage.FilesystemAttachmentInfo{
 
211
                        Path:     path,
 
212
                        ReadOnly: arg.ReadOnly,
 
213
                },
 
214
        }, nil
 
215
}
 
216
 
 
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 {
 
222
                        results[i] = err
 
223
                }
 
224
        }
 
225
        return results, nil
 
226
}
 
227
 
 
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())
 
232
        }
 
233
        if err := ensureDir(s.dirFuncs, filepath.Dir(filename)); err != nil {
 
234
                return errors.Trace(err)
 
235
        }
 
236
        err := utils.WriteYaml(filename, filesystemInfo{&info.Size})
 
237
        if err != nil {
 
238
                return errors.Annotate(err, "writing filesystem info to disk")
 
239
        }
 
240
        return err
 
241
}
 
242
 
 
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")
 
247
        }
 
248
        if info.Size == nil {
 
249
                return storage.FilesystemInfo{}, errors.New("invalid filesystem info: missing size")
 
250
        }
 
251
        return storage.FilesystemInfo{
 
252
                FilesystemId: tag.String(),
 
253
                Size:         *info.Size,
 
254
        }, nil
 
255
}
 
256
 
 
257
func (s *tmpfsFilesystemSource) filesystemInfoFile(tag names.FilesystemTag) string {
 
258
        return filepath.Join(s.storageDir, tag.Id()+".info")
 
259
}
 
260
 
 
261
type filesystemInfo struct {
 
262
        Size *uint64 `yaml:"size,omitempty"`
 
263
}