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  }