github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/provider/dummy/environs.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // The dummy provider implements an environment provider for testing 5 // purposes, registered with environs under the name "dummy". 6 // 7 // The configuration YAML for the testing environment 8 // must specify a "controller" property with a boolean 9 // value. If this is true, a controller will be started 10 // when the environment is bootstrapped. 11 // 12 // The configuration data also accepts a "broken" property 13 // of type boolean. If this is non-empty, any operation 14 // after the environment has been opened will return 15 // the error "broken environment", and will also log that. 16 // 17 // The DNS name of instances is the same as the Id, 18 // with ".dns" appended. 19 package dummy 20 21 import ( 22 "fmt" 23 "net" 24 "os" 25 "runtime" 26 "strconv" 27 "strings" 28 "sync" 29 "time" 30 31 "github.com/juju/errors" 32 "github.com/juju/loggo" 33 "github.com/juju/retry" 34 "github.com/juju/schema" 35 gitjujutesting "github.com/juju/testing" 36 jc "github.com/juju/testing/checkers" 37 "github.com/juju/utils/arch" 38 "github.com/juju/utils/clock" 39 "github.com/juju/utils/series" 40 gc "gopkg.in/check.v1" 41 "gopkg.in/juju/environschema.v1" 42 "gopkg.in/juju/names.v2" 43 44 "github.com/juju/juju/agent" 45 "github.com/juju/juju/api" 46 "github.com/juju/juju/apiserver" 47 "github.com/juju/juju/apiserver/observer" 48 "github.com/juju/juju/apiserver/observer/fakeobserver" 49 "github.com/juju/juju/cloud" 50 "github.com/juju/juju/cloudconfig/instancecfg" 51 "github.com/juju/juju/constraints" 52 "github.com/juju/juju/environs" 53 "github.com/juju/juju/environs/config" 54 "github.com/juju/juju/instance" 55 "github.com/juju/juju/mongo" 56 "github.com/juju/juju/mongo/mongotest" 57 "github.com/juju/juju/network" 58 "github.com/juju/juju/provider/common" 59 "github.com/juju/juju/state" 60 "github.com/juju/juju/state/multiwatcher" 61 "github.com/juju/juju/state/stateenvirons" 62 "github.com/juju/juju/status" 63 "github.com/juju/juju/storage" 64 "github.com/juju/juju/testing" 65 coretools "github.com/juju/juju/tools" 66 ) 67 68 var logger = loggo.GetLogger("juju.provider.dummy") 69 70 var transientErrorInjection chan error 71 72 const BootstrapInstanceId = "localhost" 73 74 var errNotPrepared = errors.New("model is not prepared") 75 76 // SampleCloudSpec returns an environs.CloudSpec that can be used to 77 // open a dummy Environ. 78 func SampleCloudSpec() environs.CloudSpec { 79 cred := cloud.NewCredential(cloud.UserPassAuthType, map[string]string{"username": "dummy", "passeord": "secret"}) 80 return environs.CloudSpec{ 81 Type: "dummy", 82 Name: "dummy", 83 Endpoint: "dummy-endpoint", 84 IdentityEndpoint: "dummy-identity-endpoint", 85 Region: "dummy-region", 86 StorageEndpoint: "dummy-storage-endpoint", 87 Credential: &cred, 88 } 89 } 90 91 // SampleConfig() returns an environment configuration with all required 92 // attributes set. 93 func SampleConfig() testing.Attrs { 94 return testing.Attrs{ 95 "type": "dummy", 96 "name": "only", 97 "uuid": testing.ModelTag.Id(), 98 "authorized-keys": testing.FakeAuthKeys, 99 "firewall-mode": config.FwInstance, 100 "ssl-hostname-verification": true, 101 "development": false, 102 "default-series": series.LatestLts(), 103 104 "secret": "pork", 105 "controller": true, 106 } 107 } 108 109 // PatchTransientErrorInjectionChannel sets the transientInjectionError 110 // channel which can be used to inject errors into StartInstance for 111 // testing purposes 112 // The injected errors will use the string received on the channel 113 // and the instance's state will eventually go to error, while the 114 // received string will appear in the info field of the machine's status 115 func PatchTransientErrorInjectionChannel(c chan error) func() { 116 return gitjujutesting.PatchValue(&transientErrorInjection, c) 117 } 118 119 // stateInfo returns a *state.Info which allows clients to connect to the 120 // shared dummy state, if it exists. 121 func stateInfo() *mongo.MongoInfo { 122 if gitjujutesting.MgoServer.Addr() == "" { 123 panic("dummy environ state tests must be run with MgoTestPackage") 124 } 125 mongoPort := strconv.Itoa(gitjujutesting.MgoServer.Port()) 126 addrs := []string{net.JoinHostPort("localhost", mongoPort)} 127 return &mongo.MongoInfo{ 128 Info: mongo.Info{ 129 Addrs: addrs, 130 CACert: testing.CACert, 131 }, 132 } 133 } 134 135 // Operation represents an action on the dummy provider. 136 type Operation interface{} 137 138 type OpBootstrap struct { 139 Context environs.BootstrapContext 140 Env string 141 Args environs.BootstrapParams 142 } 143 144 type OpFinalizeBootstrap struct { 145 Context environs.BootstrapContext 146 Env string 147 InstanceConfig *instancecfg.InstanceConfig 148 } 149 150 type OpDestroy struct { 151 Env string 152 Cloud string 153 CloudRegion string 154 Error error 155 } 156 157 type OpNetworkInterfaces struct { 158 Env string 159 InstanceId instance.Id 160 Info []network.InterfaceInfo 161 } 162 163 type OpSubnets struct { 164 Env string 165 InstanceId instance.Id 166 SubnetIds []network.Id 167 Info []network.SubnetInfo 168 } 169 170 type OpStartInstance struct { 171 Env string 172 MachineId string 173 MachineNonce string 174 PossibleTools coretools.List 175 Instance instance.Instance 176 Constraints constraints.Value 177 SubnetsToZones map[network.Id][]string 178 NetworkInfo []network.InterfaceInfo 179 Volumes []storage.Volume 180 Info *mongo.MongoInfo 181 Jobs []multiwatcher.MachineJob 182 APIInfo *api.Info 183 Secret string 184 AgentEnvironment map[string]string 185 } 186 187 type OpStopInstances struct { 188 Env string 189 Ids []instance.Id 190 } 191 192 type OpOpenPorts struct { 193 Env string 194 MachineId string 195 InstanceId instance.Id 196 Ports []network.PortRange 197 } 198 199 type OpClosePorts struct { 200 Env string 201 MachineId string 202 InstanceId instance.Id 203 Ports []network.PortRange 204 } 205 206 type OpPutFile struct { 207 Env string 208 FileName string 209 } 210 211 // environProvider represents the dummy provider. There is only ever one 212 // instance of this type (dummy) 213 type environProvider struct { 214 mu sync.Mutex 215 ops chan<- Operation 216 newStatePolicy state.NewPolicyFunc 217 supportsSpaces bool 218 supportsSpaceDiscovery bool 219 apiPort int 220 controllerState *environState 221 state map[string]*environState 222 } 223 224 // APIPort returns the randon api port used by the given provider instance. 225 func APIPort(p environs.EnvironProvider) int { 226 return p.(*environProvider).apiPort 227 } 228 229 // environState represents the state of an environment. 230 // It can be shared between several environ values, 231 // so that a given environment can be opened several times. 232 type environState struct { 233 name string 234 ops chan<- Operation 235 newStatePolicy state.NewPolicyFunc 236 mu sync.Mutex 237 maxId int // maximum instance id allocated so far. 238 maxAddr int // maximum allocated address last byte 239 insts map[instance.Id]*dummyInstance 240 globalPorts map[network.PortRange]bool 241 bootstrapped bool 242 apiListener net.Listener 243 apiServer *apiserver.Server 244 apiState *state.State 245 apiStatePool *state.StatePool 246 creator string 247 } 248 249 // environ represents a client's connection to a given environment's 250 // state. 251 type environ struct { 252 storage.ProviderRegistry 253 name string 254 modelUUID string 255 cloud environs.CloudSpec 256 ecfgMutex sync.Mutex 257 ecfgUnlocked *environConfig 258 spacesMutex sync.RWMutex 259 } 260 261 // discardOperations discards all Operations written to it. 262 var discardOperations = make(chan Operation) 263 264 func init() { 265 environs.RegisterProvider("dummy", &dummy) 266 267 // Prime the first ops channel, so that naive clients can use 268 // the testing environment by simply importing it. 269 go func() { 270 for _ = range discardOperations { 271 } 272 }() 273 } 274 275 // dummy is the dummy environmentProvider singleton. 276 var dummy = environProvider{ 277 ops: discardOperations, 278 state: make(map[string]*environState), 279 newStatePolicy: stateenvirons.GetNewPolicyFunc( 280 stateenvirons.GetNewEnvironFunc(environs.New), 281 ), 282 supportsSpaces: true, 283 supportsSpaceDiscovery: false, 284 } 285 286 // Reset resets the entire dummy environment and forgets any registered 287 // operation listener. All opened environments after Reset will share 288 // the same underlying state. 289 func Reset(c *gc.C) { 290 logger.Infof("reset model") 291 dummy.mu.Lock() 292 dummy.ops = discardOperations 293 oldState := dummy.state 294 dummy.controllerState = nil 295 dummy.state = make(map[string]*environState) 296 dummy.newStatePolicy = stateenvirons.GetNewPolicyFunc( 297 stateenvirons.GetNewEnvironFunc(environs.New), 298 ) 299 dummy.supportsSpaces = true 300 dummy.supportsSpaceDiscovery = false 301 dummy.mu.Unlock() 302 303 // NOTE(axw) we must destroy the old states without holding 304 // the provider lock, or we risk deadlocking. Destroying 305 // state involves closing the embedded API server, which 306 // may require waiting on RPC calls that interact with the 307 // EnvironProvider (e.g. EnvironProvider.Open). 308 for _, s := range oldState { 309 if s.apiListener != nil { 310 s.apiListener.Close() 311 } 312 s.destroy() 313 } 314 if mongoAlive() { 315 err := retry.Call(retry.CallArgs{ 316 Func: gitjujutesting.MgoServer.Reset, 317 // Only interested in retrying the intermittent 318 // 'unexpected message'. 319 IsFatalError: func(err error) bool { 320 return !strings.HasSuffix(err.Error(), "unexpected message") 321 }, 322 Delay: time.Millisecond, 323 Clock: clock.WallClock, 324 Attempts: 5, 325 }) 326 c.Assert(err, jc.ErrorIsNil) 327 } 328 } 329 330 func (state *environState) destroy() { 331 state.mu.Lock() 332 defer state.mu.Unlock() 333 state.destroyLocked() 334 } 335 336 func (state *environState) destroyLocked() { 337 if !state.bootstrapped { 338 return 339 } 340 apiServer := state.apiServer 341 apiStatePool := state.apiStatePool 342 apiState := state.apiState 343 state.apiServer = nil 344 state.apiStatePool = nil 345 state.apiState = nil 346 state.bootstrapped = false 347 348 // Release the lock while we close resources. In particular, 349 // we must not hold the lock while the API server is being 350 // closed, as it may need to interact with the Environ while 351 // shutting down. 352 state.mu.Unlock() 353 defer state.mu.Lock() 354 355 if apiServer != nil { 356 if err := apiServer.Stop(); err != nil && mongoAlive() { 357 panic(err) 358 } 359 } 360 361 if apiStatePool != nil { 362 if err := apiStatePool.Close(); err != nil && mongoAlive() { 363 panic(err) 364 } 365 } 366 367 if apiState != nil { 368 if err := apiState.Close(); err != nil && mongoAlive() { 369 panic(err) 370 } 371 } 372 373 if mongoAlive() { 374 gitjujutesting.MgoServer.Reset() 375 } 376 } 377 378 // mongoAlive reports whether the mongo server is 379 // still alive (i.e. has not been deliberately destroyed). 380 // If it has been deliberately destroyed, we will 381 // expect some errors when closing things down. 382 func mongoAlive() bool { 383 return gitjujutesting.MgoServer.Addr() != "" 384 } 385 386 // GetStateInAPIServer returns the state connection used by the API server 387 // This is so code in the test suite can trigger Syncs, etc that the API server 388 // will see, which will then trigger API watchers, etc. 389 func (e *environ) GetStateInAPIServer() *state.State { 390 st, err := e.state() 391 if err != nil { 392 panic(err) 393 } 394 return st.apiState 395 } 396 397 // GetStatePoolInAPIServer returns the StatePool used by the API 398 // server. As for GetStatePoolInAPIServer, this is so code in the 399 // test suite can trigger Syncs etc. 400 func (e *environ) GetStatePoolInAPIServer() *state.StatePool { 401 st, err := e.state() 402 if err != nil { 403 panic(err) 404 } 405 return st.apiStatePool 406 } 407 408 // newState creates the state for a new environment with the given name. 409 func newState(name string, ops chan<- Operation, newStatePolicy state.NewPolicyFunc) *environState { 410 buf := make([]byte, 8192) 411 buf = buf[:runtime.Stack(buf, false)] 412 s := &environState{ 413 name: name, 414 ops: ops, 415 newStatePolicy: newStatePolicy, 416 insts: make(map[instance.Id]*dummyInstance), 417 globalPorts: make(map[network.PortRange]bool), 418 creator: string(buf), 419 } 420 return s 421 } 422 423 // listenAPI starts a network listener listening for API 424 // connections and proxies them to the API server port. 425 func (s *environState) listenAPI() int { 426 l, err := net.Listen("tcp", ":0") 427 if err != nil { 428 panic(fmt.Errorf("cannot start listener: %v", err)) 429 } 430 s.apiListener = l 431 return l.Addr().(*net.TCPAddr).Port 432 } 433 434 // SetSupportsSpaces allows to enable and disable SupportsSpaces for tests. 435 func SetSupportsSpaces(supports bool) bool { 436 dummy.mu.Lock() 437 defer dummy.mu.Unlock() 438 current := dummy.supportsSpaces 439 dummy.supportsSpaces = supports 440 return current 441 } 442 443 // SetSupportsSpaceDiscovery allows to enable and disable 444 // SupportsSpaceDiscovery for tests. 445 func SetSupportsSpaceDiscovery(supports bool) bool { 446 dummy.mu.Lock() 447 defer dummy.mu.Unlock() 448 current := dummy.supportsSpaceDiscovery 449 dummy.supportsSpaceDiscovery = supports 450 return current 451 } 452 453 // Listen directs subsequent operations on any dummy environment 454 // to channel c (if not nil). 455 func Listen(c chan<- Operation) { 456 dummy.mu.Lock() 457 defer dummy.mu.Unlock() 458 if c == nil { 459 c = discardOperations 460 } 461 dummy.ops = c 462 for _, st := range dummy.state { 463 st.mu.Lock() 464 st.ops = c 465 st.mu.Unlock() 466 } 467 } 468 469 var configSchema = environschema.Fields{ 470 "controller": { 471 Description: "Whether the model should start a controller", 472 Type: environschema.Tbool, 473 }, 474 "broken": { 475 Description: "Whitespace-separated Environ methods that should return an error when called", 476 Type: environschema.Tstring, 477 }, 478 "secret": { 479 Description: "A secret", 480 Type: environschema.Tstring, 481 }, 482 } 483 484 var configFields = func() schema.Fields { 485 fs, _, err := configSchema.ValidationSchema() 486 if err != nil { 487 panic(err) 488 } 489 return fs 490 }() 491 492 var configDefaults = schema.Defaults{ 493 "broken": "", 494 "secret": "pork", 495 "controller": false, 496 } 497 498 type environConfig struct { 499 *config.Config 500 attrs map[string]interface{} 501 } 502 503 func (c *environConfig) controller() bool { 504 return c.attrs["controller"].(bool) 505 } 506 507 func (c *environConfig) broken() string { 508 return c.attrs["broken"].(string) 509 } 510 511 func (c *environConfig) secret() string { 512 return c.attrs["secret"].(string) 513 } 514 515 func (p *environProvider) newConfig(cfg *config.Config) (*environConfig, error) { 516 valid, err := p.Validate(cfg, nil) 517 if err != nil { 518 return nil, err 519 } 520 return &environConfig{valid, valid.UnknownAttrs()}, nil 521 } 522 523 func (p *environProvider) Schema() environschema.Fields { 524 fields, err := config.Schema(configSchema) 525 if err != nil { 526 panic(err) 527 } 528 return fields 529 } 530 531 var _ config.ConfigSchemaSource = (*environProvider)(nil) 532 533 // ConfigSchema returns extra config attributes specific 534 // to this provider only. 535 func (p environProvider) ConfigSchema() schema.Fields { 536 return configFields 537 } 538 539 // ConfigDefaults returns the default values for the 540 // provider specific config attributes. 541 func (p environProvider) ConfigDefaults() schema.Defaults { 542 return configDefaults 543 } 544 545 func (environProvider) CredentialSchemas() map[cloud.AuthType]cloud.CredentialSchema { 546 return map[cloud.AuthType]cloud.CredentialSchema{ 547 cloud.EmptyAuthType: {}, 548 cloud.UserPassAuthType: { 549 { 550 "username", cloud.CredentialAttr{Description: "The username to authenticate with."}, 551 }, { 552 "password", cloud.CredentialAttr{ 553 Description: "The password for the specified username.", 554 Hidden: true, 555 }, 556 }, 557 }, 558 } 559 } 560 561 func (*environProvider) DetectCredentials() (*cloud.CloudCredential, error) { 562 return cloud.NewEmptyCloudCredential(), nil 563 } 564 565 func (*environProvider) FinalizeCredential(ctx environs.FinalizeCredentialContext, args environs.FinalizeCredentialParams) (*cloud.Credential, error) { 566 return &args.Credential, nil 567 } 568 569 func (*environProvider) DetectRegions() ([]cloud.Region, error) { 570 return []cloud.Region{{Name: "dummy"}}, nil 571 } 572 573 func (p *environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) { 574 // Check for valid changes for the base config values. 575 if err := config.Validate(cfg, old); err != nil { 576 return nil, err 577 } 578 validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) 579 if err != nil { 580 return nil, err 581 } 582 // Apply the coerced unknown values back into the config. 583 return cfg.Apply(validated) 584 } 585 586 func (e *environ) state() (*environState, error) { 587 dummy.mu.Lock() 588 defer dummy.mu.Unlock() 589 state, ok := dummy.state[e.modelUUID] 590 if !ok { 591 return nil, errNotPrepared 592 } 593 return state, nil 594 } 595 596 func (p *environProvider) Open(args environs.OpenParams) (environs.Environ, error) { 597 p.mu.Lock() 598 defer p.mu.Unlock() 599 ecfg, err := p.newConfig(args.Config) 600 if err != nil { 601 return nil, err 602 } 603 env := &environ{ 604 ProviderRegistry: StorageProviders(), 605 name: ecfg.Name(), 606 modelUUID: args.Config.UUID(), 607 cloud: args.Cloud, 608 ecfgUnlocked: ecfg, 609 } 610 if err := env.checkBroken("Open"); err != nil { 611 return nil, err 612 } 613 return env, nil 614 } 615 616 // PrepareConfig is specified in the EnvironProvider interface. 617 func (p *environProvider) PrepareConfig(args environs.PrepareConfigParams) (*config.Config, error) { 618 if _, err := dummy.newConfig(args.Config); err != nil { 619 return nil, err 620 } 621 return args.Config, nil 622 } 623 624 // Override for testing - the data directory with which the state api server is initialised. 625 var DataDir = "" 626 var LogDir = "" 627 628 func (e *environ) ecfg() *environConfig { 629 e.ecfgMutex.Lock() 630 ecfg := e.ecfgUnlocked 631 e.ecfgMutex.Unlock() 632 return ecfg 633 } 634 635 func (e *environ) checkBroken(method string) error { 636 for _, m := range strings.Fields(e.ecfg().broken()) { 637 if m == method { 638 return fmt.Errorf("dummy.%s is broken", method) 639 } 640 } 641 return nil 642 } 643 644 // PrecheckInstance is specified in the state.Prechecker interface. 645 func (*environ) PrecheckInstance(series string, cons constraints.Value, placement string) error { 646 if placement != "" && placement != "valid" { 647 return fmt.Errorf("%s placement is invalid", placement) 648 } 649 return nil 650 } 651 652 // Create is part of the Environ interface. 653 func (e *environ) Create(args environs.CreateParams) error { 654 dummy.mu.Lock() 655 defer dummy.mu.Unlock() 656 dummy.state[e.modelUUID] = newState(e.name, dummy.ops, dummy.newStatePolicy) 657 return nil 658 } 659 660 // PrepareForBootstrap is part of the Environ interface. 661 func (e *environ) PrepareForBootstrap(ctx environs.BootstrapContext) error { 662 dummy.mu.Lock() 663 defer dummy.mu.Unlock() 664 ecfg := e.ecfgUnlocked 665 666 if ecfg.controller() && dummy.controllerState != nil { 667 // Because of global variables, we can only have one dummy 668 // controller per process. Panic if there is an attempt to 669 // bootstrap while there is another active controller. 670 old := dummy.controllerState 671 panic(fmt.Errorf("cannot share a state between two dummy environs; old %q; new %q: %s", old.name, e.name, old.creator)) 672 } 673 674 // The environment has not been prepared, so create it and record it. 675 // We don't start listening for State or API connections until 676 // Bootstrap has been called. 677 envState := newState(e.name, dummy.ops, dummy.newStatePolicy) 678 if ecfg.controller() { 679 dummy.apiPort = envState.listenAPI() 680 dummy.controllerState = envState 681 } 682 dummy.state[e.modelUUID] = envState 683 return nil 684 } 685 686 func (e *environ) Bootstrap(ctx environs.BootstrapContext, args environs.BootstrapParams) (*environs.BootstrapResult, error) { 687 series := config.PreferredSeries(e.Config()) 688 availableTools, err := args.AvailableTools.Match(coretools.Filter{Series: series}) 689 if err != nil { 690 return nil, err 691 } 692 arch := availableTools.Arches()[0] 693 694 defer delay() 695 if err := e.checkBroken("Bootstrap"); err != nil { 696 return nil, err 697 } 698 if _, ok := args.ControllerConfig.CACert(); !ok { 699 return nil, errors.New("no CA certificate in controller configuration") 700 } 701 702 logger.Infof("would pick tools from %s", availableTools) 703 704 estate, err := e.state() 705 if err != nil { 706 return nil, err 707 } 708 estate.mu.Lock() 709 defer estate.mu.Unlock() 710 if estate.bootstrapped { 711 return nil, errors.New("model is already bootstrapped") 712 } 713 714 // Create an instance for the bootstrap node. 715 logger.Infof("creating bootstrap instance") 716 i := &dummyInstance{ 717 id: BootstrapInstanceId, 718 addresses: network.NewAddresses("localhost"), 719 ports: make(map[network.PortRange]bool), 720 machineId: agent.BootstrapMachineId, 721 series: series, 722 firewallMode: e.Config().FirewallMode(), 723 state: estate, 724 controller: true, 725 } 726 estate.insts[i.id] = i 727 estate.bootstrapped = true 728 estate.ops <- OpBootstrap{Context: ctx, Env: e.name, Args: args} 729 730 finalize := func(ctx environs.BootstrapContext, icfg *instancecfg.InstanceConfig, _ environs.BootstrapDialOpts) error { 731 if e.ecfg().controller() { 732 icfg.Bootstrap.BootstrapMachineInstanceId = BootstrapInstanceId 733 if err := instancecfg.FinishInstanceConfig(icfg, e.Config()); err != nil { 734 return err 735 } 736 737 adminUser := names.NewUserTag("admin@local") 738 var cloudCredentialTag names.CloudCredentialTag 739 if icfg.Bootstrap.ControllerCloudCredentialName != "" { 740 cloudCredentialTag = names.NewCloudCredentialTag(fmt.Sprintf( 741 "%s/%s/%s", 742 icfg.Bootstrap.ControllerCloudName, 743 adminUser.Canonical(), 744 icfg.Bootstrap.ControllerCloudCredentialName, 745 )) 746 } 747 748 cloudCredentials := make(map[names.CloudCredentialTag]cloud.Credential) 749 if icfg.Bootstrap.ControllerCloudCredential != nil && icfg.Bootstrap.ControllerCloudCredentialName != "" { 750 cloudCredentials[cloudCredentialTag] = *icfg.Bootstrap.ControllerCloudCredential 751 } 752 753 info := stateInfo() 754 // Since the admin user isn't setup until after here, 755 // the password in the info structure is empty, so the admin 756 // user is constructed with an empty password here. 757 // It is set just below. 758 st, err := state.Initialize(state.InitializeParams{ 759 Clock: clock.WallClock, 760 ControllerConfig: icfg.Controller.Config, 761 ControllerModelArgs: state.ModelArgs{ 762 Owner: adminUser, 763 Config: icfg.Bootstrap.ControllerModelConfig, 764 Constraints: icfg.Bootstrap.BootstrapMachineConstraints, 765 CloudName: icfg.Bootstrap.ControllerCloudName, 766 CloudRegion: icfg.Bootstrap.ControllerCloudRegion, 767 CloudCredential: cloudCredentialTag, 768 StorageProviderRegistry: e, 769 }, 770 Cloud: icfg.Bootstrap.ControllerCloud, 771 CloudName: icfg.Bootstrap.ControllerCloudName, 772 CloudCredentials: cloudCredentials, 773 MongoInfo: info, 774 MongoDialOpts: mongotest.DialOpts(), 775 NewPolicy: estate.newStatePolicy, 776 }) 777 if err != nil { 778 return err 779 } 780 if err := st.SetModelConstraints(args.ModelConstraints); err != nil { 781 return err 782 } 783 if err := st.SetAdminMongoPassword(icfg.Controller.MongoInfo.Password); err != nil { 784 return err 785 } 786 if err := st.MongoSession().DB("admin").Login("admin", icfg.Controller.MongoInfo.Password); err != nil { 787 return err 788 } 789 env, err := st.Model() 790 if err != nil { 791 return err 792 } 793 owner, err := st.User(env.Owner()) 794 if err != nil { 795 return err 796 } 797 // We log this out for test purposes only. No one in real life can use 798 // a dummy provider for anything other than testing, so logging the password 799 // here is fine. 800 logger.Debugf("setting password for %q to %q", owner.Name(), icfg.Controller.MongoInfo.Password) 801 owner.SetPassword(icfg.Controller.MongoInfo.Password) 802 803 estate.apiStatePool = state.NewStatePool(st) 804 805 estate.apiServer, err = apiserver.NewServer(st, estate.apiListener, apiserver.ServerConfig{ 806 Clock: clock.WallClock, 807 Cert: testing.ServerCert, 808 Key: testing.ServerKey, 809 Tag: names.NewMachineTag("0"), 810 DataDir: DataDir, 811 LogDir: LogDir, 812 StatePool: estate.apiStatePool, 813 NewObserver: func() observer.Observer { return &fakeobserver.Instance{} }, 814 // Should never be used but prevent external access just in case. 815 AutocertURL: "https://0.1.2.3/no-autocert-here", 816 }) 817 if err != nil { 818 panic(err) 819 } 820 estate.apiState = st 821 } 822 estate.ops <- OpFinalizeBootstrap{Context: ctx, Env: e.name, InstanceConfig: icfg} 823 return nil 824 } 825 826 bsResult := &environs.BootstrapResult{ 827 Arch: arch, 828 Series: series, 829 Finalize: finalize, 830 } 831 return bsResult, nil 832 } 833 834 func (e *environ) ControllerInstances(controllerUUID string) ([]instance.Id, error) { 835 estate, err := e.state() 836 if err != nil { 837 return nil, err 838 } 839 estate.mu.Lock() 840 defer estate.mu.Unlock() 841 if err := e.checkBroken("ControllerInstances"); err != nil { 842 return nil, err 843 } 844 if !estate.bootstrapped { 845 return nil, environs.ErrNotBootstrapped 846 } 847 var controllerInstances []instance.Id 848 for _, v := range estate.insts { 849 if v.controller { 850 controllerInstances = append(controllerInstances, v.Id()) 851 } 852 } 853 return controllerInstances, nil 854 } 855 856 func (e *environ) Config() *config.Config { 857 return e.ecfg().Config 858 } 859 860 func (e *environ) SetConfig(cfg *config.Config) error { 861 if err := e.checkBroken("SetConfig"); err != nil { 862 return err 863 } 864 ecfg, err := dummy.newConfig(cfg) 865 if err != nil { 866 return err 867 } 868 e.ecfgMutex.Lock() 869 e.ecfgUnlocked = ecfg 870 e.ecfgMutex.Unlock() 871 return nil 872 } 873 874 func (e *environ) Destroy() (res error) { 875 defer delay() 876 estate, err := e.state() 877 if err != nil { 878 if err == errNotPrepared { 879 return nil 880 } 881 return err 882 } 883 defer func() { 884 // The estate is a pointer to a structure that is stored in the dummy global. 885 // The Listen method can change the ops channel of any state, and will do so 886 // under the covers. What we need to do is use the state mutex to add a memory 887 // barrier such that the ops channel we see here is the latest. 888 estate.mu.Lock() 889 ops := estate.ops 890 name := estate.name 891 delete(dummy.state, e.modelUUID) 892 estate.mu.Unlock() 893 ops <- OpDestroy{ 894 Env: name, 895 Cloud: e.cloud.Name, 896 CloudRegion: e.cloud.Region, 897 Error: res, 898 } 899 }() 900 if err := e.checkBroken("Destroy"); err != nil { 901 return err 902 } 903 if !e.ecfg().controller() { 904 return nil 905 } 906 estate.destroy() 907 return nil 908 } 909 910 func (e *environ) DestroyController(controllerUUID string) error { 911 if err := e.Destroy(); err != nil { 912 return err 913 } 914 dummy.mu.Lock() 915 dummy.controllerState = nil 916 dummy.mu.Unlock() 917 return nil 918 } 919 920 // ConstraintsValidator is defined on the Environs interface. 921 func (e *environ) ConstraintsValidator() (constraints.Validator, error) { 922 validator := constraints.NewValidator() 923 validator.RegisterUnsupported([]string{constraints.CpuPower, constraints.VirtType}) 924 validator.RegisterConflicts([]string{constraints.InstanceType}, []string{constraints.Mem}) 925 validator.RegisterVocabulary(constraints.Arch, []string{arch.AMD64, arch.ARM64, arch.I386, arch.PPC64EL}) 926 return validator, nil 927 } 928 929 // MaintainInstance is specified in the InstanceBroker interface. 930 func (*environ) MaintainInstance(args environs.StartInstanceParams) error { 931 return nil 932 } 933 934 // StartInstance is specified in the InstanceBroker interface. 935 func (e *environ) StartInstance(args environs.StartInstanceParams) (*environs.StartInstanceResult, error) { 936 937 defer delay() 938 machineId := args.InstanceConfig.MachineId 939 logger.Infof("dummy startinstance, machine %s", machineId) 940 if err := e.checkBroken("StartInstance"); err != nil { 941 return nil, err 942 } 943 estate, err := e.state() 944 if err != nil { 945 return nil, err 946 } 947 estate.mu.Lock() 948 defer estate.mu.Unlock() 949 950 // check if an error has been injected on the transientErrorInjection channel (testing purposes) 951 select { 952 case injectedError := <-transientErrorInjection: 953 return nil, injectedError 954 default: 955 } 956 957 if args.InstanceConfig.MachineNonce == "" { 958 return nil, errors.New("cannot start instance: missing machine nonce") 959 } 960 if args.InstanceConfig.Controller != nil { 961 if args.InstanceConfig.Controller.MongoInfo.Tag != names.NewMachineTag(machineId) { 962 return nil, errors.New("entity tag must match started machine") 963 } 964 } 965 if args.InstanceConfig.APIInfo.Tag != names.NewMachineTag(machineId) { 966 return nil, errors.New("entity tag must match started machine") 967 } 968 logger.Infof("would pick tools from %s", args.Tools) 969 series := args.Tools.OneSeries() 970 971 idString := fmt.Sprintf("%s-%d", e.name, estate.maxId) 972 addrs := network.NewAddresses(idString+".dns", "127.0.0.1") 973 logger.Debugf("StartInstance addresses: %v", addrs) 974 i := &dummyInstance{ 975 id: instance.Id(idString), 976 addresses: addrs, 977 ports: make(map[network.PortRange]bool), 978 machineId: machineId, 979 series: series, 980 firewallMode: e.Config().FirewallMode(), 981 state: estate, 982 } 983 984 var hc *instance.HardwareCharacteristics 985 // To match current system capability, only provide hardware characteristics for 986 // environ machines, not containers. 987 if state.ParentId(machineId) == "" { 988 // We will just assume the instance hardware characteristics exactly matches 989 // the supplied constraints (if specified). 990 hc = &instance.HardwareCharacteristics{ 991 Arch: args.Constraints.Arch, 992 Mem: args.Constraints.Mem, 993 RootDisk: args.Constraints.RootDisk, 994 CpuCores: args.Constraints.CpuCores, 995 CpuPower: args.Constraints.CpuPower, 996 Tags: args.Constraints.Tags, 997 } 998 // Fill in some expected instance hardware characteristics if constraints not specified. 999 if hc.Arch == nil { 1000 arch := "amd64" 1001 hc.Arch = &arch 1002 } 1003 if hc.Mem == nil { 1004 mem := uint64(1024) 1005 hc.Mem = &mem 1006 } 1007 if hc.RootDisk == nil { 1008 disk := uint64(8192) 1009 hc.RootDisk = &disk 1010 } 1011 if hc.CpuCores == nil { 1012 cores := uint64(1) 1013 hc.CpuCores = &cores 1014 } 1015 } 1016 // Simulate subnetsToZones gets populated when spaces given in constraints. 1017 spaces := args.Constraints.IncludeSpaces() 1018 var subnetsToZones map[network.Id][]string 1019 for isp := range spaces { 1020 // Simulate 2 subnets per space. 1021 if subnetsToZones == nil { 1022 subnetsToZones = make(map[network.Id][]string) 1023 } 1024 for isn := 0; isn < 2; isn++ { 1025 providerId := fmt.Sprintf("subnet-%d", isp+isn) 1026 zone := fmt.Sprintf("zone%d", isp+isn) 1027 subnetsToZones[network.Id(providerId)] = []string{zone} 1028 } 1029 } 1030 // Simulate creating volumes when requested. 1031 volumes := make([]storage.Volume, len(args.Volumes)) 1032 for iv, v := range args.Volumes { 1033 persistent, _ := v.Attributes["persistent"].(bool) 1034 volumes[iv] = storage.Volume{ 1035 Tag: names.NewVolumeTag(strconv.Itoa(iv + 1)), 1036 VolumeInfo: storage.VolumeInfo{ 1037 Size: v.Size, 1038 Persistent: persistent, 1039 }, 1040 } 1041 } 1042 var mongoInfo *mongo.MongoInfo 1043 if args.InstanceConfig.Controller != nil { 1044 mongoInfo = args.InstanceConfig.Controller.MongoInfo 1045 } 1046 estate.insts[i.id] = i 1047 estate.maxId++ 1048 estate.ops <- OpStartInstance{ 1049 Env: e.name, 1050 MachineId: machineId, 1051 MachineNonce: args.InstanceConfig.MachineNonce, 1052 PossibleTools: args.Tools, 1053 Constraints: args.Constraints, 1054 SubnetsToZones: subnetsToZones, 1055 Volumes: volumes, 1056 Instance: i, 1057 Jobs: args.InstanceConfig.Jobs, 1058 Info: mongoInfo, 1059 APIInfo: args.InstanceConfig.APIInfo, 1060 AgentEnvironment: args.InstanceConfig.AgentEnvironment, 1061 Secret: e.ecfg().secret(), 1062 } 1063 return &environs.StartInstanceResult{ 1064 Instance: i, 1065 Hardware: hc, 1066 }, nil 1067 } 1068 1069 func (e *environ) StopInstances(ids ...instance.Id) error { 1070 defer delay() 1071 if err := e.checkBroken("StopInstance"); err != nil { 1072 return err 1073 } 1074 estate, err := e.state() 1075 if err != nil { 1076 return err 1077 } 1078 estate.mu.Lock() 1079 defer estate.mu.Unlock() 1080 for _, id := range ids { 1081 delete(estate.insts, id) 1082 } 1083 estate.ops <- OpStopInstances{ 1084 Env: e.name, 1085 Ids: ids, 1086 } 1087 return nil 1088 } 1089 1090 func (e *environ) Instances(ids []instance.Id) (insts []instance.Instance, err error) { 1091 defer delay() 1092 if err := e.checkBroken("Instances"); err != nil { 1093 return nil, err 1094 } 1095 if len(ids) == 0 { 1096 return nil, nil 1097 } 1098 estate, err := e.state() 1099 if err != nil { 1100 return nil, err 1101 } 1102 estate.mu.Lock() 1103 defer estate.mu.Unlock() 1104 notFound := 0 1105 for _, id := range ids { 1106 inst := estate.insts[id] 1107 if inst == nil { 1108 err = environs.ErrPartialInstances 1109 notFound++ 1110 insts = append(insts, nil) 1111 } else { 1112 insts = append(insts, inst) 1113 } 1114 } 1115 if notFound == len(ids) { 1116 return nil, environs.ErrNoInstances 1117 } 1118 return 1119 } 1120 1121 // SupportsSpaces is specified on environs.Networking. 1122 func (env *environ) SupportsSpaces() (bool, error) { 1123 dummy.mu.Lock() 1124 defer dummy.mu.Unlock() 1125 if !dummy.supportsSpaces { 1126 return false, errors.NotSupportedf("spaces") 1127 } 1128 return true, nil 1129 } 1130 1131 // SupportsSpaceDiscovery is specified on environs.Networking. 1132 func (env *environ) SupportsSpaceDiscovery() (bool, error) { 1133 if err := env.checkBroken("SupportsSpaceDiscovery"); err != nil { 1134 return false, err 1135 } 1136 dummy.mu.Lock() 1137 defer dummy.mu.Unlock() 1138 if !dummy.supportsSpaceDiscovery { 1139 return false, nil 1140 } 1141 return true, nil 1142 } 1143 1144 // Spaces is specified on environs.Networking. 1145 func (env *environ) Spaces() ([]network.SpaceInfo, error) { 1146 if err := env.checkBroken("Spaces"); err != nil { 1147 return []network.SpaceInfo{}, err 1148 } 1149 return []network.SpaceInfo{{ 1150 Name: "foo", 1151 ProviderId: network.Id("0"), 1152 Subnets: []network.SubnetInfo{{ 1153 ProviderId: network.Id("1"), 1154 AvailabilityZones: []string{"zone1"}, 1155 }, { 1156 ProviderId: network.Id("2"), 1157 AvailabilityZones: []string{"zone1"}, 1158 }}}, { 1159 Name: "Another Foo 99!", 1160 ProviderId: "1", 1161 Subnets: []network.SubnetInfo{{ 1162 ProviderId: network.Id("3"), 1163 AvailabilityZones: []string{"zone1"}, 1164 }}}, { 1165 Name: "foo-", 1166 ProviderId: "2", 1167 Subnets: []network.SubnetInfo{{ 1168 ProviderId: network.Id("4"), 1169 AvailabilityZones: []string{"zone1"}, 1170 }}}, { 1171 Name: "---", 1172 ProviderId: "3", 1173 Subnets: []network.SubnetInfo{{ 1174 ProviderId: network.Id("5"), 1175 AvailabilityZones: []string{"zone1"}, 1176 }}}}, nil 1177 } 1178 1179 // NetworkInterfaces implements Environ.NetworkInterfaces(). 1180 func (env *environ) NetworkInterfaces(instId instance.Id) ([]network.InterfaceInfo, error) { 1181 if err := env.checkBroken("NetworkInterfaces"); err != nil { 1182 return nil, err 1183 } 1184 1185 estate, err := env.state() 1186 if err != nil { 1187 return nil, err 1188 } 1189 estate.mu.Lock() 1190 defer estate.mu.Unlock() 1191 1192 // Simulate 3 NICs - primary and secondary enabled plus a disabled NIC. 1193 // all configured using DHCP and having fake DNS servers and gateway. 1194 info := make([]network.InterfaceInfo, 3) 1195 for i, netName := range []string{"private", "public", "disabled"} { 1196 info[i] = network.InterfaceInfo{ 1197 DeviceIndex: i, 1198 ProviderId: network.Id(fmt.Sprintf("dummy-eth%d", i)), 1199 ProviderSubnetId: network.Id("dummy-" + netName), 1200 InterfaceType: network.EthernetInterface, 1201 CIDR: fmt.Sprintf("0.%d.0.0/24", (i+1)*10), 1202 InterfaceName: fmt.Sprintf("eth%d", i), 1203 VLANTag: i, 1204 MACAddress: fmt.Sprintf("aa:bb:cc:dd:ee:f%d", i), 1205 Disabled: i == 2, 1206 NoAutoStart: i%2 != 0, 1207 ConfigType: network.ConfigDHCP, 1208 Address: network.NewAddress( 1209 fmt.Sprintf("0.%d.0.%d", (i+1)*10, estate.maxAddr+2), 1210 ), 1211 DNSServers: network.NewAddresses("ns1.dummy", "ns2.dummy"), 1212 GatewayAddress: network.NewAddress( 1213 fmt.Sprintf("0.%d.0.1", (i+1)*10), 1214 ), 1215 } 1216 } 1217 1218 estate.ops <- OpNetworkInterfaces{ 1219 Env: env.name, 1220 InstanceId: instId, 1221 Info: info, 1222 } 1223 1224 return info, nil 1225 } 1226 1227 type azShim struct { 1228 name string 1229 available bool 1230 } 1231 1232 func (az azShim) Name() string { 1233 return az.name 1234 } 1235 1236 func (az azShim) Available() bool { 1237 return az.available 1238 } 1239 1240 // AvailabilityZones implements environs.ZonedEnviron. 1241 func (env *environ) AvailabilityZones() ([]common.AvailabilityZone, error) { 1242 // TODO(dimitern): Fix this properly. 1243 return []common.AvailabilityZone{ 1244 azShim{"zone1", true}, 1245 azShim{"zone2", false}, 1246 }, nil 1247 } 1248 1249 // InstanceAvailabilityZoneNames implements environs.ZonedEnviron. 1250 func (env *environ) InstanceAvailabilityZoneNames(ids []instance.Id) ([]string, error) { 1251 // TODO(dimitern): Fix this properly. 1252 if err := env.checkBroken("InstanceAvailabilityZoneNames"); err != nil { 1253 return nil, errors.NotSupportedf("instance availability zones") 1254 } 1255 return []string{"zone1"}, nil 1256 } 1257 1258 // Subnets implements environs.Environ.Subnets. 1259 func (env *environ) Subnets(instId instance.Id, subnetIds []network.Id) ([]network.SubnetInfo, error) { 1260 if err := env.checkBroken("Subnets"); err != nil { 1261 return nil, err 1262 } 1263 1264 estate, err := env.state() 1265 if err != nil { 1266 return nil, err 1267 } 1268 estate.mu.Lock() 1269 defer estate.mu.Unlock() 1270 1271 if ok, _ := env.SupportsSpaceDiscovery(); ok { 1272 // Space discovery needs more subnets to work with. 1273 return env.subnetsForSpaceDiscovery(estate) 1274 } 1275 1276 allSubnets := []network.SubnetInfo{{ 1277 CIDR: "0.10.0.0/24", 1278 ProviderId: "dummy-private", 1279 AvailabilityZones: []string{"zone1", "zone2"}, 1280 }, { 1281 CIDR: "0.20.0.0/24", 1282 ProviderId: "dummy-public", 1283 }} 1284 1285 // Filter result by ids, if given. 1286 var result []network.SubnetInfo 1287 for _, subId := range subnetIds { 1288 switch subId { 1289 case "dummy-private": 1290 result = append(result, allSubnets[0]) 1291 case "dummy-public": 1292 result = append(result, allSubnets[1]) 1293 } 1294 } 1295 if len(subnetIds) == 0 { 1296 result = append([]network.SubnetInfo{}, allSubnets...) 1297 } 1298 if len(result) == 0 { 1299 // No results, so just return them now. 1300 estate.ops <- OpSubnets{ 1301 Env: env.name, 1302 InstanceId: instId, 1303 SubnetIds: subnetIds, 1304 Info: result, 1305 } 1306 return result, nil 1307 } 1308 1309 estate.ops <- OpSubnets{ 1310 Env: env.name, 1311 InstanceId: instId, 1312 SubnetIds: subnetIds, 1313 Info: result, 1314 } 1315 return result, nil 1316 } 1317 1318 func (env *environ) subnetsForSpaceDiscovery(estate *environState) ([]network.SubnetInfo, error) { 1319 result := []network.SubnetInfo{{ 1320 ProviderId: network.Id("1"), 1321 AvailabilityZones: []string{"zone1"}, 1322 CIDR: "192.168.1.0/24", 1323 }, { 1324 ProviderId: network.Id("2"), 1325 AvailabilityZones: []string{"zone1"}, 1326 CIDR: "192.168.2.0/24", 1327 VLANTag: 1, 1328 }, { 1329 ProviderId: network.Id("3"), 1330 AvailabilityZones: []string{"zone1"}, 1331 CIDR: "192.168.3.0/24", 1332 }, { 1333 ProviderId: network.Id("4"), 1334 AvailabilityZones: []string{"zone1"}, 1335 CIDR: "192.168.4.0/24", 1336 }, { 1337 ProviderId: network.Id("5"), 1338 AvailabilityZones: []string{"zone1"}, 1339 CIDR: "192.168.5.0/24", 1340 }} 1341 estate.ops <- OpSubnets{ 1342 Env: env.name, 1343 InstanceId: instance.UnknownId, 1344 SubnetIds: []network.Id{}, 1345 Info: result, 1346 } 1347 return result, nil 1348 } 1349 1350 func (e *environ) AllInstances() ([]instance.Instance, error) { 1351 defer delay() 1352 if err := e.checkBroken("AllInstances"); err != nil { 1353 return nil, err 1354 } 1355 var insts []instance.Instance 1356 estate, err := e.state() 1357 if err != nil { 1358 return nil, err 1359 } 1360 estate.mu.Lock() 1361 defer estate.mu.Unlock() 1362 for _, v := range estate.insts { 1363 insts = append(insts, v) 1364 } 1365 return insts, nil 1366 } 1367 1368 func (e *environ) OpenPorts(ports []network.PortRange) error { 1369 if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal { 1370 return fmt.Errorf("invalid firewall mode %q for opening ports on model", mode) 1371 } 1372 estate, err := e.state() 1373 if err != nil { 1374 return err 1375 } 1376 estate.mu.Lock() 1377 defer estate.mu.Unlock() 1378 for _, p := range ports { 1379 estate.globalPorts[p] = true 1380 } 1381 return nil 1382 } 1383 1384 func (e *environ) ClosePorts(ports []network.PortRange) error { 1385 if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal { 1386 return fmt.Errorf("invalid firewall mode %q for closing ports on model", mode) 1387 } 1388 estate, err := e.state() 1389 if err != nil { 1390 return err 1391 } 1392 estate.mu.Lock() 1393 defer estate.mu.Unlock() 1394 for _, p := range ports { 1395 delete(estate.globalPorts, p) 1396 } 1397 return nil 1398 } 1399 1400 func (e *environ) Ports() (ports []network.PortRange, err error) { 1401 if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal { 1402 return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from model", mode) 1403 } 1404 estate, err := e.state() 1405 if err != nil { 1406 return nil, err 1407 } 1408 estate.mu.Lock() 1409 defer estate.mu.Unlock() 1410 for p := range estate.globalPorts { 1411 ports = append(ports, p) 1412 } 1413 network.SortPortRanges(ports) 1414 return 1415 } 1416 1417 func (*environ) Provider() environs.EnvironProvider { 1418 return &dummy 1419 } 1420 1421 type dummyInstance struct { 1422 state *environState 1423 ports map[network.PortRange]bool 1424 id instance.Id 1425 status string 1426 machineId string 1427 series string 1428 firewallMode string 1429 controller bool 1430 1431 mu sync.Mutex 1432 addresses []network.Address 1433 broken []string 1434 } 1435 1436 func (inst *dummyInstance) Id() instance.Id { 1437 return inst.id 1438 } 1439 1440 func (inst *dummyInstance) Status() instance.InstanceStatus { 1441 inst.mu.Lock() 1442 defer inst.mu.Unlock() 1443 // TODO(perrito666) add a provider status -> juju status mapping. 1444 jujuStatus := status.Pending 1445 if inst.status != "" { 1446 dummyStatus := status.Status(inst.status) 1447 if dummyStatus.KnownInstanceStatus() { 1448 jujuStatus = dummyStatus 1449 } 1450 } 1451 1452 return instance.InstanceStatus{ 1453 Status: jujuStatus, 1454 Message: inst.status, 1455 } 1456 1457 } 1458 1459 // SetInstanceAddresses sets the addresses associated with the given 1460 // dummy instance. 1461 func SetInstanceAddresses(inst instance.Instance, addrs []network.Address) { 1462 inst0 := inst.(*dummyInstance) 1463 inst0.mu.Lock() 1464 inst0.addresses = append(inst0.addresses[:0], addrs...) 1465 logger.Debugf("setting instance %q addresses to %v", inst0.Id(), addrs) 1466 inst0.mu.Unlock() 1467 } 1468 1469 // SetInstanceStatus sets the status associated with the given 1470 // dummy instance. 1471 func SetInstanceStatus(inst instance.Instance, status string) { 1472 inst0 := inst.(*dummyInstance) 1473 inst0.mu.Lock() 1474 inst0.status = status 1475 inst0.mu.Unlock() 1476 } 1477 1478 // SetInstanceBroken marks the named methods of the instance as broken. 1479 // Any previously broken methods not in the set will no longer be broken. 1480 func SetInstanceBroken(inst instance.Instance, methods ...string) { 1481 inst0 := inst.(*dummyInstance) 1482 inst0.mu.Lock() 1483 inst0.broken = methods 1484 inst0.mu.Unlock() 1485 } 1486 1487 func (inst *dummyInstance) checkBroken(method string) error { 1488 for _, m := range inst.broken { 1489 if m == method { 1490 return fmt.Errorf("dummyInstance.%s is broken", method) 1491 } 1492 } 1493 return nil 1494 } 1495 1496 func (inst *dummyInstance) Addresses() ([]network.Address, error) { 1497 inst.mu.Lock() 1498 defer inst.mu.Unlock() 1499 if err := inst.checkBroken("Addresses"); err != nil { 1500 return nil, err 1501 } 1502 return append([]network.Address{}, inst.addresses...), nil 1503 } 1504 1505 func (inst *dummyInstance) OpenPorts(machineId string, ports []network.PortRange) error { 1506 defer delay() 1507 logger.Infof("openPorts %s, %#v", machineId, ports) 1508 if inst.firewallMode != config.FwInstance { 1509 return fmt.Errorf("invalid firewall mode %q for opening ports on instance", 1510 inst.firewallMode) 1511 } 1512 if inst.machineId != machineId { 1513 panic(fmt.Errorf("OpenPorts with mismatched machine id, expected %q got %q", inst.machineId, machineId)) 1514 } 1515 inst.state.mu.Lock() 1516 defer inst.state.mu.Unlock() 1517 if err := inst.checkBroken("OpenPorts"); err != nil { 1518 return err 1519 } 1520 inst.state.ops <- OpOpenPorts{ 1521 Env: inst.state.name, 1522 MachineId: machineId, 1523 InstanceId: inst.Id(), 1524 Ports: ports, 1525 } 1526 for _, p := range ports { 1527 inst.ports[p] = true 1528 } 1529 return nil 1530 } 1531 1532 func (inst *dummyInstance) ClosePorts(machineId string, ports []network.PortRange) error { 1533 defer delay() 1534 if inst.firewallMode != config.FwInstance { 1535 return fmt.Errorf("invalid firewall mode %q for closing ports on instance", 1536 inst.firewallMode) 1537 } 1538 if inst.machineId != machineId { 1539 panic(fmt.Errorf("ClosePorts with mismatched machine id, expected %s got %s", inst.machineId, machineId)) 1540 } 1541 inst.state.mu.Lock() 1542 defer inst.state.mu.Unlock() 1543 if err := inst.checkBroken("ClosePorts"); err != nil { 1544 return err 1545 } 1546 inst.state.ops <- OpClosePorts{ 1547 Env: inst.state.name, 1548 MachineId: machineId, 1549 InstanceId: inst.Id(), 1550 Ports: ports, 1551 } 1552 for _, p := range ports { 1553 delete(inst.ports, p) 1554 } 1555 return nil 1556 } 1557 1558 func (inst *dummyInstance) Ports(machineId string) (ports []network.PortRange, err error) { 1559 defer delay() 1560 if inst.firewallMode != config.FwInstance { 1561 return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from instance", 1562 inst.firewallMode) 1563 } 1564 if inst.machineId != machineId { 1565 panic(fmt.Errorf("Ports with mismatched machine id, expected %q got %q", inst.machineId, machineId)) 1566 } 1567 inst.state.mu.Lock() 1568 defer inst.state.mu.Unlock() 1569 if err := inst.checkBroken("Ports"); err != nil { 1570 return nil, err 1571 } 1572 for p := range inst.ports { 1573 ports = append(ports, p) 1574 } 1575 network.SortPortRanges(ports) 1576 return 1577 } 1578 1579 // providerDelay controls the delay before dummy responds. 1580 // non empty values in JUJU_DUMMY_DELAY will be parsed as 1581 // time.Durations into this value. 1582 var providerDelay, _ = time.ParseDuration(os.Getenv("JUJU_DUMMY_DELAY")) // parse errors are ignored 1583 1584 // pause execution to simulate the latency of a real provider 1585 func delay() { 1586 if providerDelay > 0 { 1587 logger.Infof("pausing for %v", providerDelay) 1588 <-time.After(providerDelay) 1589 } 1590 } 1591 1592 func (e *environ) AllocateContainerAddresses(hostInstanceID instance.Id, containerTag names.MachineTag, preparedInfo []network.InterfaceInfo) ([]network.InterfaceInfo, error) { 1593 return nil, errors.NotSupportedf("container address allocation") 1594 } 1595 1596 func (e *environ) ReleaseContainerAddresses(interfaces []network.ProviderInterfaceInfo) error { 1597 return errors.NotSupportedf("container address allocation") 1598 }