120
120
return expectState, nil
123
// Give NewAPIFromStore a store interface that can report when the
124
// config was written to, to check if the cache is updated.
125
mockStore := &storageWithWriteNotify{store: legacyStore}
126
st, err := juju.NewAPIFromStore("noconfig", "admin@local", "noconfig", mockStore, store, apiOpen)
123
st, err := newAPIConnectionFromNames(c, "noconfig", "admin@local", "admin", store, apiOpen, noBootstrapConfig)
127
124
c.Assert(err, jc.ErrorIsNil)
128
125
c.Assert(st, gc.Equals, expectState)
129
126
c.Assert(called, gc.Equals, 1)
130
c.Assert(mockStore.written, jc.IsTrue)
131
info, err := legacyStore.ReadInfo("noconfig:noconfig")
132
c.Assert(err, jc.ErrorIsNil)
133
ep := info.APIEndpoint()
134
c.Check(ep.Addresses, jc.DeepEquals, []string{
135
"0.1.2.3:1234", "[2001:db8::1]:1234",
137
c.Check(ep.ModelUUID, gc.Equals, fakeUUID)
138
mockStore.written = false
127
// The addresses should have been updated.
129
store.Controllers["noconfig"].APIEndpoints,
131
[]string{"0.1.2.3:1234", "[2001:db8::1]:1234"},
140
134
controllerBefore, err := store.ControllerByName("noconfig")
141
135
c.Assert(err, jc.ErrorIsNil)
143
137
// If APIHostPorts haven't changed, then the store won't be updated.
144
st, err = juju.NewAPIFromStore("noconfig", "admin@local", "noconfig", mockStore, store, apiOpen)
138
stubStore := jujuclienttesting.WrapClientStore(store)
139
st, err = newAPIConnectionFromNames(c, "noconfig", "admin@local", "admin", stubStore, apiOpen, noBootstrapConfig)
145
140
c.Assert(err, jc.ErrorIsNil)
146
141
c.Assert(st, gc.Equals, expectState)
147
142
c.Assert(called, gc.Equals, 2)
148
c.Assert(mockStore.written, jc.IsFalse)
143
stubStore.CheckCallNames(c, "AccountByName", "ModelByName", "ControllerByName")
150
145
controllerAfter, err := store.ControllerByName("noconfig")
151
146
c.Assert(err, jc.ErrorIsNil)
155
150
func (s *NewAPIClientSuite) TestWithInfoError(c *gc.C) {
151
store := newClientStore(c, "noconfig")
152
err := store.UpdateController("noconfig", jujuclient.ControllerDetails{
153
ControllerUUID: fakeUUID,
154
CACert: "certificate",
156
c.Assert(err, jc.ErrorIsNil)
156
158
expectErr := fmt.Errorf("an error")
157
legacyStore := newConfigStoreWithError(expectErr)
158
store := newClientStore(c, "noconfig", &environInfo{
159
endpoint: configstore.APIEndpoint{
160
ServerUUID: "valid.uuid",
161
CACert: "certificated",
164
client, err := juju.NewAPIFromStore("noconfig", "", "", legacyStore, store, panicAPIOpen)
159
getBootstrapConfig := func(string) (*config.Config, error) {
160
return nil, expectErr
163
client, err := newAPIConnectionFromNames(c, "noconfig", "", "", store, panicAPIOpen, getBootstrapConfig)
165
164
c.Assert(errors.Cause(err), gc.Equals, expectErr)
166
165
c.Assert(client, gc.IsNil)
169
168
func (s *NewAPIClientSuite) TestWithInfoNoAddresses(c *gc.C) {
170
store := newConfigStore("noconfig", &environInfo{
171
endpoint: configstore.APIEndpoint{
172
Addresses: []string{},
173
CACert: "certificated",
176
cache := newClientStore(c, "noconfig", &environInfo{
177
endpoint: configstore.APIEndpoint{
178
Addresses: []string{},
179
ServerUUID: "valid.uuid",
180
ModelUUID: "valid.uuid",
181
CACert: "certificated",
184
st, err := juju.NewAPIFromStore("noconfig", "admin@local", "noconfig", store, cache, panicAPIOpen)
185
c.Assert(err, gc.ErrorMatches, "bootstrap config not found")
169
store := newClientStore(c, "noconfig")
170
err := store.UpdateController("noconfig", jujuclient.ControllerDetails{
171
ControllerUUID: fakeUUID,
172
CACert: "certificate",
174
c.Assert(err, jc.ErrorIsNil)
176
st, err := newAPIConnectionFromNames(c, "noconfig", "admin@local", "", store, panicAPIOpen, noBootstrapConfig)
177
c.Assert(err, gc.ErrorMatches, "bootstrap config for controller noconfig not found")
186
178
c.Assert(st, gc.IsNil)
189
var noTagStoreInfo = &environInfo{
190
creds: configstore.APICredentials{
194
endpoint: configstore.APIEndpoint{
195
Addresses: []string{"foo.invalid"},
196
CACert: "certificated",
200
181
type mockedStateFlags int
203
noFlags mockedStateFlags = 0x0000
204
mockedHostPort mockedStateFlags = 0x0001
205
mockedModelTag mockedStateFlags = 0x0002
206
mockedPreferIPv6 mockedStateFlags = 0x0004
184
noFlags mockedStateFlags = 0x0000
185
mockedHostPort mockedStateFlags = 0x0001
186
mockedModelTag mockedStateFlags = 0x0002
209
189
func mockedAPIState(flags mockedStateFlags) *mockAPIState {
210
190
hasHostPort := flags&mockedHostPort == mockedHostPort
211
191
hasModelTag := flags&mockedModelTag == mockedModelTag
212
preferIPv6 := flags&mockedPreferIPv6 == mockedPreferIPv6
215
194
apiHostPorts := [][]network.HostPort{}
243
217
func checkCommonAPIInfoAttrs(c *gc.C, apiInfo *api.Info, opts api.DialOpts) {
244
218
c.Check(apiInfo.Tag, gc.Equals, names.NewUserTag("admin@local"))
245
c.Check(string(apiInfo.CACert), gc.Equals, "certificated")
219
c.Check(string(apiInfo.CACert), gc.Equals, "certificate")
246
220
c.Check(apiInfo.Password, gc.Equals, "hunter2")
247
221
c.Check(opts, gc.DeepEquals, api.DefaultDialOpts())
250
func (s *NewAPIClientSuite) TestWithInfoNoAPIHostports(c *gc.C) {
251
// The API doesn't have apiHostPorts, we don't want to
252
// override the local cache with bad endpoints.
253
legacyStore := newConfigStore("noconfig", noTagStoreInfo)
254
store := newClientStore(c, "noconfig", dummyStoreInfo)
257
expectState := mockedAPIState(mockedModelTag | mockedPreferIPv6)
258
apiOpen := func(apiInfo *api.Info, opts api.DialOpts) (api.Connection, error) {
259
checkCommonAPIInfoAttrs(c, apiInfo, opts)
260
c.Check(apiInfo.ModelTag.Id(), gc.Equals, "")
262
return expectState, nil
265
mockStore := &storageWithWriteNotify{store: legacyStore}
266
st, err := juju.NewAPIFromStore("noconfig", "admin@local", "", mockStore, store, apiOpen)
267
c.Assert(err, jc.ErrorIsNil)
268
c.Assert(st, gc.Equals, expectState)
269
c.Assert(called, gc.Equals, 1)
271
// We should not have disturbed the Addresses
273
details, err := store.ControllerByName("noconfig")
274
c.Assert(err, jc.ErrorIsNil)
275
c.Check(details.APIEndpoints, gc.HasLen, 1)
276
c.Check(details.APIEndpoints[0], gc.Matches, `foo\.invalid`)
278
info, err := legacyStore.ReadInfo("noconfig:noconfig")
279
c.Assert(err, jc.ErrorIsNil)
280
ep := info.APIEndpoint()
281
c.Check(ep.Addresses, gc.HasLen, 1)
282
c.Check(ep.Addresses[0], gc.Matches, `foo\.invalid`)
285
224
func (s *NewAPIClientSuite) TestWithInfoAPIOpenError(c *gc.C) {
286
store := newConfigStore("noconfig", &environInfo{
287
endpoint: configstore.APIEndpoint{
288
Addresses: []string{"foo.invalid"},
291
jujuClient := newClientStore(c, "noconfig", &environInfo{
292
endpoint: configstore.APIEndpoint{
293
Addresses: []string{"foo.invalid"},
294
ServerUUID: "some.uuid",
225
jujuClient := newClientStore(c, "noconfig")
299
227
apiOpen := func(apiInfo *api.Info, opts api.DialOpts) (api.Connection, error) {
300
228
return nil, errors.Errorf("an error")
302
st, err := juju.NewAPIFromStore("noconfig", "", "", store, jujuClient, apiOpen)
303
// We expect to get the isNotFound error as it is more important than the
304
// infoConnectError "an error"
305
c.Assert(err, gc.ErrorMatches, "bootstrap config not found")
230
st, err := newAPIConnectionFromNames(c, "noconfig", "", "", jujuClient, apiOpen, noBootstrapConfig)
231
// We expect to get the error from apiOpen, because it is not
232
// fatal to have no bootstrap config.
233
c.Assert(err, gc.ErrorMatches, "connecting with cached addresses: an error")
306
234
c.Assert(st, gc.IsNil)
309
237
func (s *NewAPIClientSuite) TestWithSlowInfoConnect(c *gc.C) {
310
238
c.Skip("wallyworld - this is a dumb test relying on an arbitary 50ms delay to pass")
311
s.PatchValue(&version.Current, coretesting.FakeVersionNumber)
312
legacyStore, store := s.bootstrapEnv(c)
239
s.PatchValue(&jujuversion.Current, coretesting.FakeVersionNumber)
240
_, store := s.bootstrapModel(c)
313
241
setEndpointAddressAndHostname(c, store, "0.1.2.3", "infoapi.invalid")
315
243
infoOpenedState := mockedAPIState(noFlags)
360
func (s *NewAPIClientSuite) TestGetBootstrapConfigNoLegacyInfo(c *gc.C) {
361
store := configstore.NewMem()
362
cfg, err := juju.GetBootstrapConfig(store, "whatever", "")
363
c.Assert(err, gc.ErrorMatches, `getting controller info: model "whatever:whatever" not found`)
364
c.Assert(cfg, gc.IsNil)
367
func (s *NewAPIClientSuite) TestGetBootstrapConfigNoBootstrapConfig(c *gc.C) {
368
store := configstore.NewMem()
369
info := store.CreateInfo("whatever:whatever")
371
c.Assert(err, jc.ErrorIsNil)
372
cfg, err := juju.GetBootstrapConfig(store, "whatever", "")
373
c.Assert(err, gc.ErrorMatches, "bootstrap config not found")
374
c.Assert(cfg, gc.IsNil)
377
func (s *NewAPIClientSuite) TestGetBootstrapConfigBadConfigDoesntPanic(c *gc.C) {
378
store := configstore.NewMem()
379
info := store.CreateInfo("whatever:whatever")
380
info.SetBootstrapConfig(map[string]interface{}{"something": "else"})
382
c.Assert(err, jc.ErrorIsNil)
383
cfg, err := juju.GetBootstrapConfig(store, "whatever", "")
384
// The specific error we get depends on what key is invalid, which is a
385
// bit spurious, but what we care about is that we didn't get a panic,
386
// but instead got an error
387
c.Assert(err, gc.ErrorMatches, ".*expected.*got nothing")
388
c.Assert(cfg, gc.IsNil)
391
291
func setEndpointAddressAndHostname(c *gc.C, store jujuclient.ControllerStore, addr, host string) {
392
292
// Populate the controller details with known address and hostname.
393
293
details, err := store.ControllerByName("local.my-controller")
394
294
c.Assert(err, jc.ErrorIsNil)
395
295
details.APIEndpoints = []string{addr}
396
details.Servers = []string{host}
296
details.UnresolvedAPIEndpoints = []string{host}
397
297
err = store.UpdateController("local.my-controller", *details)
398
298
c.Assert(err, jc.ErrorIsNil)
401
301
func (s *NewAPIClientSuite) TestWithSlowConfigConnect(c *gc.C) {
402
s.PatchValue(&version.Current, coretesting.FakeVersionNumber)
302
s.PatchValue(&jujuversion.Current, coretesting.FakeVersionNumber)
404
legacyStore, store := s.bootstrapEnv(c)
304
_, store := s.bootstrapModel(c)
405
305
setEndpointAddressAndHostname(c, store, "0.1.2.3", "infoapi.invalid")
407
307
infoOpenedState := mockedAPIState(noFlags)
480
387
return nil, fmt.Errorf("config connect failed")
482
st, err := juju.NewAPIFromStore("local.my-controller", "admin@local", "only", legacyStore, store, apiOpen)
483
c.Check(err, gc.ErrorMatches, "config connect failed")
389
st, err := newAPIConnectionFromNames(c, "local.my-controller", "admin@local", "only", store, apiOpen, getBootstrapConfig)
390
c.Check(err, gc.ErrorMatches, "connecting with bootstrap config: config connect failed")
484
391
c.Check(st, gc.IsNil)
487
func defaultConfigStore(c *gc.C) configstore.Storage {
488
store, err := configstore.Default()
489
c.Assert(err, jc.ErrorIsNil)
493
func (s *NewAPIClientSuite) TestWithBootstrapConfigAndNoEnvironmentsFile(c *gc.C) {
494
s.PatchValue(&version.Current, coretesting.FakeVersionNumber)
495
legacyStore, store := s.bootstrapEnv(c)
497
details, err := store.ControllerByName("local.my-controller")
498
c.Assert(err, jc.ErrorIsNil)
499
c.Assert(details.APIEndpoints, gc.HasLen, 0)
501
info, err := legacyStore.ReadInfo("local.my-controller:only")
502
c.Assert(err, jc.ErrorIsNil)
503
c.Assert(info.BootstrapConfig(), gc.NotNil)
504
c.Assert(info.APIEndpoint().Addresses, gc.HasLen, 0)
506
apiOpen := func(*api.Info, api.DialOpts) (api.Connection, error) {
507
return mockedAPIState(noFlags), nil
509
st, err := juju.NewAPIFromStore("local.my-controller", "admin@local", "only", legacyStore, store, apiOpen)
510
c.Check(err, jc.ErrorIsNil)
514
// newConfigStoreWithError that will return the given
515
// error from ReadInfo.
516
func newConfigStoreWithError(err error) configstore.Storage {
517
return &errorConfigStorage{
518
Storage: configstore.NewMem(),
523
type errorConfigStorage struct {
528
func (store *errorConfigStorage) ReadInfo(envName string) (configstore.EnvironInfo, error) {
529
return nil, store.err
532
type environInfo struct {
533
creds configstore.APICredentials
534
endpoint configstore.APIEndpoint
535
bootstrapConfig map[string]interface{}
538
// newConfigStore returns a storage that contains information
539
// for the environment name.
540
func newConfigStore(envName string, info *environInfo) configstore.Storage {
541
store := configstore.NewMem()
542
newInfo := store.CreateInfo(envName + ":" + envName)
543
newInfo.SetAPICredentials(info.creds)
544
newInfo.SetAPIEndpoint(info.endpoint)
545
newInfo.SetBootstrapConfig(info.bootstrapConfig)
546
err := newInfo.Write()
553
394
// newClientStore returns a client store that contains information
554
395
// based on the given controller namd and info.
555
func newClientStore(c *gc.C, controllerName string, info *environInfo) jujuclient.ClientStore {
396
func newClientStore(c *gc.C, controllerName string) *jujuclienttesting.MemStore {
556
397
store := jujuclienttesting.NewMemStore()
557
398
err := store.UpdateController(controllerName, jujuclient.ControllerDetails{
558
info.endpoint.Hostnames,
559
info.endpoint.ServerUUID,
560
info.endpoint.Addresses,
561
info.endpoint.CACert,
563
c.Assert(err, jc.ErrorIsNil)
565
if info.endpoint.ModelUUID != "" {
566
err = store.UpdateModel(controllerName, "admin@local", controllerName, jujuclient.ModelDetails{
567
info.endpoint.ModelUUID,
569
c.Assert(err, jc.ErrorIsNil)
571
// Models belong to accounts, so we must have an account even
572
// if "creds" is not initialised. If it is, it may overwrite
574
err = store.UpdateAccount(controllerName, "admin@local", jujuclient.AccountDetails{
577
c.Assert(err, jc.ErrorIsNil)
578
err = store.SetCurrentAccount(controllerName, "admin@local")
579
c.Assert(err, jc.ErrorIsNil)
582
if info.creds.User != "" {
583
user := names.NewUserTag(info.creds.User).Canonical()
584
err = store.UpdateAccount(controllerName, user, jujuclient.AccountDetails{
586
Password: info.creds.Password,
588
c.Assert(err, jc.ErrorIsNil)
589
err = store.SetCurrentAccount(controllerName, user)
590
c.Assert(err, jc.ErrorIsNil)
399
ControllerUUID: fakeUUID,
400
CACert: "certificate",
401
APIEndpoints: []string{"foo.invalid"},
403
c.Assert(err, jc.ErrorIsNil)
405
err = store.UpdateModel(controllerName, "admin@local", "admin", jujuclient.ModelDetails{
408
c.Assert(err, jc.ErrorIsNil)
410
// Models belong to accounts, so we must have an account even
411
// if "creds" is not initialised. If it is, it may overwrite
413
err = store.UpdateAccount(controllerName, "admin@local", jujuclient.AccountDetails{
417
c.Assert(err, jc.ErrorIsNil)
418
err = store.SetCurrentAccount(controllerName, "admin@local")
419
c.Assert(err, jc.ErrorIsNil)
596
type storageWithWriteNotify struct {
598
store configstore.Storage
601
func (*storageWithWriteNotify) CreateInfo(envName string) configstore.EnvironInfo {
602
panic("CreateInfo not implemented")
605
func (*storageWithWriteNotify) List() ([]string, error) {
609
func (*storageWithWriteNotify) ListSystems() ([]string, error) {
610
return []string{"noconfig:noconfig"}, nil
613
func (s *storageWithWriteNotify) ReadInfo(envName string) (configstore.EnvironInfo, error) {
614
info, err := s.store.ReadInfo(envName)
618
return &infoWithWriteNotify{
624
type infoWithWriteNotify struct {
625
configstore.EnvironInfo
629
func (info *infoWithWriteNotify) Write() error {
631
return info.EnvironInfo.Write()
634
423
type CacheAPIEndpointsSuite struct {
635
424
jujutesting.JujuConnSuite
637
426
hostPorts [][]network.HostPort
638
427
modelTag names.ModelTag
639
428
apiHostPort network.HostPort
640
store configstore.Storage
643
431
resolveNumCalls int
682
468
s.resolveNumCalls = 0
683
469
s.numResolved = 0
684
470
s.modelTag = names.NewModelTag(fakeUUID)
685
s.store = configstore.NewMem()
687
472
s.JujuConnSuite.SetUpTest(c)
473
s.PatchValue(juju.ResolveOrDropHostnames, s.mockResolveOrDropHostnames)
689
475
apiHostPort, err := network.ParseHostPorts(s.APIState.Addr())
690
476
c.Assert(err, jc.ErrorIsNil)
691
477
s.apiHostPort = apiHostPort[0]
694
func (s *CacheAPIEndpointsSuite) assertCreateInfo(c *gc.C, name string) configstore.EnvironInfo {
695
info := s.store.CreateInfo(name)
697
// info should have server uuid.
698
updateEndpoint := info.APIEndpoint()
699
updateEndpoint.ServerUUID = fakeUUID
700
info.SetAPIEndpoint(updateEndpoint)
480
func (s *CacheAPIEndpointsSuite) assertCreateController(c *gc.C, name string) jujuclient.ControllerDetails {
702
481
// write controller
703
c.Assert(updateEndpoint.Hostnames, gc.HasLen, 0)
704
c.Assert(updateEndpoint.Addresses, gc.HasLen, 0)
705
482
controllerDetails := jujuclient.ControllerDetails{
706
updateEndpoint.Hostnames,
708
updateEndpoint.Addresses,
709
"this.is.ca.cert.but.not.relevant.slash.used.in.this.test",
483
ControllerUUID: fakeUUID,
484
CACert: "certificate",
711
486
err := s.ControllerStore.UpdateController(name, controllerDetails)
712
487
c.Assert(err, jc.ErrorIsNil)
488
return controllerDetails
716
491
func (s *CacheAPIEndpointsSuite) assertControllerDetailsUpdated(c *gc.C, name string, check gc.Checker) {
717
492
found, err := s.ControllerStore.ControllerByName(name)
718
493
c.Assert(err, jc.ErrorIsNil)
719
c.Assert(found.Servers, check, 0)
494
c.Assert(found.UnresolvedAPIEndpoints, check, 0)
720
495
c.Assert(found.APIEndpoints, check, 0)
728
503
s.assertControllerDetailsUpdated(c, name, gc.HasLen)
731
func (s *CacheAPIEndpointsSuite) TestPrepareEndpointsForCachingPreferIPv6True(c *gc.C) {
732
s.PatchValue(juju.MaybePreferIPv6, func(_ configstore.EnvironInfo) bool {
736
info := s.assertCreateInfo(c, "controller-name1")
738
c.Assert(err, jc.ErrorIsNil)
739
err = juju.UpdateControllerAddresses(s.ControllerStore, s.store, "controller-name1", s.hostPorts, s.apiHostPort)
740
c.Assert(err, jc.ErrorIsNil)
741
info, err = s.store.ReadInfo("controller-name1")
742
c.Assert(err, jc.ErrorIsNil)
743
s.assertEndpointsPreferIPv6True(c, info)
744
s.assertControllerUpdated(c, "controller-name1")
747
func (s *CacheAPIEndpointsSuite) TestPrepareEndpointsForCachingPreferIPv6False(c *gc.C) {
748
s.PatchValue(juju.MaybePreferIPv6, func(_ configstore.EnvironInfo) bool {
751
info := s.assertCreateInfo(c, "controller-name1")
753
c.Assert(err, jc.ErrorIsNil)
754
err = juju.UpdateControllerAddresses(s.ControllerStore, s.store, "controller-name1", s.hostPorts, s.apiHostPort)
755
c.Assert(err, jc.ErrorIsNil)
756
info, err = s.store.ReadInfo("controller-name1")
757
c.Assert(err, jc.ErrorIsNil)
758
s.assertEndpointsPreferIPv6False(c, info)
506
func (s *CacheAPIEndpointsSuite) TestPrepareEndpointsForCaching(c *gc.C) {
507
s.assertCreateController(c, "controller-name1")
508
err := juju.UpdateControllerAddresses(s.ControllerStore, "controller-name1", s.hostPorts, s.apiHostPort)
509
c.Assert(err, jc.ErrorIsNil)
510
controllerDetails, err := s.ControllerStore.ControllerByName("controller-name1")
511
c.Assert(err, jc.ErrorIsNil)
512
s.assertEndpoints(c, controllerDetails)
759
513
s.assertControllerUpdated(c, "controller-name1")
763
517
// Test that if new endpoints hostnames are the same as the
764
518
// cached, no DNS resolution happens (i.e. we don't resolve on
765
519
// every connection, but as needed).
766
info := s.store.CreateInfo("controller-name")
767
520
hps := network.NewHostPorts(1234,
772
info.SetAPIEndpoint(configstore.APIEndpoint{
773
Hostnames: network.HostPortsToStrings(hps),
525
controllerDetails := jujuclient.ControllerDetails{
526
ControllerUUID: fakeUUID,
527
CACert: "certificate",
528
UnresolvedAPIEndpoints: network.HostPortsToStrings(hps),
530
err := s.ControllerStore.UpdateController("controller-name", controllerDetails)
776
531
c.Assert(err, jc.ErrorIsNil)
778
533
addrs, hosts, changed := juju.PrepareEndpointsForCaching(
779
info, [][]network.HostPort{hps},
534
controllerDetails, [][]network.HostPort{hps},
781
536
c.Assert(addrs, gc.IsNil)
782
537
c.Assert(hosts, gc.IsNil)
956
717
"[fc00::9]:1234", // From ipv6+6.example.com
958
719
// Check Hostnames before resolving
959
c.Check(endpoint.Hostnames, jc.DeepEquals, []string{
960
s.apiHostPort.NetAddr(), // Last endpoint successfully connected to is always on top.
964
"[2001:db8::1]:1234",
965
"[2001:db8::2]:1235",
967
"ipv4+4.example.com:1234",
968
"ipv4+6.example.com:1234",
969
"ipv4.example.com:1234",
970
"ipv6+4.example.com:1235",
971
"ipv6+6.example.com:1234",
972
"ipv6.example.com:1234",
979
func (s *CacheAPIEndpointsSuite) assertEndpointsPreferIPv6True(c *gc.C, info configstore.EnvironInfo) {
980
c.Assert(s.resolveNumCalls, gc.Equals, 1)
981
c.Assert(s.numResolved, gc.Equals, 10)
982
endpoint := info.APIEndpoint()
983
// Check Addresses after resolving.
984
c.Check(endpoint.Addresses, jc.DeepEquals, []string{
985
s.apiHostPort.NetAddr(), // Last endpoint successfully connected to is always on top.
986
"[2001:db8::1]:1234",
987
"[2001:db8::2]:1235",
988
"0.1.2.1:1234", // From ipv4+4.example.com
989
"0.1.2.2:1234", // From ipv4+4.example.com
990
"0.1.2.3:1234", // From ipv4+6.example.com
991
"0.1.2.5:1234", // From ipv4.example.com
992
"0.1.2.6:1234", // From ipv6+4.example.com
996
"localhost:1234", // Left intact on purpose.
997
"localhost:1235", // Left intact on purpose.
998
"[fc00::10]:1234", // From ipv6.example.com
1000
"[fc00::3]:1234", // From ipv4+6.example.com
1001
"[fc00::6]:1234", // From ipv6+4.example.com
1002
"[fc00::8]:1234", // From ipv6+6.example.com
1003
"[fc00::9]:1234", // From ipv6+6.example.com
1005
// Check Hostnames before resolving
1006
c.Check(endpoint.Hostnames, jc.DeepEquals, []string{
1007
s.apiHostPort.NetAddr(), // Last endpoint successfully connected to is always on top.
1008
"[2001:db8::1]:1234",
1009
"[2001:db8::2]:1235",
720
c.Check(controllerDetails.UnresolvedAPIEndpoints, jc.DeepEquals, []string{
721
s.apiHostPort.NetAddr(), // Last endpoint successfully connected to is always on top.
725
"[2001:db8::1]:1234",
726
"[2001:db8::2]:1235",
1013
727
"invalid host:1234",
1014
728
"ipv4+4.example.com:1234",
1015
729
"ipv4+6.example.com:1234",
1085
799
var fakeUUID = "df136476-12e9-11e4-8a70-b2227cce2b54"
1087
var dummyStoreInfo = &environInfo{
1088
creds: configstore.APICredentials{
1089
User: "admin@local",
1090
Password: "hunter2",
1092
endpoint: configstore.APIEndpoint{
1093
Addresses: []string{"foo.invalid"},
1094
CACert: "certificated",
1095
ModelUUID: fakeUUID,
1096
ServerUUID: fakeUUID,
801
func noBootstrapConfig(controllerName string) (*config.Config, error) {
802
return nil, errors.NotFoundf("bootstrap config for controller %s", controllerName)
805
func newAPIConnectionFromNames(
807
controller, account, model string,
808
store jujuclient.ClientStore,
809
apiOpen api.OpenFunc,
810
getBootstrapConfig func(string) (*config.Config, error),
811
) (api.Connection, error) {
812
params := juju.NewAPIConnectionParams{
814
ControllerName: controller,
815
BootstrapConfig: getBootstrapConfig,
816
DialOpts: api.DefaultDialOpts(),
819
accountDetails, err := store.AccountByName(controller, account)
820
c.Assert(err, jc.ErrorIsNil)
821
params.AccountDetails = accountDetails
824
modelDetails, err := store.ModelByName(controller, account, model)
825
c.Assert(err, jc.ErrorIsNil)
826
params.ModelUUID = modelDetails.ModelUUID
828
return juju.NewAPIFromStore(params, apiOpen)