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

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package pubsub_test
     5  
     6  import (
     7  	"fmt"
     8  	"time"
     9  
    10  	"github.com/juju/clock/testclock"
    11  	"github.com/juju/errors"
    12  	"github.com/juju/loggo"
    13  	"github.com/juju/pubsub"
    14  	"github.com/juju/testing"
    15  	jc "github.com/juju/testing/checkers"
    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  	"github.com/juju/juju/api"
    22  	"github.com/juju/juju/apiserver/params"
    23  	"github.com/juju/juju/pubsub/apiserver"
    24  	"github.com/juju/juju/pubsub/centralhub"
    25  	coretesting "github.com/juju/juju/testing"
    26  	psworker "github.com/juju/juju/worker/pubsub"
    27  )
    28  
    29  type WorkerConfigSuite struct {
    30  }
    31  
    32  var _ = gc.Suite(&WorkerConfigSuite{})
    33  
    34  func (*WorkerConfigSuite) TestValidate(c *gc.C) {
    35  	logger := loggo.GetLogger("juju.worker.pubsub")
    36  	for i, test := range []struct {
    37  		cfg      psworker.WorkerConfig
    38  		errMatch string
    39  	}{
    40  		{
    41  			errMatch: "missing origin not valid",
    42  		}, {
    43  			cfg: psworker.WorkerConfig{
    44  				Origin: "origin",
    45  			},
    46  			errMatch: "missing clock not valid",
    47  		}, {
    48  			cfg: psworker.WorkerConfig{
    49  				Origin: "origin",
    50  				Clock:  testclock.NewClock(time.Now()),
    51  			},
    52  			errMatch: "missing hub not valid",
    53  		}, {
    54  			cfg: psworker.WorkerConfig{
    55  				Origin: "origin",
    56  				Clock:  testclock.NewClock(time.Now()),
    57  				Hub:    pubsub.NewStructuredHub(nil),
    58  			},
    59  			errMatch: "missing logger not valid",
    60  		}, {
    61  			cfg: psworker.WorkerConfig{
    62  				Origin: "origin",
    63  				Clock:  testclock.NewClock(time.Now()),
    64  				Hub:    pubsub.NewStructuredHub(nil),
    65  				Logger: logger,
    66  			},
    67  			errMatch: "missing api info not valid",
    68  		}, {
    69  			cfg: psworker.WorkerConfig{
    70  				Origin: "origin",
    71  				Clock:  testclock.NewClock(time.Now()),
    72  				Hub:    pubsub.NewStructuredHub(nil),
    73  				Logger: logger,
    74  				APIInfo: &api.Info{
    75  					Addrs: []string{"localhost"},
    76  				},
    77  			},
    78  			errMatch: "missing new writer not valid",
    79  		}, {
    80  			cfg: psworker.WorkerConfig{
    81  				Origin: "origin",
    82  				Clock:  testclock.NewClock(time.Now()),
    83  				Hub:    pubsub.NewStructuredHub(nil),
    84  				Logger: logger,
    85  				APIInfo: &api.Info{
    86  					Addrs: []string{"localhost"},
    87  				},
    88  				NewWriter: func(*api.Info) (psworker.MessageWriter, error) {
    89  					return &messageWriter{}, nil
    90  				},
    91  			},
    92  			errMatch: "missing new remote not valid",
    93  		}, {
    94  			cfg: psworker.WorkerConfig{
    95  				Origin: "origin",
    96  				Clock:  testclock.NewClock(time.Now()),
    97  				Hub:    pubsub.NewStructuredHub(nil),
    98  				Logger: logger,
    99  				APIInfo: &api.Info{
   100  					Addrs: []string{"localhost"},
   101  				},
   102  				NewWriter: func(*api.Info) (psworker.MessageWriter, error) {
   103  					return &messageWriter{}, nil
   104  				},
   105  				NewRemote: func(psworker.RemoteServerConfig) (psworker.RemoteServer, error) {
   106  					return &fakeRemote{}, nil
   107  				},
   108  			},
   109  		},
   110  	} {
   111  		c.Logf("test %d", i)
   112  		err := test.cfg.Validate()
   113  		if test.errMatch != "" {
   114  			c.Check(err, gc.ErrorMatches, test.errMatch)
   115  			c.Check(err, jc.Satisfies, errors.IsNotValid)
   116  		} else {
   117  			c.Check(err, jc.ErrorIsNil)
   118  		}
   119  	}
   120  }
   121  
   122  type SubscriberSuite struct {
   123  	testing.IsolationSuite
   124  	config  psworker.WorkerConfig
   125  	clock   *testclock.Clock
   126  	hub     *pubsub.StructuredHub
   127  	origin  string
   128  	remotes *fakeRemoteTracker
   129  }
   130  
   131  var _ = gc.Suite(&SubscriberSuite{})
   132  
   133  func (s *SubscriberSuite) SetUpTest(c *gc.C) {
   134  	s.IsolationSuite.SetUpTest(c)
   135  	logger := loggo.GetLogger("juju.worker.pubsub")
   136  	logger.SetLogLevel(loggo.TRACE)
   137  	// loggo.GetLogger("pubsub").SetLogLevel(loggo.TRACE)
   138  	tag := names.NewMachineTag("42")
   139  	s.clock = testclock.NewClock(time.Now())
   140  	s.hub = centralhub.New(tag)
   141  	s.origin = tag.String()
   142  	s.remotes = &fakeRemoteTracker{
   143  		remotes: make(map[string]*fakeRemote),
   144  	}
   145  	s.config = psworker.WorkerConfig{
   146  		Origin: s.origin,
   147  		Clock:  s.clock,
   148  		Hub:    s.hub,
   149  		Logger: logger,
   150  		APIInfo: &api.Info{
   151  			Addrs:  []string{"localhost"},
   152  			CACert: "fake as",
   153  			Tag:    tag,
   154  		},
   155  		NewWriter: func(*api.Info) (psworker.MessageWriter, error) {
   156  			return &messageWriter{}, nil
   157  		},
   158  		NewRemote: s.remotes.new,
   159  	}
   160  }
   161  
   162  func (s *SubscriberSuite) TestBadConfig(c *gc.C) {
   163  	s.config.Clock = nil
   164  	w, err := psworker.NewWorker(s.config)
   165  	c.Assert(err, gc.ErrorMatches, "missing clock not valid")
   166  	c.Assert(w, gc.IsNil)
   167  }
   168  
   169  func (s *SubscriberSuite) TestCleanShutdown(c *gc.C) {
   170  	w, err := psworker.NewWorker(s.config)
   171  	c.Assert(err, jc.ErrorIsNil)
   172  	workertest.CleanKill(c, w)
   173  }
   174  
   175  func (s *SubscriberSuite) TestNoInitialRemotes(c *gc.C) {
   176  	w, err := psworker.NewWorker(s.config)
   177  	c.Assert(err, jc.ErrorIsNil)
   178  	defer workertest.CleanKill(c, w)
   179  
   180  	c.Assert(s.remotes.remotes, gc.HasLen, 0)
   181  }
   182  
   183  func (s *SubscriberSuite) enableHA(c *gc.C) {
   184  	done, err := s.hub.Publish(apiserver.DetailsTopic, apiserver.Details{
   185  		Servers: map[string]apiserver.APIServer{
   186  			"3": {
   187  				ID:        "3",
   188  				Addresses: []string{"10.1.2.3"},
   189  			},
   190  			"5": {
   191  				ID:        "5",
   192  				Addresses: []string{"10.1.2.5"},
   193  			},
   194  			"42": {
   195  				ID:        "42",
   196  				Addresses: []string{"10.1.2.42"},
   197  			},
   198  		},
   199  		LocalOnly: true,
   200  	})
   201  	c.Assert(err, jc.ErrorIsNil)
   202  
   203  	select {
   204  	case <-done:
   205  	case <-time.After(coretesting.LongWait):
   206  		c.Fatal("message handling not completed")
   207  	}
   208  }
   209  
   210  func (s *SubscriberSuite) newHAWorker(c *gc.C) worker.Worker {
   211  	w, err := psworker.NewWorker(s.config)
   212  	c.Assert(err, jc.ErrorIsNil)
   213  	s.AddCleanup(func(c *gc.C) { workertest.CleanKill(c, w) })
   214  	s.enableHA(c)
   215  	return w
   216  }
   217  
   218  func (s *SubscriberSuite) TestEnableHA(c *gc.C) {
   219  	s.newHAWorker(c)
   220  
   221  	c.Assert(s.remotes.remotes, gc.HasLen, 2)
   222  	remote3 := s.remotes.remotes["machine-3"]
   223  	c.Assert(remote3.config.APIInfo.Addrs, jc.DeepEquals, []string{"10.1.2.3"})
   224  	remote5 := s.remotes.remotes["machine-5"]
   225  	c.Assert(remote5.config.APIInfo.Addrs, jc.DeepEquals, []string{"10.1.2.5"})
   226  }
   227  
   228  func (s *SubscriberSuite) TestEnableHAInternalAddress(c *gc.C) {
   229  	w, err := psworker.NewWorker(s.config)
   230  	c.Assert(err, jc.ErrorIsNil)
   231  	s.AddCleanup(func(c *gc.C) { workertest.CleanKill(c, w) })
   232  	done, err := s.hub.Publish(apiserver.DetailsTopic, apiserver.Details{
   233  		Servers: map[string]apiserver.APIServer{
   234  			"3": {
   235  				ID:              "3",
   236  				Addresses:       []string{"10.1.2.3"},
   237  				InternalAddress: "10.5.4.3",
   238  			},
   239  			"5": {
   240  				ID:              "5",
   241  				Addresses:       []string{"10.1.2.5"},
   242  				InternalAddress: "10.5.4.4",
   243  			},
   244  			"42": {
   245  				ID:              "42",
   246  				Addresses:       []string{"10.1.2.42"},
   247  				InternalAddress: "10.5.4.5",
   248  			},
   249  		},
   250  		LocalOnly: true,
   251  	})
   252  	c.Assert(err, jc.ErrorIsNil)
   253  
   254  	select {
   255  	case <-done:
   256  	case <-time.After(coretesting.LongWait):
   257  		c.Fatal("message handling not completed")
   258  	}
   259  	c.Assert(s.remotes.remotes, gc.HasLen, 2)
   260  	remote3 := s.remotes.remotes["machine-3"]
   261  	c.Assert(remote3.config.APIInfo.Addrs, jc.DeepEquals, []string{"10.5.4.3"})
   262  	remote5 := s.remotes.remotes["machine-5"]
   263  	c.Assert(remote5.config.APIInfo.Addrs, jc.DeepEquals, []string{"10.5.4.4"})
   264  }
   265  
   266  func (s *SubscriberSuite) TestSameMessagesForwarded(c *gc.C) {
   267  	s.newHAWorker(c)
   268  
   269  	var expected []*params.PubSubMessage
   270  	var last <-chan struct{}
   271  	for i := 0; i < 10; i++ {
   272  		message := &params.PubSubMessage{
   273  			Topic: fmt.Sprintf("topic.%d", i),
   274  			Data:  map[string]interface{}{"origin": "machine-42"},
   275  		}
   276  		expected = append(expected, message)
   277  		done, err := s.hub.Publish(message.Topic, nil)
   278  		c.Assert(err, jc.ErrorIsNil)
   279  		last = done
   280  	}
   281  	select {
   282  	case <-last:
   283  		c.Logf("message processing complete")
   284  	case <-time.After(coretesting.LongWait):
   285  		c.Fatal("messages not handled")
   286  	}
   287  
   288  	c.Assert(s.remotes.remotes, gc.HasLen, 2)
   289  	remote3 := s.remotes.remotes["machine-3"]
   290  	remote5 := s.remotes.remotes["machine-5"]
   291  
   292  	c.Assert(remote3.messages, jc.DeepEquals, expected)
   293  	c.Assert(remote5.messages, jc.DeepEquals, expected)
   294  }
   295  
   296  func (s *SubscriberSuite) TestLocalMessagesNotForwarded(c *gc.C) {
   297  	s.newHAWorker(c)
   298  
   299  	var last <-chan struct{}
   300  	for i := 0; i < 10; i++ {
   301  		done, err := s.hub.Publish("local.message", map[string]interface{}{
   302  			"foo":        "bar",
   303  			"local-only": true,
   304  		})
   305  		c.Assert(err, jc.ErrorIsNil)
   306  		last = done
   307  	}
   308  	select {
   309  	case <-last:
   310  		c.Logf("message processing complete")
   311  	case <-time.After(coretesting.LongWait):
   312  		c.Fatal("messages not handled")
   313  	}
   314  
   315  	c.Assert(s.remotes.remotes, gc.HasLen, 2)
   316  	remote3 := s.remotes.remotes["machine-3"]
   317  	remote5 := s.remotes.remotes["machine-5"]
   318  
   319  	c.Assert(remote3.messages, gc.HasLen, 0)
   320  	c.Assert(remote5.messages, gc.HasLen, 0)
   321  }
   322  
   323  func (s *SubscriberSuite) TestOtherOriginMessagesNotForwarded(c *gc.C) {
   324  	s.newHAWorker(c)
   325  
   326  	var last <-chan struct{}
   327  	for i := 0; i < 10; i++ {
   328  		done, err := s.hub.Publish("not.ours", map[string]interface{}{
   329  			"foo":    "bar",
   330  			"origin": "other",
   331  		})
   332  		c.Assert(err, jc.ErrorIsNil)
   333  		last = done
   334  	}
   335  	select {
   336  	case <-last:
   337  		c.Logf("message processing complete")
   338  	case <-time.After(coretesting.LongWait):
   339  		c.Fatal("messages not handled")
   340  	}
   341  
   342  	c.Assert(s.remotes.remotes, gc.HasLen, 2)
   343  	remote3 := s.remotes.remotes["machine-3"]
   344  	remote5 := s.remotes.remotes["machine-5"]
   345  
   346  	c.Assert(remote3.messages, gc.HasLen, 0)
   347  	c.Assert(remote5.messages, gc.HasLen, 0)
   348  }
   349  
   350  func (s *SubscriberSuite) TestIntrospectionReport(c *gc.C) {
   351  	w := s.newHAWorker(c)
   352  
   353  	r, ok := w.(psworker.Reporter)
   354  	c.Assert(ok, jc.IsTrue)
   355  	c.Assert(r.IntrospectionReport(), gc.Equals, ""+
   356  		"Source: machine-42\n"+
   357  		"\n"+
   358  		"Target: machine-3\n"+
   359  		"  Status: connected\n"+
   360  		"  Addresses: [10.1.2.3]\n"+
   361  		"\n"+
   362  		"Target: machine-5\n"+
   363  		"  Status: connected\n"+
   364  		"  Addresses: [10.1.2.5]\n")
   365  }
   366  
   367  func (s *SubscriberSuite) TestReport(c *gc.C) {
   368  	w := s.newHAWorker(c)
   369  
   370  	r, ok := w.(psworker.Reporter)
   371  	c.Assert(ok, jc.IsTrue)
   372  	c.Assert(r.Report(), jc.DeepEquals, map[string]interface{}{
   373  		"source": "machine-42",
   374  		"targets": map[string]interface{}{
   375  			"machine-3": map[string]interface{}{
   376  				"status":    "connected",
   377  				"addresses": []string{"10.1.2.3"},
   378  			},
   379  			"machine-5": map[string]interface{}{
   380  				"status":    "connected",
   381  				"addresses": []string{"10.1.2.5"},
   382  			},
   383  		}})
   384  }
   385  
   386  func (s *SubscriberSuite) TestRequestsDetailsOnceSubscribed(c *gc.C) {
   387  	subscribed := make(chan apiserver.DetailsRequest)
   388  	s.config.Hub.Subscribe(apiserver.DetailsRequestTopic,
   389  		func(_ string, req apiserver.DetailsRequest, err error) {
   390  			c.Check(err, jc.ErrorIsNil)
   391  			subscribed <- req
   392  		},
   393  	)
   394  
   395  	s.newHAWorker(c)
   396  
   397  	select {
   398  	case req := <-subscribed:
   399  		c.Assert(req, gc.Equals, apiserver.DetailsRequest{Requester: "pubsub-forwarder", LocalOnly: true})
   400  	case <-time.After(coretesting.LongWait):
   401  		c.Fatalf("timed out waiting for details request")
   402  	}
   403  }
   404  
   405  var logger = loggo.GetLogger("workertest")
   406  
   407  type fakeRemoteTracker struct {
   408  	remotes map[string]*fakeRemote
   409  }
   410  
   411  func (f *fakeRemoteTracker) new(config psworker.RemoteServerConfig) (psworker.RemoteServer, error) {
   412  	remote := &fakeRemote{config: config}
   413  	f.remotes[config.Target] = remote
   414  	return remote, nil
   415  }
   416  
   417  type fakeRemote struct {
   418  	psworker.RemoteServer
   419  	config   psworker.RemoteServerConfig
   420  	messages []*params.PubSubMessage
   421  }
   422  
   423  func (f *fakeRemote) Report() map[string]interface{} {
   424  	return map[string]interface{}{
   425  		"status":    "connected",
   426  		"addresses": f.config.APIInfo.Addrs,
   427  	}
   428  }
   429  
   430  func (f *fakeRemote) IntrospectionReport() string {
   431  	return fmt.Sprintf(""+
   432  		"  Status: connected\n"+
   433  		"  Addresses: %v\n",
   434  		f.config.APIInfo.Addrs)
   435  }
   436  
   437  func (f *fakeRemote) Publish(message *params.PubSubMessage) {
   438  	logger.Debugf("fakeRemote.Publish %s to %s", message.Topic, f.config.Target)
   439  	f.messages = append(f.messages, message)
   440  }
   441  func (f *fakeRemote) UpdateAddresses(addresses []string) {
   442  	f.config.APIInfo.Addrs = addresses
   443  }
   444  func (*fakeRemote) Kill()       {}
   445  func (*fakeRemote) Wait() error { return nil }