github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/worker/uniter/relation/relation_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package relation_test
     5  
     6  import (
     7  	"fmt"
     8  	"io/ioutil"
     9  	"os"
    10  	"path/filepath"
    11  	"strconv"
    12  
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  	"gopkg.in/juju/charm.v5/hooks"
    16  
    17  	"github.com/juju/juju/worker/uniter/hook"
    18  	"github.com/juju/juju/worker/uniter/relation"
    19  )
    20  
    21  type StateDirSuite struct{}
    22  
    23  var _ = gc.Suite(&StateDirSuite{})
    24  
    25  func (s *StateDirSuite) TestReadStateDirEmpty(c *gc.C) {
    26  	basedir := c.MkDir()
    27  	reldir := filepath.Join(basedir, "123")
    28  
    29  	dir, err := relation.ReadStateDir(basedir, 123)
    30  	c.Assert(err, jc.ErrorIsNil)
    31  	state := dir.State()
    32  	c.Assert(state.RelationId, gc.Equals, 123)
    33  	c.Assert(msi(state.Members), gc.DeepEquals, msi{})
    34  	c.Assert(state.ChangedPending, gc.Equals, "")
    35  
    36  	_, err = os.Stat(reldir)
    37  	c.Assert(err, jc.Satisfies, os.IsNotExist)
    38  
    39  	err = dir.Ensure()
    40  	c.Assert(err, jc.ErrorIsNil)
    41  	fi, err := os.Stat(reldir)
    42  	c.Assert(err, jc.ErrorIsNil)
    43  	c.Assert(fi, jc.Satisfies, os.FileInfo.IsDir)
    44  }
    45  
    46  func (s *StateDirSuite) TestReadStateDirValid(c *gc.C) {
    47  	basedir := c.MkDir()
    48  	reldir := setUpDir(c, basedir, "123", map[string]string{
    49  		"foo-bar-1":           "change-version: 99\n",
    50  		"foo-bar-1.preparing": "change-version: 100\n",
    51  		"baz-qux-7":           "change-version: 101\nchanged-pending: true\n",
    52  		"nonsensical":         "blah",
    53  		"27":                  "blah",
    54  	})
    55  	setUpDir(c, reldir, "ignored", nil)
    56  
    57  	dir, err := relation.ReadStateDir(basedir, 123)
    58  	c.Assert(err, jc.ErrorIsNil)
    59  	state := dir.State()
    60  	c.Assert(state.RelationId, gc.Equals, 123)
    61  	c.Assert(msi(state.Members), gc.DeepEquals, msi{"foo-bar/1": 99, "baz-qux/7": 101})
    62  	c.Assert(state.ChangedPending, gc.Equals, "baz-qux/7")
    63  }
    64  
    65  var badRelationsTests = []struct {
    66  	contents map[string]string
    67  	subdirs  []string
    68  	err      string
    69  }{
    70  	{
    71  		nil, []string{"foo-bar-1"},
    72  		`.* (is a directory|handle is invalid.)`,
    73  	}, {
    74  		map[string]string{"foo-1": "'"}, nil,
    75  		`invalid unit file "foo-1": YAML error: .*`,
    76  	}, {
    77  		map[string]string{"foo-1": "blah: blah\n"}, nil,
    78  		`invalid unit file "foo-1": "changed-version" not set`,
    79  	}, {
    80  		map[string]string{
    81  			"foo-1": "change-version: 123\nchanged-pending: true\n",
    82  			"foo-2": "change-version: 456\nchanged-pending: true\n",
    83  		}, nil,
    84  		`"foo/1" and "foo/2" both have pending changed hooks`,
    85  	},
    86  }
    87  
    88  func (s *StateDirSuite) TestBadRelations(c *gc.C) {
    89  	for i, t := range badRelationsTests {
    90  		c.Logf("test %d", i)
    91  		basedir := c.MkDir()
    92  		reldir := setUpDir(c, basedir, "123", t.contents)
    93  		for _, subdir := range t.subdirs {
    94  			setUpDir(c, reldir, subdir, nil)
    95  		}
    96  		_, err := relation.ReadStateDir(basedir, 123)
    97  		expect := `cannot load relation state from ".*": ` + t.err
    98  		c.Assert(err, gc.ErrorMatches, expect)
    99  	}
   100  }
   101  
   102  var defaultMembers = msi{"foo/1": 0, "foo/2": 0}
   103  
   104  // writeTests verify the behaviour of sequences of HookInfos on a relation
   105  // state that starts off containing defaultMembers.
   106  var writeTests = []struct {
   107  	hooks   []hook.Info
   108  	members msi
   109  	pending string
   110  	err     string
   111  	deleted bool
   112  }{
   113  	// Verify that valid changes work.
   114  	{
   115  		hooks: []hook.Info{
   116  			{Kind: hooks.RelationChanged, RelationId: 123, RemoteUnit: "foo/1", ChangeVersion: 1},
   117  		},
   118  		members: msi{"foo/1": 1, "foo/2": 0},
   119  	}, {
   120  		hooks: []hook.Info{
   121  			{Kind: hooks.RelationJoined, RelationId: 123, RemoteUnit: "foo/3"},
   122  		},
   123  		members: msi{"foo/1": 0, "foo/2": 0, "foo/3": 0},
   124  		pending: "foo/3",
   125  	}, {
   126  		hooks: []hook.Info{
   127  			{Kind: hooks.RelationJoined, RelationId: 123, RemoteUnit: "foo/3"},
   128  			{Kind: hooks.RelationChanged, RelationId: 123, RemoteUnit: "foo/3"},
   129  		},
   130  		members: msi{"foo/1": 0, "foo/2": 0, "foo/3": 0},
   131  	}, {
   132  		hooks: []hook.Info{
   133  			{Kind: hooks.RelationDeparted, RelationId: 123, RemoteUnit: "foo/1"},
   134  		},
   135  		members: msi{"foo/2": 0},
   136  	}, {
   137  		hooks: []hook.Info{
   138  			{Kind: hooks.RelationDeparted, RelationId: 123, RemoteUnit: "foo/1"},
   139  			{Kind: hooks.RelationJoined, RelationId: 123, RemoteUnit: "foo/1"},
   140  		},
   141  		members: msi{"foo/1": 0, "foo/2": 0},
   142  		pending: "foo/1",
   143  	}, {
   144  		hooks: []hook.Info{
   145  			{Kind: hooks.RelationDeparted, RelationId: 123, RemoteUnit: "foo/1"},
   146  			{Kind: hooks.RelationJoined, RelationId: 123, RemoteUnit: "foo/1"},
   147  			{Kind: hooks.RelationChanged, RelationId: 123, RemoteUnit: "foo/1"},
   148  		},
   149  		members: msi{"foo/1": 0, "foo/2": 0},
   150  	}, {
   151  		hooks: []hook.Info{
   152  			{Kind: hooks.RelationDeparted, RelationId: 123, RemoteUnit: "foo/1"},
   153  			{Kind: hooks.RelationDeparted, RelationId: 123, RemoteUnit: "foo/2"},
   154  			{Kind: hooks.RelationBroken, RelationId: 123},
   155  		},
   156  		deleted: true,
   157  	},
   158  	// Verify detection of various error conditions.
   159  	{
   160  		hooks: []hook.Info{
   161  			{Kind: hooks.RelationJoined, RelationId: 456, RemoteUnit: "foo/1"},
   162  		},
   163  		err: "expected relation 123, got relation 456",
   164  	}, {
   165  		hooks: []hook.Info{
   166  			{Kind: hooks.RelationJoined, RelationId: 123, RemoteUnit: "foo/3"},
   167  			{Kind: hooks.RelationJoined, RelationId: 123, RemoteUnit: "foo/4"},
   168  		},
   169  		members: msi{"foo/1": 0, "foo/2": 0, "foo/3": 0},
   170  		pending: "foo/3",
   171  		err:     `expected "relation-changed" for "foo/3"`,
   172  	}, {
   173  		hooks: []hook.Info{
   174  			{Kind: hooks.RelationJoined, RelationId: 123, RemoteUnit: "foo/3"},
   175  			{Kind: hooks.RelationChanged, RelationId: 123, RemoteUnit: "foo/1"},
   176  		},
   177  		members: msi{"foo/1": 0, "foo/2": 0, "foo/3": 0},
   178  		pending: "foo/3",
   179  		err:     `expected "relation-changed" for "foo/3"`,
   180  	}, {
   181  		hooks: []hook.Info{
   182  			{Kind: hooks.RelationJoined, RelationId: 123, RemoteUnit: "foo/1"},
   183  		},
   184  		err: "unit already joined",
   185  	}, {
   186  		hooks: []hook.Info{
   187  			{Kind: hooks.RelationChanged, RelationId: 123, RemoteUnit: "foo/3"},
   188  		},
   189  		err: "unit has not joined",
   190  	}, {
   191  		hooks: []hook.Info{
   192  			{Kind: hooks.RelationDeparted, RelationId: 123, RemoteUnit: "foo/3"},
   193  		},
   194  		err: "unit has not joined",
   195  	}, {
   196  		hooks: []hook.Info{
   197  			{Kind: hooks.RelationBroken, RelationId: 123},
   198  		},
   199  		err: `cannot run "relation-broken" while units still present`,
   200  	}, {
   201  		hooks: []hook.Info{
   202  			{Kind: hooks.RelationDeparted, RelationId: 123, RemoteUnit: "foo/1"},
   203  			{Kind: hooks.RelationDeparted, RelationId: 123, RemoteUnit: "foo/2"},
   204  			{Kind: hooks.RelationBroken, RelationId: 123},
   205  			{Kind: hooks.RelationJoined, RelationId: 123, RemoteUnit: "foo/1"},
   206  		},
   207  		err:     `relation is broken and cannot be changed further`,
   208  		deleted: true,
   209  	},
   210  }
   211  
   212  func (s *StateDirSuite) TestWrite(c *gc.C) {
   213  	for i, t := range writeTests {
   214  		c.Logf("test %d", i)
   215  		basedir := c.MkDir()
   216  		setUpDir(c, basedir, "123", map[string]string{
   217  			"foo-1": "change-version: 0\n",
   218  			"foo-2": "change-version: 0\n",
   219  		})
   220  		dir, err := relation.ReadStateDir(basedir, 123)
   221  		c.Assert(err, jc.ErrorIsNil)
   222  		for i, hi := range t.hooks {
   223  			c.Logf("  hook %d", i)
   224  			if i == len(t.hooks)-1 && t.err != "" {
   225  				err = dir.State().Validate(hi)
   226  				expect := fmt.Sprintf(`inappropriate %q for %q: %s`, hi.Kind, hi.RemoteUnit, t.err)
   227  				c.Assert(err, gc.ErrorMatches, expect)
   228  			} else {
   229  				err = dir.State().Validate(hi)
   230  				c.Assert(err, jc.ErrorIsNil)
   231  				err = dir.Write(hi)
   232  				c.Assert(err, jc.ErrorIsNil)
   233  				// Check that writing the same change again is OK.
   234  				err = dir.Write(hi)
   235  				c.Assert(err, jc.ErrorIsNil)
   236  			}
   237  		}
   238  		members := t.members
   239  		if members == nil && !t.deleted {
   240  			members = defaultMembers
   241  		}
   242  		assertState(c, dir, basedir, 123, members, t.pending, t.deleted)
   243  	}
   244  }
   245  
   246  func (s *StateDirSuite) TestRemove(c *gc.C) {
   247  	basedir := c.MkDir()
   248  	dir, err := relation.ReadStateDir(basedir, 1)
   249  	c.Assert(err, jc.ErrorIsNil)
   250  	err = dir.Ensure()
   251  	c.Assert(err, jc.ErrorIsNil)
   252  	err = dir.Remove()
   253  	c.Assert(err, jc.ErrorIsNil)
   254  	err = dir.Remove()
   255  	c.Assert(err, jc.ErrorIsNil)
   256  
   257  	setUpDir(c, basedir, "99", map[string]string{
   258  		"foo-1": "change-version: 0\n",
   259  	})
   260  	dir, err = relation.ReadStateDir(basedir, 99)
   261  	c.Assert(err, jc.ErrorIsNil)
   262  	err = dir.Remove()
   263  	// Windows message is The directory is not empty
   264  	// Unix message is directory not empty
   265  	c.Assert(err, gc.ErrorMatches, ".* directory (is )?not empty.?")
   266  }
   267  
   268  type ReadAllStateDirsSuite struct{}
   269  
   270  var _ = gc.Suite(&ReadAllStateDirsSuite{})
   271  
   272  func (s *ReadAllStateDirsSuite) TestNoDir(c *gc.C) {
   273  	basedir := c.MkDir()
   274  	relsdir := filepath.Join(basedir, "relations")
   275  
   276  	dirs, err := relation.ReadAllStateDirs(relsdir)
   277  	c.Assert(err, jc.ErrorIsNil)
   278  	c.Assert(dirs, gc.HasLen, 0)
   279  
   280  	_, err = os.Stat(relsdir)
   281  	c.Assert(err, jc.Satisfies, os.IsNotExist)
   282  }
   283  
   284  func (s *ReadAllStateDirsSuite) TestBadStateDir(c *gc.C) {
   285  	basedir := c.MkDir()
   286  	relsdir := setUpDir(c, basedir, "relations", nil)
   287  	setUpDir(c, relsdir, "123", map[string]string{
   288  		"bad-0": "blah: blah\n",
   289  	})
   290  	_, err := relation.ReadAllStateDirs(relsdir)
   291  	c.Assert(err, gc.ErrorMatches, `cannot load relations state from .*: cannot load relation state from .*: invalid unit file "bad-0": "changed-version" not set`)
   292  }
   293  
   294  func (s *ReadAllStateDirsSuite) TestReadAllStateDirs(c *gc.C) {
   295  	basedir := c.MkDir()
   296  	relsdir := setUpDir(c, basedir, "relations", map[string]string{
   297  		"ignored":     "blah",
   298  		"foo-bar-123": "gibberish",
   299  	})
   300  	setUpDir(c, relsdir, "123", map[string]string{
   301  		"foo-0":     "change-version: 1\n",
   302  		"foo-1":     "change-version: 2\nchanged-pending: true\n",
   303  		"gibberish": "gibberish",
   304  	})
   305  	setUpDir(c, relsdir, "456", map[string]string{
   306  		"bar-0": "change-version: 3\n",
   307  		"bar-1": "change-version: 4\n",
   308  	})
   309  	setUpDir(c, relsdir, "789", nil)
   310  	setUpDir(c, relsdir, "onethousand", map[string]string{
   311  		"baz-0": "change-version: 3\n",
   312  		"baz-1": "change-version: 4\n",
   313  	})
   314  
   315  	dirs, err := relation.ReadAllStateDirs(relsdir)
   316  	c.Assert(err, jc.ErrorIsNil)
   317  	for id, dir := range dirs {
   318  		c.Logf("%d: %#v", id, dir)
   319  	}
   320  	assertState(c, dirs[123], relsdir, 123, msi{"foo/0": 1, "foo/1": 2}, "foo/1", false)
   321  	assertState(c, dirs[456], relsdir, 456, msi{"bar/0": 3, "bar/1": 4}, "", false)
   322  	assertState(c, dirs[789], relsdir, 789, msi{}, "", false)
   323  	c.Assert(dirs, gc.HasLen, 3)
   324  }
   325  
   326  func setUpDir(c *gc.C, basedir, name string, contents map[string]string) string {
   327  	reldir := filepath.Join(basedir, name)
   328  	err := os.Mkdir(reldir, 0777)
   329  	c.Assert(err, jc.ErrorIsNil)
   330  	for name, content := range contents {
   331  		path := filepath.Join(reldir, name)
   332  		err := ioutil.WriteFile(path, []byte(content), 0777)
   333  		c.Assert(err, jc.ErrorIsNil)
   334  	}
   335  	return reldir
   336  }
   337  
   338  func assertState(c *gc.C, dir *relation.StateDir, relsdir string, relationId int, members msi, pending string, deleted bool) {
   339  	expect := &relation.State{
   340  		RelationId:     relationId,
   341  		Members:        map[string]int64(members),
   342  		ChangedPending: pending,
   343  	}
   344  	c.Assert(dir.State(), gc.DeepEquals, expect)
   345  	if deleted {
   346  		_, err := os.Stat(filepath.Join(relsdir, strconv.Itoa(relationId)))
   347  		c.Assert(err, jc.Satisfies, os.IsNotExist)
   348  	} else {
   349  		fresh, err := relation.ReadStateDir(relsdir, relationId)
   350  		c.Assert(err, jc.ErrorIsNil)
   351  		c.Assert(fresh.State(), gc.DeepEquals, expect)
   352  	}
   353  }