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 }