github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/worker/certupdater/certupdater_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package certupdater_test
     5  
     6  import (
     7  	"crypto/x509"
     8  	stdtesting "testing"
     9  	"time"
    10  
    11  	jc "github.com/juju/testing/checkers"
    12  	"github.com/juju/utils/set"
    13  	gc "gopkg.in/check.v1"
    14  
    15  	"github.com/juju/juju/apiserver/params"
    16  	"github.com/juju/juju/cert"
    17  	"github.com/juju/juju/environs/config"
    18  	"github.com/juju/juju/network"
    19  	"github.com/juju/juju/state"
    20  	coretesting "github.com/juju/juju/testing"
    21  	"github.com/juju/juju/worker/certupdater"
    22  )
    23  
    24  func TestPackage(t *stdtesting.T) {
    25  	gc.TestingT(t)
    26  }
    27  
    28  type CertUpdaterSuite struct {
    29  	coretesting.BaseSuite
    30  	stateServingInfo params.StateServingInfo
    31  }
    32  
    33  var _ = gc.Suite(&CertUpdaterSuite{})
    34  
    35  func (s *CertUpdaterSuite) SetUpTest(c *gc.C) {
    36  	s.BaseSuite.SetUpTest(c)
    37  
    38  	s.stateServingInfo = params.StateServingInfo{
    39  		Cert:         coretesting.ServerCert,
    40  		PrivateKey:   coretesting.ServerKey,
    41  		CAPrivateKey: coretesting.CAKey,
    42  		StatePort:    123,
    43  		APIPort:      456,
    44  	}
    45  }
    46  
    47  type mockNotifyWatcher struct {
    48  	changes <-chan struct{}
    49  }
    50  
    51  func (w *mockNotifyWatcher) Changes() <-chan struct{} {
    52  	return w.changes
    53  }
    54  
    55  func (*mockNotifyWatcher) Stop() error {
    56  	return nil
    57  }
    58  
    59  func (*mockNotifyWatcher) Kill() {}
    60  
    61  func (*mockNotifyWatcher) Wait() error {
    62  	return nil
    63  }
    64  
    65  func (*mockNotifyWatcher) Err() error {
    66  	return nil
    67  }
    68  
    69  func newMockNotifyWatcher(changes <-chan struct{}) state.NotifyWatcher {
    70  	return &mockNotifyWatcher{changes}
    71  }
    72  
    73  type mockMachine struct {
    74  	changes chan struct{}
    75  }
    76  
    77  func (m *mockMachine) WatchAddresses() state.NotifyWatcher {
    78  	return newMockNotifyWatcher(m.changes)
    79  }
    80  
    81  func (m *mockMachine) Addresses() (addresses []network.Address) {
    82  	return []network.Address{{
    83  		Value: "0.1.2.3",
    84  	}}
    85  }
    86  
    87  func (s *CertUpdaterSuite) StateServingInfo() (params.StateServingInfo, bool) {
    88  	return s.stateServingInfo, true
    89  }
    90  
    91  type mockConfigGetter struct{}
    92  
    93  func (g *mockConfigGetter) EnvironConfig() (*config.Config, error) {
    94  	return config.New(config.NoDefaults, coretesting.FakeConfig())
    95  
    96  }
    97  
    98  type mockAPIHostGetter struct{}
    99  
   100  func (g *mockAPIHostGetter) APIHostPorts() ([][]network.HostPort, error) {
   101  	return [][]network.HostPort{
   102  		{
   103  			{Address: network.Address{Value: "192.168.1.1", Scope: network.ScopeCloudLocal}, Port: 17070},
   104  			{Address: network.Address{Value: "10.1.1.1", Scope: network.ScopeMachineLocal}, Port: 17070},
   105  		},
   106  	}, nil
   107  }
   108  
   109  func (s *CertUpdaterSuite) TestStartStop(c *gc.C) {
   110  	var initialAddresses []string
   111  	setter := func(info params.StateServingInfo, dying <-chan struct{}) error {
   112  		// Only care about first time called.
   113  		if len(initialAddresses) > 0 {
   114  			return nil
   115  		}
   116  		srvCert, err := cert.ParseCert(info.Cert)
   117  		c.Assert(err, jc.ErrorIsNil)
   118  		initialAddresses = make([]string, len(srvCert.IPAddresses))
   119  		for i, ip := range srvCert.IPAddresses {
   120  			initialAddresses[i] = ip.String()
   121  		}
   122  		return nil
   123  	}
   124  	changes := make(chan struct{})
   125  	certChangedChan := make(chan params.StateServingInfo)
   126  	worker := certupdater.NewCertificateUpdater(
   127  		&mockMachine{changes}, s, &mockConfigGetter{}, &mockAPIHostGetter{}, setter, certChangedChan,
   128  	)
   129  	worker.Kill()
   130  	c.Assert(worker.Wait(), gc.IsNil)
   131  	// Initial cert addresses initialised to cloud local ones.
   132  	c.Assert(initialAddresses, jc.DeepEquals, []string{"192.168.1.1"})
   133  }
   134  
   135  func (s *CertUpdaterSuite) TestAddressChange(c *gc.C) {
   136  	var srvCert *x509.Certificate
   137  	updated := make(chan struct{})
   138  	setter := func(info params.StateServingInfo, dying <-chan struct{}) error {
   139  		s.stateServingInfo = info
   140  		var err error
   141  		srvCert, err = cert.ParseCert(info.Cert)
   142  		c.Assert(err, jc.ErrorIsNil)
   143  		sanIPs := make([]string, len(srvCert.IPAddresses))
   144  		for i, ip := range srvCert.IPAddresses {
   145  			sanIPs[i] = ip.String()
   146  		}
   147  		sanIPsSet := set.NewStrings(sanIPs...)
   148  		if sanIPsSet.Size() == 2 && sanIPsSet.Contains("0.1.2.3") && sanIPsSet.Contains("192.168.1.1") {
   149  			close(updated)
   150  		}
   151  		return nil
   152  	}
   153  	changes := make(chan struct{})
   154  	certChangedChan := make(chan params.StateServingInfo)
   155  	worker := certupdater.NewCertificateUpdater(
   156  		&mockMachine{changes}, s, &mockConfigGetter{}, &mockAPIHostGetter{}, setter, certChangedChan,
   157  	)
   158  	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
   159  	defer worker.Kill()
   160  
   161  	changes <- struct{}{}
   162  	// Certificate should be updated with the address value.
   163  	select {
   164  	case <-updated:
   165  	case <-time.After(coretesting.LongWait):
   166  		c.Fatalf("timed out waiting for certificate to be updated")
   167  	}
   168  
   169  	// The server certificates must report "juju-apiserver" as a DNS
   170  	// name for backwards-compatibility with API clients. They must
   171  	// also report "juju-mongodb" because these certicates are also
   172  	// used for serving MongoDB connections.
   173  	c.Assert(srvCert.DNSNames, jc.SameContents,
   174  		[]string{"localhost", "juju-apiserver", "juju-mongodb", "anything"})
   175  }
   176  
   177  type mockStateServingGetterNoCAKey struct{}
   178  
   179  func (g *mockStateServingGetterNoCAKey) StateServingInfo() (params.StateServingInfo, bool) {
   180  	return params.StateServingInfo{
   181  		Cert:       coretesting.ServerCert,
   182  		PrivateKey: coretesting.ServerKey,
   183  		StatePort:  123,
   184  		APIPort:    456,
   185  	}, true
   186  
   187  }
   188  
   189  func (s *CertUpdaterSuite) TestAddressChangeNoCAKey(c *gc.C) {
   190  	updated := make(chan struct{})
   191  	setter := func(info params.StateServingInfo, dying <-chan struct{}) error {
   192  		close(updated)
   193  		return nil
   194  	}
   195  	changes := make(chan struct{})
   196  	certChangedChan := make(chan params.StateServingInfo)
   197  	worker := certupdater.NewCertificateUpdater(
   198  		&mockMachine{changes}, &mockStateServingGetterNoCAKey{}, &mockConfigGetter{}, &mockAPIHostGetter{}, setter, certChangedChan,
   199  	)
   200  	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
   201  	defer worker.Kill()
   202  
   203  	changes <- struct{}{}
   204  	// Certificate should not be updated with the address value.
   205  	select {
   206  	case <-time.After(coretesting.ShortWait):
   207  	case <-updated:
   208  		c.Fatalf("set state serving info unexpectedly called")
   209  	}
   210  }