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  }