github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/presence/presence_test.go (about)

     1  // Copyright 2018 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package presence_test
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/clock/testclock"
    10  	"github.com/juju/errors"
    11  	"github.com/juju/loggo"
    12  	"github.com/juju/pubsub"
    13  	"github.com/juju/testing"
    14  	jc "github.com/juju/testing/checkers"
    15  	"github.com/kr/pretty"
    16  	gc "gopkg.in/check.v1"
    17  	"gopkg.in/juju/names.v2"
    18  	"gopkg.in/juju/worker.v1"
    19  	"gopkg.in/juju/worker.v1/workertest"
    20  
    21  	corepresence "github.com/juju/juju/core/presence"
    22  	"github.com/juju/juju/pubsub/apiserver"
    23  	"github.com/juju/juju/pubsub/centralhub"
    24  	"github.com/juju/juju/pubsub/forwarder"
    25  	coretesting "github.com/juju/juju/testing"
    26  	"github.com/juju/juju/worker/presence"
    27  )
    28  
    29  type PresenceSuite struct {
    30  	testing.IsolationSuite
    31  	server   string
    32  	hub      *pubsub.StructuredHub
    33  	clock    *testclock.Clock
    34  	recorder corepresence.Recorder
    35  	config   presence.WorkerConfig
    36  }
    37  
    38  var _ = gc.Suite(&PresenceSuite{})
    39  
    40  func (s *PresenceSuite) SetUpTest(c *gc.C) {
    41  	s.IsolationSuite.SetUpTest(c)
    42  	s.hub = centralhub.New(ourTag)
    43  	s.clock = testclock.NewClock(time.Time{})
    44  	s.recorder = corepresence.New(s.clock)
    45  	s.recorder.Enable()
    46  	s.config = presence.WorkerConfig{
    47  		Origin:   ourServer,
    48  		Hub:      s.hub,
    49  		Recorder: s.recorder,
    50  		Logger:   loggo.GetLogger("test"),
    51  	}
    52  	loggo.ConfigureLoggers("<root>=trace")
    53  }
    54  
    55  func (s *PresenceSuite) worker(c *gc.C) worker.Worker {
    56  	w, err := presence.NewWorker(s.config)
    57  	c.Assert(err, jc.ErrorIsNil)
    58  	return w
    59  }
    60  
    61  func (s *PresenceSuite) TestWorkerConfigMissingOrigin(c *gc.C) {
    62  	s.config.Origin = ""
    63  	err := s.config.Validate()
    64  	c.Check(err, jc.Satisfies, errors.IsNotValid)
    65  	c.Check(err, gc.ErrorMatches, "missing origin not valid")
    66  }
    67  
    68  func (s *PresenceSuite) TestWorkerConfigMissingHub(c *gc.C) {
    69  	s.config.Hub = nil
    70  	err := s.config.Validate()
    71  	c.Check(err, jc.Satisfies, errors.IsNotValid)
    72  	c.Check(err, gc.ErrorMatches, "missing hub not valid")
    73  }
    74  
    75  func (s *PresenceSuite) TestWorkerConfigMissingRecorder(c *gc.C) {
    76  	s.config.Recorder = nil
    77  	err := s.config.Validate()
    78  	c.Check(err, jc.Satisfies, errors.IsNotValid)
    79  	c.Check(err, gc.ErrorMatches, "missing recorder not valid")
    80  }
    81  
    82  func (s *PresenceSuite) TestWorkerConfigMissingLogger(c *gc.C) {
    83  	s.config.Logger = nil
    84  	err := s.config.Validate()
    85  	c.Check(err, jc.Satisfies, errors.IsNotValid)
    86  	c.Check(err, gc.ErrorMatches, "missing logger not valid")
    87  }
    88  
    89  func (s *PresenceSuite) TestNewWorkerValidatesConfig(c *gc.C) {
    90  	w, err := presence.NewWorker(presence.WorkerConfig{})
    91  	c.Check(err, gc.ErrorMatches, "missing origin not valid")
    92  	c.Check(w, gc.IsNil)
    93  }
    94  
    95  func (s *PresenceSuite) TestWorkerDies(c *gc.C) {
    96  	w := s.worker(c)
    97  	workertest.CleanKill(c, w)
    98  }
    99  
   100  func (s *PresenceSuite) TestReport(c *gc.C) {
   101  	w := s.worker(c)
   102  	defer workertest.CleanKill(c, w)
   103  
   104  	s.recorder.Connect("machine-0", "model-uuid", "agent", 1, false, "")
   105  	s.recorder.Connect("machine-0", "model-uuid", "agent", 2, false, "")
   106  	s.recorder.Connect("machine-0", "model-uuid", "agent", 3, false, "")
   107  	s.recorder.Connect("machine-1", "model-uuid", "agent", 4, false, "")
   108  	s.recorder.Connect("machine-1", "model-uuid", "agent", 5, false, "")
   109  	s.recorder.Connect("machine-2", "model-uuid", "agent", 6, false, "")
   110  
   111  	reporter, ok := w.(worker.Reporter)
   112  	c.Assert(ok, jc.IsTrue)
   113  	c.Assert(reporter.Report(), jc.DeepEquals, map[string]interface{}{
   114  		"machine-0": 3,
   115  		"machine-1": 2,
   116  		"machine-2": 1,
   117  	})
   118  }
   119  
   120  func (s *PresenceSuite) TestForwarderConnectToOther(c *gc.C) {
   121  	w := s.worker(c)
   122  	defer workertest.CleanKill(c, w)
   123  
   124  	done := make(chan struct{})
   125  
   126  	unsub, err := s.hub.Subscribe(apiserver.PresenceRequestTopic, func(topic string, data apiserver.OriginTarget, err error) {
   127  		c.Logf("handler called for %q", topic)
   128  		c.Check(err, jc.ErrorIsNil)
   129  		c.Check(data.Target, gc.Equals, otherServer)
   130  		c.Check(data.Origin, gc.Equals, ourServer)
   131  		close(done)
   132  	})
   133  	c.Assert(err, jc.ErrorIsNil)
   134  	defer unsub()
   135  
   136  	// When connections are established from us to them, we ask for their presence info.
   137  	_, err = s.hub.Publish(
   138  		forwarder.ConnectedTopic,
   139  		apiserver.OriginTarget{Origin: ourServer, Target: otherServer})
   140  	c.Assert(err, jc.ErrorIsNil)
   141  	s.AssertDone(c, done)
   142  }
   143  
   144  func (s *PresenceSuite) TestForwarderConnectFromOther(c *gc.C) {
   145  	w := s.worker(c)
   146  	defer workertest.CleanKill(c, w)
   147  
   148  	done := make(chan struct{})
   149  
   150  	unsub, err := s.hub.Subscribe(apiserver.PresenceRequestTopic, func(topic string, data apiserver.OriginTarget, err error) {
   151  		c.Logf("handler called for %q", topic)
   152  		c.Check(err, jc.ErrorIsNil)
   153  		c.Check(data.Target, gc.Equals, otherServer)
   154  		c.Check(data.Origin, gc.Equals, ourServer)
   155  		close(done)
   156  	})
   157  	c.Assert(err, jc.ErrorIsNil)
   158  	defer unsub()
   159  
   160  	// When connections are established from them to us, we ask for their presence info.
   161  	_, err = s.hub.Publish(
   162  		forwarder.ConnectedTopic,
   163  		apiserver.OriginTarget{Origin: otherServer, Target: ourServer})
   164  	c.Assert(err, jc.ErrorIsNil)
   165  	s.AssertDone(c, done)
   166  }
   167  
   168  func (s *PresenceSuite) TestForwarderConnectOtherIgnored(c *gc.C) {
   169  	w := s.worker(c)
   170  	defer workertest.CleanKill(c, w)
   171  
   172  	called := make(chan struct{})
   173  
   174  	unsub, err := s.hub.Subscribe(apiserver.PresenceRequestTopic, func(topic string, data apiserver.OriginTarget, err error) {
   175  		c.Logf("handler called for %q", topic)
   176  		close(called)
   177  	})
   178  	c.Assert(err, jc.ErrorIsNil)
   179  	defer unsub()
   180  
   181  	_, err = s.hub.Publish(
   182  		forwarder.ConnectedTopic,
   183  		apiserver.OriginTarget{Origin: otherServer, Target: "machine-8"})
   184  	c.Assert(err, jc.ErrorIsNil)
   185  	s.AssertNotCalled(c, called)
   186  }
   187  
   188  func (s *PresenceSuite) TestForwarderDisconnectConnectFromOther(c *gc.C) {
   189  	w := s.worker(c)
   190  	defer workertest.CleanKill(c, w)
   191  
   192  	connect(s.recorder, agent1, agent2)
   193  
   194  	done, err := s.hub.Publish(
   195  		forwarder.DisconnectedTopic,
   196  		apiserver.OriginTarget{Origin: ourServer, Target: otherServer})
   197  	c.Assert(err, jc.ErrorIsNil)
   198  	s.AssertDone(c, done)
   199  	s.AssertConnections(c, alive(agent1), missing(agent2))
   200  }
   201  
   202  func (s *PresenceSuite) TestForwarderDisconnectOthersIgnored(c *gc.C) {
   203  	w := s.worker(c)
   204  	defer workertest.CleanKill(c, w)
   205  
   206  	connect(s.recorder, agent1, agent2)
   207  
   208  	done, err := s.hub.Publish(
   209  		forwarder.DisconnectedTopic,
   210  		apiserver.OriginTarget{Origin: "machine-7", Target: otherServer})
   211  	c.Assert(err, jc.ErrorIsNil)
   212  	s.AssertDone(c, done)
   213  	s.AssertConnections(c, alive(agent1), alive(agent2))
   214  }
   215  
   216  func (s *PresenceSuite) TestConnectTopic(c *gc.C) {
   217  	w := s.worker(c)
   218  	defer workertest.CleanKill(c, w)
   219  
   220  	done, err := s.hub.Publish(
   221  		apiserver.ConnectTopic,
   222  		apiserver.APIConnection{
   223  			Origin:          "machine-5",
   224  			ModelUUID:       "model-uuid",
   225  			AgentTag:        "agent-2",
   226  			ConnectionID:    42,
   227  			ControllerAgent: true,
   228  			UserData:        "test",
   229  		})
   230  	c.Assert(err, jc.ErrorIsNil)
   231  	s.AssertDone(c, done)
   232  	s.AssertConnections(c, corepresence.Value{
   233  		Model:           "model-uuid",
   234  		Server:          "machine-5",
   235  		Agent:           "agent-2",
   236  		ConnectionID:    42,
   237  		Status:          corepresence.Alive,
   238  		ControllerAgent: true,
   239  		UserData:        "test",
   240  	})
   241  }
   242  
   243  func (s *PresenceSuite) TestDisconnectTopic(c *gc.C) {
   244  	w := s.worker(c)
   245  	defer workertest.CleanKill(c, w)
   246  
   247  	connect(s.recorder, agent1, agent2)
   248  
   249  	done, err := s.hub.Publish(
   250  		apiserver.DisconnectTopic,
   251  		apiserver.APIConnection{
   252  			Origin:       agent2.Server,
   253  			ConnectionID: agent2.ConnectionID,
   254  		})
   255  	c.Assert(err, jc.ErrorIsNil)
   256  	s.AssertDone(c, done)
   257  	s.AssertConnections(c, alive(agent1))
   258  }
   259  
   260  func (s *PresenceSuite) TestPresenceRequest(c *gc.C) {
   261  	w := s.worker(c)
   262  	defer workertest.CleanKill(c, w)
   263  
   264  	connect(s.recorder, agent1, agent2, agent3, agent4)
   265  
   266  	done := make(chan struct{})
   267  	unsub, err := s.hub.Subscribe(apiserver.PresenceResponseTopic, func(topic string, data apiserver.PresenceResponse, err error) {
   268  		c.Logf("handler called for %q", topic)
   269  		c.Check(err, jc.ErrorIsNil)
   270  		c.Check(data.Origin, gc.Equals, ourServer)
   271  
   272  		c.Check(data.Connections, gc.HasLen, 2)
   273  		s.CheckConnection(c, data.Connections[0], agent1)
   274  		s.CheckConnection(c, data.Connections[1], agent3)
   275  
   276  		close(done)
   277  	})
   278  	c.Assert(err, jc.ErrorIsNil)
   279  	defer unsub()
   280  
   281  	// When asked for our presence, we respond with the agents connected to us.
   282  	_, err = s.hub.Publish(
   283  		apiserver.PresenceRequestTopic,
   284  		apiserver.OriginTarget{Origin: otherServer, Target: ourServer})
   285  	c.Assert(err, jc.ErrorIsNil)
   286  	s.AssertDone(c, done)
   287  }
   288  
   289  func (s *PresenceSuite) TestPresenceRequestOtherServer(c *gc.C) {
   290  	w := s.worker(c)
   291  	defer workertest.CleanKill(c, w)
   292  
   293  	called := make(chan struct{})
   294  	unsub, err := s.hub.Subscribe(apiserver.PresenceResponseTopic, func(topic string, data apiserver.PresenceResponse, err error) {
   295  		c.Logf("handler called for %q", topic)
   296  		close(called)
   297  	})
   298  	c.Assert(err, jc.ErrorIsNil)
   299  	defer unsub()
   300  
   301  	// When presence requests come in for other servers, we ignore them.
   302  	_, err = s.hub.Publish(
   303  		apiserver.PresenceRequestTopic,
   304  		apiserver.OriginTarget{Origin: otherServer, Target: "another"})
   305  	c.Assert(err, jc.ErrorIsNil)
   306  	s.AssertNotCalled(c, called)
   307  }
   308  
   309  func (s *PresenceSuite) TestPresenceResponse(c *gc.C) {
   310  	w := s.worker(c)
   311  	defer workertest.CleanKill(c, w)
   312  
   313  	connect(s.recorder, agent1, agent2, agent3, agent4)
   314  	s.recorder.ServerDown(otherServer)
   315  
   316  	// When connections information comes from other servers, we update our recorder.
   317  	done, err := s.hub.Publish(
   318  		apiserver.PresenceResponseTopic,
   319  		apiserver.PresenceResponse{
   320  			Origin: otherServer,
   321  			Connections: []apiserver.APIConnection{
   322  				apiConn(agent2), apiConn(agent4),
   323  			},
   324  		})
   325  	c.Assert(err, jc.ErrorIsNil)
   326  	s.AssertDone(c, done)
   327  
   328  	s.AssertConnections(c, alive(agent1), alive(agent2), alive(agent3), alive(agent4))
   329  }
   330  
   331  func (s *PresenceSuite) AssertDone(c *gc.C, called <-chan struct{}) {
   332  	select {
   333  	case <-called:
   334  	case <-time.After(coretesting.LongWait):
   335  		c.Fatal("event not handled")
   336  	}
   337  }
   338  
   339  func (s *PresenceSuite) AssertNotCalled(c *gc.C, called <-chan struct{}) {
   340  	select {
   341  	case <-called:
   342  		c.Fatal("event called unexpectedly")
   343  	case <-time.After(coretesting.ShortWait):
   344  	}
   345  }
   346  
   347  func (s *PresenceSuite) AssertConnections(c *gc.C, values ...corepresence.Value) {
   348  	connections := s.recorder.Connections()
   349  	c.Log(pretty.Sprint(connections))
   350  	c.Assert(connections.Values(), jc.SameContents, values)
   351  }
   352  
   353  func (s *PresenceSuite) CheckConnection(c *gc.C, conn apiserver.APIConnection, agent corepresence.Value) {
   354  	c.Check(conn.AgentTag, gc.Equals, agent.Agent)
   355  	c.Check(conn.ControllerAgent, gc.Equals, agent.ControllerAgent)
   356  	c.Check(conn.ModelUUID, gc.Equals, agent.Model)
   357  	c.Check(conn.ConnectionID, gc.Equals, agent.ConnectionID)
   358  	c.Check(conn.Origin, gc.Equals, agent.Server)
   359  	c.Check(conn.UserData, gc.Equals, agent.UserData)
   360  }
   361  
   362  func apiConn(value corepresence.Value) apiserver.APIConnection {
   363  	return apiserver.APIConnection{
   364  		AgentTag:        value.Agent,
   365  		ControllerAgent: value.ControllerAgent,
   366  		ModelUUID:       value.Model,
   367  		ConnectionID:    value.ConnectionID,
   368  		Origin:          value.Server,
   369  		UserData:        value.UserData,
   370  	}
   371  }
   372  
   373  func alive(v corepresence.Value) corepresence.Value {
   374  	v.Status = corepresence.Alive
   375  	return v
   376  }
   377  
   378  func missing(v corepresence.Value) corepresence.Value {
   379  	v.Status = corepresence.Missing
   380  	return v
   381  }
   382  
   383  func connect(r corepresence.Recorder, values ...corepresence.Value) {
   384  	for _, info := range values {
   385  		r.Connect(info.Server, info.Model, info.Agent, info.ConnectionID, info.ControllerAgent, info.UserData)
   386  	}
   387  }
   388  
   389  const modelUUID = "model-uuid"
   390  
   391  var (
   392  	ourTag      = names.NewMachineTag("1")
   393  	ourServer   = ourTag.String()
   394  	otherServer = "machine-2"
   395  	agent1      = corepresence.Value{
   396  		Model:        modelUUID,
   397  		Server:       ourServer,
   398  		Agent:        "machine-0",
   399  		ConnectionID: 1237,
   400  		UserData:     "foo",
   401  	}
   402  	agent2 = corepresence.Value{
   403  		Model:        modelUUID,
   404  		Server:       otherServer,
   405  		Agent:        "machine-1",
   406  		ConnectionID: 1238,
   407  		UserData:     "bar",
   408  	}
   409  	agent3 = corepresence.Value{
   410  		Model:        modelUUID,
   411  		Server:       ourServer,
   412  		Agent:        "unit-ubuntu-0",
   413  		ConnectionID: 1239,
   414  		UserData:     "baz",
   415  	}
   416  	agent4 = corepresence.Value{
   417  		Model:        modelUUID,
   418  		Server:       otherServer,
   419  		Agent:        "unit-ubuntu-1",
   420  		ConnectionID: 1240,
   421  		UserData:     "splat",
   422  	}
   423  )