github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/environs/configstore/disk_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package configstore_test
     5  
     6  import (
     7  	"fmt"
     8  	"io/ioutil"
     9  	"os"
    10  	"path/filepath"
    11  	"runtime"
    12  	"strings"
    13  
    14  	"github.com/juju/errors"
    15  	"github.com/juju/loggo"
    16  	jc "github.com/juju/testing/checkers"
    17  	"github.com/juju/utils/fslock"
    18  	gc "gopkg.in/check.v1"
    19  
    20  	"github.com/juju/juju/environs/configstore"
    21  	"github.com/juju/juju/testing"
    22  )
    23  
    24  var _ = gc.Suite(&diskInterfaceSuite{})
    25  
    26  type diskInterfaceSuite struct {
    27  	interfaceSuite
    28  	dir string
    29  }
    30  
    31  func (s *diskInterfaceSuite) SetUpTest(c *gc.C) {
    32  	s.interfaceSuite.SetUpTest(c)
    33  	s.dir = c.MkDir()
    34  	s.NewStore = func(c *gc.C) configstore.Storage {
    35  		store, err := configstore.NewDisk(s.dir)
    36  		c.Assert(err, jc.ErrorIsNil)
    37  		return store
    38  	}
    39  }
    40  
    41  // storePath returns the path to the environment info
    42  // for the named environment in the given directory.
    43  // If envName is empty, it returns the path
    44  // to the info files' containing directory.
    45  func storePath(dir string, envName string) string {
    46  	path := filepath.Join(dir, "environments")
    47  	if envName != "" {
    48  		path = filepath.Join(path, envName+".jenv")
    49  	}
    50  	return path
    51  }
    52  
    53  func (s *diskInterfaceSuite) TearDownTest(c *gc.C) {
    54  	s.NewStore = nil
    55  	// Check that no stray temp files have been left behind
    56  	entries, err := ioutil.ReadDir(storePath(s.dir, ""))
    57  	c.Assert(err, jc.ErrorIsNil)
    58  	for _, entry := range entries {
    59  		if !strings.HasSuffix(entry.Name(), ".jenv") && entry.Name() != "cache.yaml" {
    60  			c.Errorf("found possible stray temp file %s, %q", s.dir, entry.Name())
    61  		}
    62  	}
    63  	s.interfaceSuite.TearDownTest(c)
    64  }
    65  
    66  var _ = gc.Suite(&diskStoreSuite{})
    67  
    68  type diskStoreSuite struct {
    69  	testing.BaseSuite
    70  }
    71  
    72  func (*diskStoreSuite) TestNewDisk(c *gc.C) {
    73  	dir := c.MkDir()
    74  	store, err := configstore.NewDisk(filepath.Join(dir, "foo"))
    75  	c.Assert(err, jc.Satisfies, os.IsNotExist)
    76  	c.Assert(store, gc.IsNil)
    77  
    78  	store, err = configstore.NewDisk(filepath.Join(dir))
    79  	c.Assert(err, jc.ErrorIsNil)
    80  	c.Assert(store, gc.NotNil)
    81  }
    82  
    83  var sampleInfo = `
    84    user: rog
    85    password: guessit
    86    state-servers:
    87    - 10.0.0.1
    88    - 127.0.0.1
    89    server-hostnames:
    90    - example.com
    91    - kremvax.ru
    92    ca-cert: 'first line
    93  
    94      second line'
    95    bootstrap-config:
    96      secret: blah
    97      arble: bletch
    98  `[1:]
    99  
   100  func (*diskStoreSuite) TestRead(c *gc.C) {
   101  	dir := c.MkDir()
   102  	err := os.Mkdir(storePath(dir, ""), 0700)
   103  	c.Assert(err, jc.ErrorIsNil)
   104  	err = ioutil.WriteFile(storePath(dir, "someenv"), []byte(sampleInfo), 0666)
   105  	c.Assert(err, jc.ErrorIsNil)
   106  	store, err := configstore.NewDisk(dir)
   107  	c.Assert(err, jc.ErrorIsNil)
   108  	info, err := store.ReadInfo("someenv")
   109  	c.Assert(err, jc.ErrorIsNil)
   110  	c.Assert(info.Initialized(), jc.IsTrue)
   111  	c.Assert(info.APICredentials(), gc.DeepEquals, configstore.APICredentials{
   112  		User:     "rog",
   113  		Password: "guessit",
   114  	})
   115  	c.Assert(info.APIEndpoint(), gc.DeepEquals, configstore.APIEndpoint{
   116  		Addresses: []string{"10.0.0.1", "127.0.0.1"},
   117  		Hostnames: []string{"example.com", "kremvax.ru"},
   118  		CACert:    "first line\nsecond line",
   119  	})
   120  	c.Assert(info.Location(), gc.Equals, fmt.Sprintf("file %q", filepath.Join(dir, "environments", "someenv.jenv")))
   121  	c.Assert(info.BootstrapConfig(), gc.DeepEquals, map[string]interface{}{
   122  		"secret": "blah",
   123  		"arble":  "bletch",
   124  	})
   125  }
   126  
   127  func (*diskStoreSuite) TestReadNotFound(c *gc.C) {
   128  	dir := c.MkDir()
   129  	store, err := configstore.NewDisk(dir)
   130  	c.Assert(err, jc.ErrorIsNil)
   131  	info, err := store.ReadInfo("someenv")
   132  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   133  	c.Assert(info, gc.IsNil)
   134  }
   135  
   136  func (*diskStoreSuite) TestWriteFails(c *gc.C) {
   137  	dir := c.MkDir()
   138  	store, err := configstore.NewDisk(dir)
   139  	c.Assert(err, jc.ErrorIsNil)
   140  
   141  	info := store.CreateInfo("someenv")
   142  
   143  	// Make the directory non-writable
   144  	err = os.Chmod(storePath(dir, ""), 0555)
   145  	c.Assert(err, jc.ErrorIsNil)
   146  
   147  	// Cannot use permissions properly on windows for now
   148  	if runtime.GOOS != "windows" {
   149  		err = info.Write()
   150  		c.Assert(err, gc.ErrorMatches, ".* permission denied")
   151  	}
   152  
   153  	// Make the directory writable again so that gocheck can clean it up.
   154  	err = os.Chmod(storePath(dir, ""), 0777)
   155  	c.Assert(err, jc.ErrorIsNil)
   156  }
   157  
   158  func (*diskStoreSuite) TestRenameFails(c *gc.C) {
   159  	if runtime.GOOS == "windows" {
   160  		c.Skip("issue 1403084: the way the error is checked doesn't work on windows")
   161  	}
   162  	dir := c.MkDir()
   163  	store, err := configstore.NewDisk(dir)
   164  	c.Assert(err, jc.ErrorIsNil)
   165  
   166  	// Replace the file by an directory which can't be renamed over.
   167  	path := storePath(dir, "someenv")
   168  	err = os.Mkdir(path, 0777)
   169  	c.Assert(err, jc.ErrorIsNil)
   170  
   171  	info := store.CreateInfo("someenv")
   172  	err = info.Write()
   173  	c.Assert(err, gc.ErrorMatches, "environment info already exists")
   174  }
   175  
   176  func (*diskStoreSuite) TestDestroyRemovesFiles(c *gc.C) {
   177  	dir := c.MkDir()
   178  	store, err := configstore.NewDisk(dir)
   179  	c.Assert(err, jc.ErrorIsNil)
   180  
   181  	info := store.CreateInfo("someenv")
   182  	err = info.Write()
   183  	c.Assert(err, jc.ErrorIsNil)
   184  
   185  	_, err = os.Stat(storePath(dir, "someenv"))
   186  	c.Assert(err, jc.ErrorIsNil)
   187  
   188  	err = info.Destroy()
   189  	c.Assert(err, jc.ErrorIsNil)
   190  
   191  	_, err = os.Stat(storePath(dir, "someenv"))
   192  	c.Assert(err, jc.Satisfies, os.IsNotExist)
   193  
   194  	err = info.Destroy()
   195  	c.Assert(err, gc.ErrorMatches, "environment info has already been removed")
   196  }
   197  
   198  func (*diskStoreSuite) TestWriteSmallerFile(c *gc.C) {
   199  	dir := c.MkDir()
   200  	store, err := configstore.NewDisk(dir)
   201  	c.Assert(err, jc.ErrorIsNil)
   202  	info := store.CreateInfo("someenv")
   203  	endpoint := configstore.APIEndpoint{
   204  		Addresses:   []string{"this", "is", "never", "validated", "here"},
   205  		Hostnames:   []string{"neither", "is", "this"},
   206  		EnvironUUID: testing.EnvironmentTag.Id(),
   207  	}
   208  	info.SetAPIEndpoint(endpoint)
   209  	err = info.Write()
   210  	c.Assert(err, jc.ErrorIsNil)
   211  
   212  	newInfo, err := store.ReadInfo("someenv")
   213  	c.Assert(err, jc.ErrorIsNil)
   214  	// Now change the number of addresses to be shorter.
   215  	endpoint.Addresses = []string{"just one"}
   216  	endpoint.Hostnames = []string{"just this"}
   217  	newInfo.SetAPIEndpoint(endpoint)
   218  	err = newInfo.Write()
   219  	c.Assert(err, jc.ErrorIsNil)
   220  
   221  	// We should be able to read in in fine.
   222  	yaInfo, err := store.ReadInfo("someenv")
   223  	c.Assert(err, jc.ErrorIsNil)
   224  	c.Assert(yaInfo.APIEndpoint().Addresses, gc.DeepEquals, []string{"just one"})
   225  	c.Assert(yaInfo.APIEndpoint().Hostnames, gc.DeepEquals, []string{"just this"})
   226  }
   227  
   228  func (*diskStoreSuite) TestConcurrentAccess(c *gc.C) {
   229  	var tw loggo.TestWriter
   230  	c.Assert(loggo.RegisterWriter("test-log", &tw, loggo.DEBUG), gc.IsNil)
   231  
   232  	dir := c.MkDir()
   233  	store, err := configstore.NewDisk(dir)
   234  	c.Assert(err, jc.ErrorIsNil)
   235  
   236  	envDir := storePath(dir, "")
   237  	lock, err := configstore.AcquireEnvironmentLock(envDir, "blocking-op")
   238  	c.Assert(err, jc.ErrorIsNil)
   239  	defer lock.Unlock()
   240  
   241  	_, err = store.ReadInfo("someenv")
   242  	c.Assert(errors.Cause(err), gc.Equals, fslock.ErrTimeout)
   243  
   244  	// Using . between environments and env.lock so we don't have to care
   245  	// about forward vs. backwards slash separator.
   246  	messages := []jc.SimpleMessage{
   247  		{loggo.WARNING, `configstore lock held, lock dir: .*environments.env\.lock`},
   248  		{loggo.WARNING, `lock holder message: pid: \d+, operation: blocking-op`},
   249  	}
   250  
   251  	c.Check(tw.Log(), jc.LogMatches, messages)
   252  }