github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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-unstable/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 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 ".*": yaml: unmarshal errors:
    80  `+"  line 1: cannot unmarshal !!str `rubbish` into storage.diskInfo")
    81  }
    82  
    83  func (s *stateSuite) TestReadAllStateFilesDirNotExist(c *gc.C) {
    84  	dir := filepath.Join(c.MkDir(), "doesnotexist")
    85  	states, err := storage.ReadAllStateFiles(dir)
    86  	c.Assert(err, jc.ErrorIsNil)
    87  	c.Assert(states, gc.HasLen, 0)
    88  }
    89  
    90  func (s *stateSuite) TestReadAllStateFilesDirEmpty(c *gc.C) {
    91  	dir := c.MkDir()
    92  	states, err := storage.ReadAllStateFiles(dir)
    93  	c.Assert(err, jc.ErrorIsNil)
    94  	c.Assert(states, gc.HasLen, 0)
    95  }
    96  
    97  func (s *stateSuite) TestReadStateFileFileNotExist(c *gc.C) {
    98  	dir := c.MkDir()
    99  	state, err := storage.ReadStateFile(dir, names.NewStorageTag("data/0"))
   100  	c.Assert(err, jc.ErrorIsNil)
   101  	c.Assert(state, gc.NotNil)
   102  
   103  	data, err := ioutil.ReadFile(filepath.Join(dir, "data-0"))
   104  	c.Assert(err, jc.Satisfies, os.IsNotExist)
   105  
   106  	err = state.CommitHook(hook.Info{
   107  		Kind:      hooks.StorageAttached,
   108  		StorageId: "data-0",
   109  	})
   110  	c.Assert(err, jc.ErrorIsNil)
   111  
   112  	data, err = ioutil.ReadFile(filepath.Join(dir, "data-0"))
   113  	c.Assert(err, jc.ErrorIsNil)
   114  	c.Assert(string(data), gc.Equals, "attached: true\n")
   115  }
   116  
   117  func (s *stateSuite) TestReadStateFileDirNotExist(c *gc.C) {
   118  	dir := filepath.Join(c.MkDir(), "doesnotexist")
   119  	state, err := storage.ReadStateFile(dir, names.NewStorageTag("data/0"))
   120  	c.Assert(err, jc.ErrorIsNil)
   121  	c.Assert(state, gc.NotNil)
   122  
   123  	// CommitHook will fail if the directory does not exist. The uniter
   124  	// must ensure the directory is created before committing any hooks
   125  	// to the storage state.
   126  	err = state.CommitHook(hook.Info{
   127  		Kind:      hooks.StorageAttached,
   128  		StorageId: "data-0",
   129  	})
   130  	c.Assert(errors.Cause(err), jc.Satisfies, os.IsNotExist)
   131  }
   132  
   133  func (s *stateSuite) TestReadStateFileBadFormat(c *gc.C) {
   134  	dir := c.MkDir()
   135  	writeFile(c, filepath.Join(dir, "data-0"), "!@#")
   136  	_, err := storage.ReadStateFile(dir, names.NewStorageTag("data/0"))
   137  	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`)
   138  
   139  	writeFile(c, filepath.Join(dir, "data-0"), "icantbelieveitsnotattached: true\n")
   140  	_, err = storage.ReadStateFile(dir, names.NewStorageTag("data/0"))
   141  	c.Assert(err, gc.ErrorMatches, `cannot load storage "data/0" state from ".*": invalid storage state file ".*": missing 'attached'`)
   142  }
   143  
   144  func (s *stateSuite) TestCommitHook(c *gc.C) {
   145  	dir := c.MkDir()
   146  	state, err := storage.ReadStateFile(dir, names.NewStorageTag("data/0"))
   147  	c.Assert(err, jc.ErrorIsNil)
   148  	c.Assert(state, gc.NotNil)
   149  	stateFile := filepath.Join(dir, "data-0")
   150  
   151  	// CommitHook must be idempotent, so test each operation
   152  	// twice in a row.
   153  
   154  	for i := 0; i < 2; i++ {
   155  		err := state.CommitHook(hook.Info{
   156  			Kind:      hooks.StorageAttached,
   157  			StorageId: "data-0",
   158  		})
   159  		c.Assert(err, jc.ErrorIsNil)
   160  		c.Assert(stateFile, jc.IsNonEmptyFile)
   161  	}
   162  
   163  	for i := 0; i < 2; i++ {
   164  		err := state.CommitHook(hook.Info{
   165  			Kind:      hooks.StorageDetaching,
   166  			StorageId: "data-0",
   167  		})
   168  		c.Assert(err, jc.ErrorIsNil)
   169  		c.Assert(stateFile, jc.DoesNotExist)
   170  	}
   171  }
   172  
   173  func (s *stateSuite) TestValidateHook(c *gc.C) {
   174  	const unattached = false
   175  	const attached = true
   176  
   177  	err := storage.ValidateHook(
   178  		names.NewStorageTag("data/0"), unattached,
   179  		hook.Info{Kind: hooks.StorageAttached, StorageId: "data/1"},
   180  	)
   181  	c.Assert(err, gc.ErrorMatches, `inappropriate "storage-attached" hook for storage "data/0": expected storage "data/0", got storage "data/1"`)
   182  
   183  	validate := func(attached bool, kind hooks.Kind) error {
   184  		return storage.ValidateHook(
   185  			names.NewStorageTag("data/0"), attached,
   186  			hook.Info{Kind: kind, StorageId: "data/0"},
   187  		)
   188  	}
   189  	assertValidates := func(attached bool, kind hooks.Kind) {
   190  		err := validate(attached, kind)
   191  		c.Assert(err, jc.ErrorIsNil)
   192  	}
   193  	assertValidateFails := func(attached bool, kind hooks.Kind, expect string) {
   194  		err := validate(attached, kind)
   195  		c.Assert(err, gc.ErrorMatches, expect)
   196  	}
   197  
   198  	assertValidates(false, hooks.StorageAttached)
   199  	assertValidates(true, hooks.StorageDetaching)
   200  	assertValidateFails(false, hooks.StorageDetaching, `inappropriate "storage-detaching" hook for storage "data/0": storage not attached`)
   201  	assertValidateFails(true, hooks.StorageAttached, `inappropriate "storage-attached" hook for storage "data/0": storage already attached`)
   202  }