github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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/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 (s *stateSuite) TestReadAllStateFiles(c *gc.C) { 34 dir := c.MkDir() 35 writeFile(c, filepath.Join(dir, "data-0"), "attached: true") 36 // We don't currently ever write a file with attached=false, 37 // but test it for coverage in case of required changes. 38 writeFile(c, filepath.Join(dir, "data-1"), "attached: false") 39 40 states, err := storage.ReadAllStateFiles(dir) 41 c.Assert(err, jc.ErrorIsNil) 42 c.Assert(states, gc.HasLen, 2) 43 44 state, ok := states[names.NewStorageTag("data/0")] 45 c.Assert(ok, jc.IsTrue) 46 c.Assert(storage.StateAttached(state), jc.IsTrue) 47 48 state, ok = states[names.NewStorageTag("data/1")] 49 c.Assert(ok, jc.IsTrue) 50 c.Assert(storage.StateAttached(state), jc.IsFalse) 51 } 52 53 func (s *stateSuite) TestReadAllStateFilesJunk(c *gc.C) { 54 dir := c.MkDir() 55 writeFile(c, filepath.Join(dir, "data-0"), "attached: true") 56 writeFile(c, filepath.Join(dir, "data-hyphen-1"), "attached: true") 57 // data_underscore-2 is not a valid storage ID, so it will 58 // be ignored by ReadAllStateFiles. 59 writeFile(c, filepath.Join(dir, "data_underscore-2"), "attached: false") 60 // subdirs are ignored. 61 err := os.Mkdir(filepath.Join(dir, "data-1"), 0755) 62 c.Assert(err, jc.ErrorIsNil) 63 64 states, err := storage.ReadAllStateFiles(dir) 65 c.Assert(err, jc.ErrorIsNil) 66 c.Assert(states, gc.HasLen, 2) 67 _, ok := states[names.NewStorageTag("data/0")] 68 c.Assert(ok, jc.IsTrue) 69 _, ok = states[names.NewStorageTag("data-hyphen/1")] 70 c.Assert(ok, jc.IsTrue) 71 } 72 73 func (s *stateSuite) TestReadAllStateFilesOneBadApple(c *gc.C) { 74 dir := c.MkDir() 75 writeFile(c, filepath.Join(dir, "data-0"), "rubbish") 76 _, err := storage.ReadAllStateFiles(dir) 77 c.Assert(err, gc.ErrorMatches, `cannot load storage state from ".*": cannot load storage "data/0" state from ".*": invalid storage state file ".*": yaml: unmarshal errors: 78 `+" line 1: cannot unmarshal !!str `rubbish` into storage.diskInfo") 79 } 80 81 func (s *stateSuite) TestReadAllStateFilesDirNotExist(c *gc.C) { 82 dir := filepath.Join(c.MkDir(), "doesnotexist") 83 states, err := storage.ReadAllStateFiles(dir) 84 c.Assert(err, jc.ErrorIsNil) 85 c.Assert(states, gc.HasLen, 0) 86 } 87 88 func (s *stateSuite) TestReadAllStateFilesDirEmpty(c *gc.C) { 89 dir := c.MkDir() 90 states, err := storage.ReadAllStateFiles(dir) 91 c.Assert(err, jc.ErrorIsNil) 92 c.Assert(states, gc.HasLen, 0) 93 } 94 95 func (s *stateSuite) TestReadStateFileFileNotExist(c *gc.C) { 96 dir := c.MkDir() 97 state, err := storage.ReadStateFile(dir, names.NewStorageTag("data/0")) 98 c.Assert(err, jc.ErrorIsNil) 99 c.Assert(state, gc.NotNil) 100 101 data, err := ioutil.ReadFile(filepath.Join(dir, "data-0")) 102 c.Assert(err, jc.Satisfies, os.IsNotExist) 103 104 err = state.CommitHook(hook.Info{ 105 Kind: hooks.StorageAttached, 106 StorageId: "data-0", 107 }) 108 c.Assert(err, jc.ErrorIsNil) 109 110 data, err = ioutil.ReadFile(filepath.Join(dir, "data-0")) 111 c.Assert(err, jc.ErrorIsNil) 112 c.Assert(string(data), gc.Equals, "attached: true\n") 113 } 114 115 func (s *stateSuite) TestReadStateFileDirNotExist(c *gc.C) { 116 dir := filepath.Join(c.MkDir(), "doesnotexist") 117 state, err := storage.ReadStateFile(dir, names.NewStorageTag("data/0")) 118 c.Assert(err, jc.ErrorIsNil) 119 c.Assert(state, gc.NotNil) 120 121 // CommitHook will fail if the directory does not exist. The uniter 122 // must ensure the directory is created before committing any hooks 123 // to the storage state. 124 err = state.CommitHook(hook.Info{ 125 Kind: hooks.StorageAttached, 126 StorageId: "data-0", 127 }) 128 c.Assert(errors.Cause(err), jc.Satisfies, os.IsNotExist) 129 } 130 131 func (s *stateSuite) TestReadStateFileBadFormat(c *gc.C) { 132 dir := c.MkDir() 133 writeFile(c, filepath.Join(dir, "data-0"), "!@#") 134 _, err := storage.ReadStateFile(dir, names.NewStorageTag("data/0")) 135 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`) 136 137 writeFile(c, filepath.Join(dir, "data-0"), "icantbelieveitsnotattached: true\n") 138 _, err = storage.ReadStateFile(dir, names.NewStorageTag("data/0")) 139 c.Assert(err, gc.ErrorMatches, `cannot load storage "data/0" state from ".*": invalid storage state file ".*": missing 'attached'`) 140 } 141 142 func (s *stateSuite) TestCommitHook(c *gc.C) { 143 dir := c.MkDir() 144 state, err := storage.ReadStateFile(dir, names.NewStorageTag("data/0")) 145 c.Assert(err, jc.ErrorIsNil) 146 c.Assert(state, gc.NotNil) 147 stateFile := filepath.Join(dir, "data-0") 148 149 // CommitHook must be idempotent, so test each operation 150 // twice in a row. 151 152 for i := 0; i < 2; i++ { 153 err := state.CommitHook(hook.Info{ 154 Kind: hooks.StorageAttached, 155 StorageId: "data-0", 156 }) 157 c.Assert(err, jc.ErrorIsNil) 158 c.Assert(stateFile, jc.IsNonEmptyFile) 159 } 160 161 for i := 0; i < 2; i++ { 162 err := state.CommitHook(hook.Info{ 163 Kind: hooks.StorageDetaching, 164 StorageId: "data-0", 165 }) 166 c.Assert(err, jc.ErrorIsNil) 167 c.Assert(stateFile, jc.DoesNotExist) 168 } 169 } 170 171 func (s *stateSuite) TestValidateHook(c *gc.C) { 172 const unattached = false 173 const attached = true 174 175 err := storage.ValidateHook( 176 names.NewStorageTag("data/0"), unattached, 177 hook.Info{Kind: hooks.StorageAttached, StorageId: "data/1"}, 178 ) 179 c.Assert(err, gc.ErrorMatches, `inappropriate "storage-attached" hook for storage "data/0": expected storage "data/0", got storage "data/1"`) 180 181 validate := func(attached bool, kind hooks.Kind) error { 182 return storage.ValidateHook( 183 names.NewStorageTag("data/0"), attached, 184 hook.Info{Kind: kind, StorageId: "data/0"}, 185 ) 186 } 187 assertValidates := func(attached bool, kind hooks.Kind) { 188 err := validate(attached, kind) 189 c.Assert(err, jc.ErrorIsNil) 190 } 191 assertValidateFails := func(attached bool, kind hooks.Kind, expect string) { 192 err := validate(attached, kind) 193 c.Assert(err, gc.ErrorMatches, expect) 194 } 195 196 assertValidates(false, hooks.StorageAttached) 197 assertValidates(true, hooks.StorageDetaching) 198 assertValidateFails(false, hooks.StorageDetaching, `inappropriate "storage-detaching" hook for storage "data/0": storage not attached`) 199 assertValidateFails(true, hooks.StorageAttached, `inappropriate "storage-attached" hook for storage "data/0": storage already attached`) 200 }