1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
11
"github.com/juju/errors"
12
"github.com/juju/names"
13
jc "github.com/juju/testing/checkers"
14
gc "gopkg.in/check.v1"
15
"gopkg.in/juju/charm.v5/hooks"
17
"github.com/juju/juju/testing"
18
"github.com/juju/juju/worker/uniter/hook"
19
"github.com/juju/juju/worker/uniter/storage"
22
type stateSuite struct {
26
var _ = gc.Suite(&stateSuite{})
28
func writeFile(c *gc.C, path string, content string) {
29
err := ioutil.WriteFile(path, []byte(content), 0644)
30
c.Assert(err, jc.ErrorIsNil)
33
func assertFileNotExist(c *gc.C, path string) {
34
_, err := os.Stat(path)
35
c.Assert(err, jc.Satisfies, os.IsNotExist)
38
func (s *stateSuite) TestReadAllStateFiles(c *gc.C) {
40
writeFile(c, filepath.Join(dir, "data-0"), "attached: true")
41
// We don't currently ever write a file with attached=false,
42
// but test it for coverage in case of required changes.
43
writeFile(c, filepath.Join(dir, "data-1"), "attached: false")
45
states, err := storage.ReadAllStateFiles(dir)
46
c.Assert(err, jc.ErrorIsNil)
47
c.Assert(states, gc.HasLen, 2)
49
state, ok := states[names.NewStorageTag("data/0")]
50
c.Assert(ok, jc.IsTrue)
51
c.Assert(storage.StateAttached(state), jc.IsTrue)
53
state, ok = states[names.NewStorageTag("data/1")]
54
c.Assert(ok, jc.IsTrue)
55
c.Assert(storage.StateAttached(state), jc.IsFalse)
58
func (s *stateSuite) TestReadAllStateFilesJunk(c *gc.C) {
60
writeFile(c, filepath.Join(dir, "data-0"), "attached: true")
61
// data-extra-1 is not a valid storage ID, so it will
62
// be ignored by ReadAllStateFiles.
63
writeFile(c, filepath.Join(dir, "data-extra-1"), "attached: false")
64
// subdirs are ignored.
65
err := os.Mkdir(filepath.Join(dir, "data-1"), 0755)
66
c.Assert(err, jc.ErrorIsNil)
68
states, err := storage.ReadAllStateFiles(dir)
69
c.Assert(err, jc.ErrorIsNil)
70
c.Assert(states, gc.HasLen, 1)
71
_, ok := states[names.NewStorageTag("data/0")]
72
c.Assert(ok, jc.IsTrue)
75
func (s *stateSuite) TestReadAllStateFilesOneBadApple(c *gc.C) {
77
writeFile(c, filepath.Join(dir, "data-0"), "rubbish")
78
_, err := storage.ReadAllStateFiles(dir)
79
c.Assert(err, gc.ErrorMatches, `cannot load storage state from ".*": cannot load storage "data/0" state from ".*": invalid storage state file ".*": missing 'attached'`)
82
func (s *stateSuite) TestReadAllStateFilesDirNotExist(c *gc.C) {
83
dir := filepath.Join(c.MkDir(), "doesnotexist")
84
states, err := storage.ReadAllStateFiles(dir)
85
c.Assert(err, jc.ErrorIsNil)
86
c.Assert(states, gc.HasLen, 0)
89
func (s *stateSuite) TestReadAllStateFilesDirEmpty(c *gc.C) {
91
states, err := storage.ReadAllStateFiles(dir)
92
c.Assert(err, jc.ErrorIsNil)
93
c.Assert(states, gc.HasLen, 0)
96
func (s *stateSuite) TestReadStateFileFileNotExist(c *gc.C) {
98
state, err := storage.ReadStateFile(dir, names.NewStorageTag("data/0"))
99
c.Assert(err, jc.ErrorIsNil)
100
c.Assert(state, gc.NotNil)
102
data, err := ioutil.ReadFile(filepath.Join(dir, "data-0"))
103
c.Assert(err, jc.Satisfies, os.IsNotExist)
105
err = state.CommitHook(hook.Info{
106
Kind: hooks.StorageAttached,
109
c.Assert(err, jc.ErrorIsNil)
111
data, err = ioutil.ReadFile(filepath.Join(dir, "data-0"))
112
c.Assert(err, jc.ErrorIsNil)
113
c.Assert(string(data), gc.Equals, "attached: true\n")
116
func (s *stateSuite) TestReadStateFileDirNotExist(c *gc.C) {
117
dir := filepath.Join(c.MkDir(), "doesnotexist")
118
state, err := storage.ReadStateFile(dir, names.NewStorageTag("data/0"))
119
c.Assert(err, jc.ErrorIsNil)
120
c.Assert(state, gc.NotNil)
122
// CommitHook will fail if the directory does not exist. The uniter
123
// must ensure the directory is created before committing any hooks
124
// to the storage state.
125
err = state.CommitHook(hook.Info{
126
Kind: hooks.StorageAttached,
129
c.Assert(errors.Cause(err), jc.Satisfies, os.IsNotExist)
132
func (s *stateSuite) TestReadStateFileBadFormat(c *gc.C) {
134
writeFile(c, filepath.Join(dir, "data-0"), "!@#")
135
_, err := storage.ReadStateFile(dir, names.NewStorageTag("data/0"))
136
c.Assert(err, gc.ErrorMatches, `cannot load storage "data/0" state from ".*": invalid storage state file ".*": YAML error: did not find expected whitespace or line break`)
138
writeFile(c, filepath.Join(dir, "data-0"), "icantbelieveitsnotattached: true\n")
139
_, err = storage.ReadStateFile(dir, names.NewStorageTag("data/0"))
140
c.Assert(err, gc.ErrorMatches, `cannot load storage "data/0" state from ".*": invalid storage state file ".*": missing 'attached'`)
143
func (s *stateSuite) TestCommitHook(c *gc.C) {
145
state, err := storage.ReadStateFile(dir, names.NewStorageTag("data/0"))
146
c.Assert(err, jc.ErrorIsNil)
147
c.Assert(state, gc.NotNil)
148
stateFile := filepath.Join(dir, "data-0")
150
// CommitHook must be idempotent, so test each operation
153
for i := 0; i < 2; i++ {
154
err := state.CommitHook(hook.Info{
155
Kind: hooks.StorageAttached,
158
c.Assert(err, jc.ErrorIsNil)
159
c.Assert(stateFile, jc.IsNonEmptyFile)
162
for i := 0; i < 2; i++ {
163
err := state.CommitHook(hook.Info{
164
Kind: hooks.StorageDetaching,
167
c.Assert(err, jc.ErrorIsNil)
168
c.Assert(stateFile, jc.DoesNotExist)
172
func (s *stateSuite) TestValidateHook(c *gc.C) {
173
const unattached = false
174
const attached = true
176
err := storage.ValidateHook(
177
names.NewStorageTag("data/0"), unattached,
178
hook.Info{Kind: hooks.StorageAttached, StorageId: "data/1"},
180
c.Assert(err, gc.ErrorMatches, `inappropriate "storage-attached" hook for storage "data/0": expected storage "data/0", got storage "data/1"`)
182
validate := func(attached bool, kind hooks.Kind) error {
183
return storage.ValidateHook(
184
names.NewStorageTag("data/0"), attached,
185
hook.Info{Kind: kind, StorageId: "data/0"},
188
assertValidates := func(attached bool, kind hooks.Kind) {
189
err := validate(attached, kind)
190
c.Assert(err, jc.ErrorIsNil)
192
assertValidateFails := func(attached bool, kind hooks.Kind, expect string) {
193
err := validate(attached, kind)
194
c.Assert(err, gc.ErrorMatches, expect)
197
assertValidates(false, hooks.StorageAttached)
198
assertValidates(true, hooks.StorageDetaching)
199
assertValidateFails(false, hooks.StorageDetaching, `inappropriate "storage-detaching" hook for storage "data/0": storage not attached`)
200
assertValidateFails(true, hooks.StorageAttached, `inappropriate "storage-attached" hook for storage "data/0": storage already attached`)