github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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 "github.com/juju/names" 13 jc "github.com/juju/testing/checkers" 14 gc "gopkg.in/check.v1" 15 "gopkg.in/juju/charm.v6-unstable/hooks" 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 ".*": missing 'attached'`) 80 } 81 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) 87 } 88 89 func (s *stateSuite) TestReadAllStateFilesDirEmpty(c *gc.C) { 90 dir := c.MkDir() 91 states, err := storage.ReadAllStateFiles(dir) 92 c.Assert(err, jc.ErrorIsNil) 93 c.Assert(states, gc.HasLen, 0) 94 } 95 96 func (s *stateSuite) TestReadStateFileFileNotExist(c *gc.C) { 97 dir := c.MkDir() 98 state, err := storage.ReadStateFile(dir, names.NewStorageTag("data/0")) 99 c.Assert(err, jc.ErrorIsNil) 100 c.Assert(state, gc.NotNil) 101 102 data, err := ioutil.ReadFile(filepath.Join(dir, "data-0")) 103 c.Assert(err, jc.Satisfies, os.IsNotExist) 104 105 err = state.CommitHook(hook.Info{ 106 Kind: hooks.StorageAttached, 107 StorageId: "data-0", 108 }) 109 c.Assert(err, jc.ErrorIsNil) 110 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") 114 } 115 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) 121 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, 127 StorageId: "data-0", 128 }) 129 c.Assert(errors.Cause(err), jc.Satisfies, os.IsNotExist) 130 } 131 132 func (s *stateSuite) TestReadStateFileBadFormat(c *gc.C) { 133 dir := c.MkDir() 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`) 137 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'`) 141 } 142 143 func (s *stateSuite) TestCommitHook(c *gc.C) { 144 dir := c.MkDir() 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") 149 150 // CommitHook must be idempotent, so test each operation 151 // twice in a row. 152 153 for i := 0; i < 2; i++ { 154 err := state.CommitHook(hook.Info{ 155 Kind: hooks.StorageAttached, 156 StorageId: "data-0", 157 }) 158 c.Assert(err, jc.ErrorIsNil) 159 c.Assert(stateFile, jc.IsNonEmptyFile) 160 } 161 162 for i := 0; i < 2; i++ { 163 err := state.CommitHook(hook.Info{ 164 Kind: hooks.StorageDetaching, 165 StorageId: "data-0", 166 }) 167 c.Assert(err, jc.ErrorIsNil) 168 c.Assert(stateFile, jc.DoesNotExist) 169 } 170 } 171 172 func (s *stateSuite) TestValidateHook(c *gc.C) { 173 const unattached = false 174 const attached = true 175 176 err := storage.ValidateHook( 177 names.NewStorageTag("data/0"), unattached, 178 hook.Info{Kind: hooks.StorageAttached, StorageId: "data/1"}, 179 ) 180 c.Assert(err, gc.ErrorMatches, `inappropriate "storage-attached" hook for storage "data/0": expected storage "data/0", got storage "data/1"`) 181 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"}, 186 ) 187 } 188 assertValidates := func(attached bool, kind hooks.Kind) { 189 err := validate(attached, kind) 190 c.Assert(err, jc.ErrorIsNil) 191 } 192 assertValidateFails := func(attached bool, kind hooks.Kind, expect string) { 193 err := validate(attached, kind) 194 c.Assert(err, gc.ErrorMatches, expect) 195 } 196 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`) 201 }