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