github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/uniter/storage/state_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package storage_test 5 6 import ( 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 11 "github.com/juju/errors" 12 jc "github.com/juju/testing/checkers" 13 gc "gopkg.in/check.v1" 14 "gopkg.in/juju/charm.v6-unstable/hooks" 15 "gopkg.in/juju/names.v2" 16 17 "github.com/juju/juju/testing" 18 "github.com/juju/juju/worker/uniter/hook" 19 "github.com/juju/juju/worker/uniter/storage" 20 ) 21 22 type stateSuite struct { 23 testing.BaseSuite 24 } 25 26 var _ = gc.Suite(&stateSuite{}) 27 28 func writeFile(c *gc.C, path string, content string) { 29 err := ioutil.WriteFile(path, []byte(content), 0644) 30 c.Assert(err, jc.ErrorIsNil) 31 } 32 33 func assertFileNotExist(c *gc.C, path string) { 34 _, err := os.Stat(path) 35 c.Assert(err, jc.Satisfies, os.IsNotExist) 36 } 37 38 func (s *stateSuite) TestReadAllStateFiles(c *gc.C) { 39 dir := c.MkDir() 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") 44 45 states, err := storage.ReadAllStateFiles(dir) 46 c.Assert(err, jc.ErrorIsNil) 47 c.Assert(states, gc.HasLen, 2) 48 49 state, ok := states[names.NewStorageTag("data/0")] 50 c.Assert(ok, jc.IsTrue) 51 c.Assert(storage.StateAttached(state), jc.IsTrue) 52 53 state, ok = states[names.NewStorageTag("data/1")] 54 c.Assert(ok, jc.IsTrue) 55 c.Assert(storage.StateAttached(state), jc.IsFalse) 56 } 57 58 func (s *stateSuite) TestReadAllStateFilesJunk(c *gc.C) { 59 dir := c.MkDir() 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) 67 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) 73 } 74 75 func (s *stateSuite) TestReadAllStateFilesOneBadApple(c *gc.C) { 76 dir := c.MkDir() 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 ".*": yaml: unmarshal errors: 80 `+" line 1: cannot unmarshal !!str `rubbish` into storage.diskInfo") 81 } 82 83 func (s *stateSuite) TestReadAllStateFilesDirNotExist(c *gc.C) { 84 dir := filepath.Join(c.MkDir(), "doesnotexist") 85 states, err := storage.ReadAllStateFiles(dir) 86 c.Assert(err, jc.ErrorIsNil) 87 c.Assert(states, gc.HasLen, 0) 88 } 89 90 func (s *stateSuite) TestReadAllStateFilesDirEmpty(c *gc.C) { 91 dir := c.MkDir() 92 states, err := storage.ReadAllStateFiles(dir) 93 c.Assert(err, jc.ErrorIsNil) 94 c.Assert(states, gc.HasLen, 0) 95 } 96 97 func (s *stateSuite) TestReadStateFileFileNotExist(c *gc.C) { 98 dir := c.MkDir() 99 state, err := storage.ReadStateFile(dir, names.NewStorageTag("data/0")) 100 c.Assert(err, jc.ErrorIsNil) 101 c.Assert(state, gc.NotNil) 102 103 data, err := ioutil.ReadFile(filepath.Join(dir, "data-0")) 104 c.Assert(err, jc.Satisfies, os.IsNotExist) 105 106 err = state.CommitHook(hook.Info{ 107 Kind: hooks.StorageAttached, 108 StorageId: "data-0", 109 }) 110 c.Assert(err, jc.ErrorIsNil) 111 112 data, err = ioutil.ReadFile(filepath.Join(dir, "data-0")) 113 c.Assert(err, jc.ErrorIsNil) 114 c.Assert(string(data), gc.Equals, "attached: true\n") 115 } 116 117 func (s *stateSuite) TestReadStateFileDirNotExist(c *gc.C) { 118 dir := filepath.Join(c.MkDir(), "doesnotexist") 119 state, err := storage.ReadStateFile(dir, names.NewStorageTag("data/0")) 120 c.Assert(err, jc.ErrorIsNil) 121 c.Assert(state, gc.NotNil) 122 123 // CommitHook will fail if the directory does not exist. The uniter 124 // must ensure the directory is created before committing any hooks 125 // to the storage state. 126 err = state.CommitHook(hook.Info{ 127 Kind: hooks.StorageAttached, 128 StorageId: "data-0", 129 }) 130 c.Assert(errors.Cause(err), jc.Satisfies, os.IsNotExist) 131 } 132 133 func (s *stateSuite) TestReadStateFileBadFormat(c *gc.C) { 134 dir := c.MkDir() 135 writeFile(c, filepath.Join(dir, "data-0"), "!@#") 136 _, err := storage.ReadStateFile(dir, names.NewStorageTag("data/0")) 137 c.Assert(err, gc.ErrorMatches, `cannot load storage "data/0" state from ".*": invalid storage state file ".*": yaml: did not find expected whitespace or line break`) 138 139 writeFile(c, filepath.Join(dir, "data-0"), "icantbelieveitsnotattached: true\n") 140 _, err = storage.ReadStateFile(dir, names.NewStorageTag("data/0")) 141 c.Assert(err, gc.ErrorMatches, `cannot load storage "data/0" state from ".*": invalid storage state file ".*": missing 'attached'`) 142 } 143 144 func (s *stateSuite) TestCommitHook(c *gc.C) { 145 dir := c.MkDir() 146 state, err := storage.ReadStateFile(dir, names.NewStorageTag("data/0")) 147 c.Assert(err, jc.ErrorIsNil) 148 c.Assert(state, gc.NotNil) 149 stateFile := filepath.Join(dir, "data-0") 150 151 // CommitHook must be idempotent, so test each operation 152 // twice in a row. 153 154 for i := 0; i < 2; i++ { 155 err := state.CommitHook(hook.Info{ 156 Kind: hooks.StorageAttached, 157 StorageId: "data-0", 158 }) 159 c.Assert(err, jc.ErrorIsNil) 160 c.Assert(stateFile, jc.IsNonEmptyFile) 161 } 162 163 for i := 0; i < 2; i++ { 164 err := state.CommitHook(hook.Info{ 165 Kind: hooks.StorageDetaching, 166 StorageId: "data-0", 167 }) 168 c.Assert(err, jc.ErrorIsNil) 169 c.Assert(stateFile, jc.DoesNotExist) 170 } 171 } 172 173 func (s *stateSuite) TestValidateHook(c *gc.C) { 174 const unattached = false 175 const attached = true 176 177 err := storage.ValidateHook( 178 names.NewStorageTag("data/0"), unattached, 179 hook.Info{Kind: hooks.StorageAttached, StorageId: "data/1"}, 180 ) 181 c.Assert(err, gc.ErrorMatches, `inappropriate "storage-attached" hook for storage "data/0": expected storage "data/0", got storage "data/1"`) 182 183 validate := func(attached bool, kind hooks.Kind) error { 184 return storage.ValidateHook( 185 names.NewStorageTag("data/0"), attached, 186 hook.Info{Kind: kind, StorageId: "data/0"}, 187 ) 188 } 189 assertValidates := func(attached bool, kind hooks.Kind) { 190 err := validate(attached, kind) 191 c.Assert(err, jc.ErrorIsNil) 192 } 193 assertValidateFails := func(attached bool, kind hooks.Kind, expect string) { 194 err := validate(attached, kind) 195 c.Assert(err, gc.ErrorMatches, expect) 196 } 197 198 assertValidates(false, hooks.StorageAttached) 199 assertValidates(true, hooks.StorageDetaching) 200 assertValidateFails(false, hooks.StorageDetaching, `inappropriate "storage-detaching" hook for storage "data/0": storage not attached`) 201 assertValidateFails(true, hooks.StorageAttached, `inappropriate "storage-attached" hook for storage "data/0": storage already attached`) 202 }