1
// Copyright 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
4
package configstore_test
14
"github.com/juju/errors"
15
"github.com/juju/loggo"
16
jc "github.com/juju/testing/checkers"
17
gc "gopkg.in/check.v1"
19
"github.com/juju/juju/environs/configstore"
20
"github.com/juju/juju/testing"
23
var _ = gc.Suite(&diskInterfaceSuite{})
25
type diskInterfaceSuite struct {
30
func (s *diskInterfaceSuite) SetUpTest(c *gc.C) {
31
s.interfaceSuite.SetUpTest(c)
33
s.NewStore = func(c *gc.C) configstore.Storage {
34
store, err := configstore.NewDisk(s.dir)
35
c.Assert(err, jc.ErrorIsNil)
40
// storePath returns the path to the environment info
41
// for the named environment in the given directory.
42
// If envName is empty, it returns the path
43
// to the info files' containing directory.
44
func storePath(dir string, envName string) string {
45
path := filepath.Join(dir, "models")
47
path = filepath.Join(path, envName+".jenv")
52
func (s *diskInterfaceSuite) TearDownTest(c *gc.C) {
54
// Check that no stray temp files have been left behind
55
entries, err := ioutil.ReadDir(storePath(s.dir, ""))
56
c.Assert(err, jc.ErrorIsNil)
57
for _, entry := range entries {
58
if !strings.HasSuffix(entry.Name(), ".jenv") && entry.Name() != "cache.yaml" {
59
c.Errorf("found possible stray temp file %s, %q", s.dir, entry.Name())
62
s.interfaceSuite.TearDownTest(c)
65
var _ = gc.Suite(&diskStoreSuite{})
67
type diskStoreSuite struct {
71
func (*diskStoreSuite) TestNewDisk(c *gc.C) {
73
store, err := configstore.NewDisk(filepath.Join(dir, "foo"))
74
c.Assert(err, jc.Satisfies, os.IsNotExist)
75
c.Assert(store, gc.IsNil)
77
store, err = configstore.NewDisk(filepath.Join(dir))
78
c.Assert(err, jc.ErrorIsNil)
79
c.Assert(store, gc.NotNil)
99
func (*diskStoreSuite) TestRead(c *gc.C) {
101
err := os.Mkdir(storePath(dir, ""), 0700)
102
c.Assert(err, jc.ErrorIsNil)
103
err = ioutil.WriteFile(storePath(dir, "somemodel"), []byte(sampleInfo), 0666)
104
c.Assert(err, jc.ErrorIsNil)
105
store, err := configstore.NewDisk(dir)
106
c.Assert(err, jc.ErrorIsNil)
107
info, err := store.ReadInfo("somemodel")
108
c.Assert(err, jc.ErrorIsNil)
109
c.Assert(info.Initialized(), jc.IsTrue)
110
c.Assert(info.APICredentials(), gc.DeepEquals, configstore.APICredentials{
114
c.Assert(info.APIEndpoint(), gc.DeepEquals, configstore.APIEndpoint{
115
Addresses: []string{"10.0.0.1", "127.0.0.1"},
116
Hostnames: []string{"example.com", "kremvax.ru"},
117
CACert: "first line\nsecond line",
119
c.Assert(info.Location(), gc.Equals, fmt.Sprintf("file %q", filepath.Join(dir, "models", "somemodel.jenv")))
120
c.Assert(info.BootstrapConfig(), gc.DeepEquals, map[string]interface{}{
126
func (*diskStoreSuite) TestReadNotFound(c *gc.C) {
128
store, err := configstore.NewDisk(dir)
129
c.Assert(err, jc.ErrorIsNil)
130
info, err := store.ReadInfo("somemodel")
131
c.Assert(err, jc.Satisfies, errors.IsNotFound)
132
c.Assert(info, gc.IsNil)
135
func (*diskStoreSuite) TestWriteFails(c *gc.C) {
137
store, err := configstore.NewDisk(dir)
138
c.Assert(err, jc.ErrorIsNil)
140
info := store.CreateInfo("somemodel")
142
// Make the directory non-writable
143
err = os.Chmod(storePath(dir, ""), 0555)
144
c.Assert(err, jc.ErrorIsNil)
146
// Cannot use permissions properly on windows for now
147
if runtime.GOOS != "windows" {
149
c.Assert(err, gc.ErrorMatches, ".* permission denied")
152
// Make the directory writable again so that gocheck can clean it up.
153
err = os.Chmod(storePath(dir, ""), 0777)
154
c.Assert(err, jc.ErrorIsNil)
157
func (*diskStoreSuite) TestRenameFails(c *gc.C) {
158
if runtime.GOOS == "windows" {
159
c.Skip("issue 1403084: the way the error is checked doesn't work on windows")
162
store, err := configstore.NewDisk(dir)
163
c.Assert(err, jc.ErrorIsNil)
165
// Replace the file by an directory which can't be renamed over.
166
path := storePath(dir, "somemodel")
167
err = os.Mkdir(path, 0777)
168
c.Assert(err, jc.ErrorIsNil)
170
info := store.CreateInfo("somemodel")
172
c.Assert(err, gc.ErrorMatches, "model info already exists")
175
func (*diskStoreSuite) TestDestroyRemovesFiles(c *gc.C) {
177
store, err := configstore.NewDisk(dir)
178
c.Assert(err, jc.ErrorIsNil)
180
info := store.CreateInfo("somemodel")
182
c.Assert(err, jc.ErrorIsNil)
184
_, err = os.Stat(storePath(dir, "somemodel"))
185
c.Assert(err, jc.ErrorIsNil)
188
c.Assert(err, jc.ErrorIsNil)
190
_, err = os.Stat(storePath(dir, "somemodel"))
191
c.Assert(err, jc.Satisfies, os.IsNotExist)
194
c.Assert(err, gc.ErrorMatches, "model info has already been removed")
197
func (*diskStoreSuite) TestWriteSmallerFile(c *gc.C) {
199
store, err := configstore.NewDisk(dir)
200
c.Assert(err, jc.ErrorIsNil)
201
info := store.CreateInfo("somemodel")
202
endpoint := configstore.APIEndpoint{
203
Addresses: []string{"this", "is", "never", "validated", "here"},
204
Hostnames: []string{"neither", "is", "this"},
205
ModelUUID: testing.ModelTag.Id(),
207
info.SetAPIEndpoint(endpoint)
209
c.Assert(err, jc.ErrorIsNil)
211
newInfo, err := store.ReadInfo("somemodel")
212
c.Assert(err, jc.ErrorIsNil)
213
// Now change the number of addresses to be shorter.
214
endpoint.Addresses = []string{"just one"}
215
endpoint.Hostnames = []string{"just this"}
216
newInfo.SetAPIEndpoint(endpoint)
217
err = newInfo.Write()
218
c.Assert(err, jc.ErrorIsNil)
220
// We should be able to read in in fine.
221
yaInfo, err := store.ReadInfo("somemodel")
222
c.Assert(err, jc.ErrorIsNil)
223
c.Assert(yaInfo.APIEndpoint().Addresses, gc.DeepEquals, []string{"just one"})
224
c.Assert(yaInfo.APIEndpoint().Hostnames, gc.DeepEquals, []string{"just this"})
227
func (*diskStoreSuite) TestConcurrentAccessBreaksIfTimeExceeded(c *gc.C) {
228
var tw loggo.TestWriter
229
c.Assert(loggo.RegisterWriter("test-log", &tw, loggo.DEBUG), gc.IsNil)
232
store, err := configstore.NewDisk(dir)
233
c.Assert(err, jc.ErrorIsNil)
235
envDir := storePath(dir, "")
236
_, err = configstore.AcquireEnvironmentLock(envDir, "blocking-op")
237
c.Assert(err, jc.ErrorIsNil)
239
_, err = store.ReadInfo("somemodel")
240
c.Check(err, jc.Satisfies, errors.IsNotFound)
242
// Using . between environments and env.lock so we don't have to care
243
// about forward vs. backwards slash separator.
244
messages := []jc.SimpleMessage{
245
{loggo.WARNING, `breaking configstore lock, lock dir: .*models.env\.lock`},
246
{loggo.WARNING, `lock holder message: pid: \d+, operation: blocking-op`},
249
c.Check(tw.Log(), jc.LogMatches, messages)