github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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.Id(), 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 // BootstrapMessage is part of the Environ interface. 835 func (e *environ) BootstrapMessage() string { 836 return "" 837 } 838 839 func (e *environ) ControllerInstances(controllerUUID string) ([]instance.Id, error) { 840 estate, err := e.state() 841 if err != nil { 842 return nil, err 843 } 844 estate.mu.Lock() 845 defer estate.mu.Unlock() 846 if err := e.checkBroken("ControllerInstances"); err != nil { 847 return nil, err 848 } 849 if !estate.bootstrapped { 850 return nil, environs.ErrNotBootstrapped 851 } 852 var controllerInstances []instance.Id 853 for _, v := range estate.insts { 854 if v.controller { 855 controllerInstances = append(controllerInstances, v.Id()) 856 } 857 } 858 return controllerInstances, nil 859 } 860 861 func (e *environ) Config() *config.Config { 862 return e.ecfg().Config 863 } 864 865 func (e *environ) SetConfig(cfg *config.Config) error { 866 if err := e.checkBroken("SetConfig"); err != nil { 867 return err 868 } 869 ecfg, err := dummy.newConfig(cfg) 870 if err != nil { 871 return err 872 } 873 e.ecfgMutex.Lock() 874 e.ecfgUnlocked = ecfg 875 e.ecfgMutex.Unlock() 876 return nil 877 } 878 879 func (e *environ) Destroy() (res error) { 880 defer delay() 881 estate, err := e.state() 882 if err != nil { 883 if err == errNotPrepared { 884 return nil 885 } 886 return err 887 } 888 defer func() { 889 // The estate is a pointer to a structure that is stored in the dummy global. 890 // The Listen method can change the ops channel of any state, and will do so 891 // under the covers. What we need to do is use the state mutex to add a memory 892 // barrier such that the ops channel we see here is the latest. 893 estate.mu.Lock() 894 ops := estate.ops 895 name := estate.name 896 delete(dummy.state, e.modelUUID) 897 estate.mu.Unlock() 898 ops <- OpDestroy{ 899 Env: name, 900 Cloud: e.cloud.Name, 901 CloudRegion: e.cloud.Region, 902 Error: res, 903 } 904 }() 905 if err := e.checkBroken("Destroy"); err != nil { 906 return err 907 } 908 if !e.ecfg().controller() { 909 return nil 910 } 911 estate.destroy() 912 return nil 913 } 914 915 func (e *environ) DestroyController(controllerUUID string) error { 916 if err := e.Destroy(); err != nil { 917 return err 918 } 919 dummy.mu.Lock() 920 dummy.controllerState = nil 921 dummy.mu.Unlock() 922 return nil 923 } 924 925 // ConstraintsValidator is defined on the Environs interface. 926 func (e *environ) ConstraintsValidator() (constraints.Validator, error) { 927 validator := constraints.NewValidator() 928 validator.RegisterUnsupported([]string{constraints.CpuPower, constraints.VirtType}) 929 validator.RegisterConflicts([]string{constraints.InstanceType}, []string{constraints.Mem}) 930 validator.RegisterVocabulary(constraints.Arch, []string{arch.AMD64, arch.ARM64, arch.I386, arch.PPC64EL}) 931 return validator, nil 932 } 933 934 // MaintainInstance is specified in the InstanceBroker interface. 935 func (*environ) MaintainInstance(args environs.StartInstanceParams) error { 936 return nil 937 } 938 939 // StartInstance is specified in the InstanceBroker interface. 940 func (e *environ) StartInstance(args environs.StartInstanceParams) (*environs.StartInstanceResult, error) { 941 942 defer delay() 943 machineId := args.InstanceConfig.MachineId 944 logger.Infof("dummy startinstance, machine %s", machineId) 945 if err := e.checkBroken("StartInstance"); err != nil { 946 return nil, err 947 } 948 estate, err := e.state() 949 if err != nil { 950 return nil, err 951 } 952 estate.mu.Lock() 953 defer estate.mu.Unlock() 954 955 // check if an error has been injected on the transientErrorInjection channel (testing purposes) 956 select { 957 case injectedError := <-transientErrorInjection: 958 return nil, injectedError 959 default: 960 } 961 962 if args.InstanceConfig.MachineNonce == "" { 963 return nil, errors.New("cannot start instance: missing machine nonce") 964 } 965 if args.InstanceConfig.Controller != nil { 966 if args.InstanceConfig.Controller.MongoInfo.Tag != names.NewMachineTag(machineId) { 967 return nil, errors.New("entity tag must match started machine") 968 } 969 } 970 if args.InstanceConfig.APIInfo.Tag != names.NewMachineTag(machineId) { 971 return nil, errors.New("entity tag must match started machine") 972 } 973 logger.Infof("would pick tools from %s", args.Tools) 974 series := args.Tools.OneSeries() 975 976 idString := fmt.Sprintf("%s-%d", e.name, estate.maxId) 977 addrs := network.NewAddresses(idString+".dns", "127.0.0.1") 978 logger.Debugf("StartInstance addresses: %v", addrs) 979 i := &dummyInstance{ 980 id: instance.Id(idString), 981 addresses: addrs, 982 ports: make(map[network.PortRange]bool), 983 machineId: machineId, 984 series: series, 985 firewallMode: e.Config().FirewallMode(), 986 state: estate, 987 } 988 989 var hc *instance.HardwareCharacteristics 990 // To match current system capability, only provide hardware characteristics for 991 // environ machines, not containers. 992 if state.ParentId(machineId) == "" { 993 // We will just assume the instance hardware characteristics exactly matches 994 // the supplied constraints (if specified). 995 hc = &instance.HardwareCharacteristics{ 996 Arch: args.Constraints.Arch, 997 Mem: args.Constraints.Mem, 998 RootDisk: args.Constraints.RootDisk, 999 CpuCores: args.Constraints.CpuCores, 1000 CpuPower: args.Constraints.CpuPower, 1001 Tags: args.Constraints.Tags, 1002 } 1003 // Fill in some expected instance hardware characteristics if constraints not specified. 1004 if hc.Arch == nil { 1005 arch := "amd64" 1006 hc.Arch = &arch 1007 } 1008 if hc.Mem == nil { 1009 mem := uint64(1024) 1010 hc.Mem = &mem 1011 } 1012 if hc.RootDisk == nil { 1013 disk := uint64(8192) 1014 hc.RootDisk = &disk 1015 } 1016 if hc.CpuCores == nil { 1017 cores := uint64(1) 1018 hc.CpuCores = &cores 1019 } 1020 } 1021 // Simulate subnetsToZones gets populated when spaces given in constraints. 1022 spaces := args.Constraints.IncludeSpaces() 1023 var subnetsToZones map[network.Id][]string 1024 for isp := range spaces { 1025 // Simulate 2 subnets per space. 1026 if subnetsToZones == nil { 1027 subnetsToZones = make(map[network.Id][]string) 1028 } 1029 for isn := 0; isn < 2; isn++ { 1030 providerId := fmt.Sprintf("subnet-%d", isp+isn) 1031 zone := fmt.Sprintf("zone%d", isp+isn) 1032 subnetsToZones[network.Id(providerId)] = []string{zone} 1033 } 1034 } 1035 // Simulate creating volumes when requested. 1036 volumes := make([]storage.Volume, len(args.Volumes)) 1037 for iv, v := range args.Volumes { 1038 persistent, _ := v.Attributes["persistent"].(bool) 1039 volumes[iv] = storage.Volume{ 1040 Tag: names.NewVolumeTag(strconv.Itoa(iv + 1)), 1041 VolumeInfo: storage.VolumeInfo{ 1042 Size: v.Size, 1043 Persistent: persistent, 1044 }, 1045 } 1046 } 1047 var mongoInfo *mongo.MongoInfo 1048 if args.InstanceConfig.Controller != nil { 1049 mongoInfo = args.InstanceConfig.Controller.MongoInfo 1050 } 1051 estate.insts[i.id] = i 1052 estate.maxId++ 1053 estate.ops <- OpStartInstance{ 1054 Env: e.name, 1055 MachineId: machineId, 1056 MachineNonce: args.InstanceConfig.MachineNonce, 1057 PossibleTools: args.Tools, 1058 Constraints: args.Constraints, 1059 SubnetsToZones: subnetsToZones, 1060 Volumes: volumes, 1061 Instance: i, 1062 Jobs: args.InstanceConfig.Jobs, 1063 Info: mongoInfo, 1064 APIInfo: args.InstanceConfig.APIInfo, 1065 AgentEnvironment: args.InstanceConfig.AgentEnvironment, 1066 Secret: e.ecfg().secret(), 1067 } 1068 return &environs.StartInstanceResult{ 1069 Instance: i, 1070 Hardware: hc, 1071 }, nil 1072 } 1073 1074 func (e *environ) StopInstances(ids ...instance.Id) error { 1075 defer delay() 1076 if err := e.checkBroken("StopInstance"); err != nil { 1077 return err 1078 } 1079 estate, err := e.state() 1080 if err != nil { 1081 return err 1082 } 1083 estate.mu.Lock() 1084 defer estate.mu.Unlock() 1085 for _, id := range ids { 1086 delete(estate.insts, id) 1087 } 1088 estate.ops <- OpStopInstances{ 1089 Env: e.name, 1090 Ids: ids, 1091 } 1092 return nil 1093 } 1094 1095 func (e *environ) Instances(ids []instance.Id) (insts []instance.Instance, err error) { 1096 defer delay() 1097 if err := e.checkBroken("Instances"); err != nil { 1098 return nil, err 1099 } 1100 if len(ids) == 0 { 1101 return nil, nil 1102 } 1103 estate, err := e.state() 1104 if err != nil { 1105 return nil, err 1106 } 1107 estate.mu.Lock() 1108 defer estate.mu.Unlock() 1109 notFound := 0 1110 for _, id := range ids { 1111 inst := estate.insts[id] 1112 if inst == nil { 1113 err = environs.ErrPartialInstances 1114 notFound++ 1115 insts = append(insts, nil) 1116 } else { 1117 insts = append(insts, inst) 1118 } 1119 } 1120 if notFound == len(ids) { 1121 return nil, environs.ErrNoInstances 1122 } 1123 return 1124 } 1125 1126 // SupportsSpaces is specified on environs.Networking. 1127 func (env *environ) SupportsSpaces() (bool, error) { 1128 dummy.mu.Lock() 1129 defer dummy.mu.Unlock() 1130 if !dummy.supportsSpaces { 1131 return false, errors.NotSupportedf("spaces") 1132 } 1133 return true, nil 1134 } 1135 1136 // SupportsSpaceDiscovery is specified on environs.Networking. 1137 func (env *environ) SupportsSpaceDiscovery() (bool, error) { 1138 if err := env.checkBroken("SupportsSpaceDiscovery"); err != nil { 1139 return false, err 1140 } 1141 dummy.mu.Lock() 1142 defer dummy.mu.Unlock() 1143 if !dummy.supportsSpaceDiscovery { 1144 return false, nil 1145 } 1146 return true, nil 1147 } 1148 1149 // Spaces is specified on environs.Networking. 1150 func (env *environ) Spaces() ([]network.SpaceInfo, error) { 1151 if err := env.checkBroken("Spaces"); err != nil { 1152 return []network.SpaceInfo{}, err 1153 } 1154 return []network.SpaceInfo{{ 1155 Name: "foo", 1156 ProviderId: network.Id("0"), 1157 Subnets: []network.SubnetInfo{{ 1158 ProviderId: network.Id("1"), 1159 AvailabilityZones: []string{"zone1"}, 1160 }, { 1161 ProviderId: network.Id("2"), 1162 AvailabilityZones: []string{"zone1"}, 1163 }}}, { 1164 Name: "Another Foo 99!", 1165 ProviderId: "1", 1166 Subnets: []network.SubnetInfo{{ 1167 ProviderId: network.Id("3"), 1168 AvailabilityZones: []string{"zone1"}, 1169 }}}, { 1170 Name: "foo-", 1171 ProviderId: "2", 1172 Subnets: []network.SubnetInfo{{ 1173 ProviderId: network.Id("4"), 1174 AvailabilityZones: []string{"zone1"}, 1175 }}}, { 1176 Name: "---", 1177 ProviderId: "3", 1178 Subnets: []network.SubnetInfo{{ 1179 ProviderId: network.Id("5"), 1180 AvailabilityZones: []string{"zone1"}, 1181 }}}}, nil 1182 } 1183 1184 // NetworkInterfaces implements Environ.NetworkInterfaces(). 1185 func (env *environ) NetworkInterfaces(instId instance.Id) ([]network.InterfaceInfo, error) { 1186 if err := env.checkBroken("NetworkInterfaces"); err != nil { 1187 return nil, err 1188 } 1189 1190 estate, err := env.state() 1191 if err != nil { 1192 return nil, err 1193 } 1194 estate.mu.Lock() 1195 defer estate.mu.Unlock() 1196 1197 // Simulate 3 NICs - primary and secondary enabled plus a disabled NIC. 1198 // all configured using DHCP and having fake DNS servers and gateway. 1199 info := make([]network.InterfaceInfo, 3) 1200 for i, netName := range []string{"private", "public", "disabled"} { 1201 info[i] = network.InterfaceInfo{ 1202 DeviceIndex: i, 1203 ProviderId: network.Id(fmt.Sprintf("dummy-eth%d", i)), 1204 ProviderSubnetId: network.Id("dummy-" + netName), 1205 InterfaceType: network.EthernetInterface, 1206 CIDR: fmt.Sprintf("0.%d.0.0/24", (i+1)*10), 1207 InterfaceName: fmt.Sprintf("eth%d", i), 1208 VLANTag: i, 1209 MACAddress: fmt.Sprintf("aa:bb:cc:dd:ee:f%d", i), 1210 Disabled: i == 2, 1211 NoAutoStart: i%2 != 0, 1212 ConfigType: network.ConfigDHCP, 1213 Address: network.NewAddress( 1214 fmt.Sprintf("0.%d.0.%d", (i+1)*10, estate.maxAddr+2), 1215 ), 1216 DNSServers: network.NewAddresses("ns1.dummy", "ns2.dummy"), 1217 GatewayAddress: network.NewAddress( 1218 fmt.Sprintf("0.%d.0.1", (i+1)*10), 1219 ), 1220 } 1221 } 1222 1223 estate.ops <- OpNetworkInterfaces{ 1224 Env: env.name, 1225 InstanceId: instId, 1226 Info: info, 1227 } 1228 1229 return info, nil 1230 } 1231 1232 type azShim struct { 1233 name string 1234 available bool 1235 } 1236 1237 func (az azShim) Name() string { 1238 return az.name 1239 } 1240 1241 func (az azShim) Available() bool { 1242 return az.available 1243 } 1244 1245 // AvailabilityZones implements environs.ZonedEnviron. 1246 func (env *environ) AvailabilityZones() ([]common.AvailabilityZone, error) { 1247 // TODO(dimitern): Fix this properly. 1248 return []common.AvailabilityZone{ 1249 azShim{"zone1", true}, 1250 azShim{"zone2", false}, 1251 }, nil 1252 } 1253 1254 // InstanceAvailabilityZoneNames implements environs.ZonedEnviron. 1255 func (env *environ) InstanceAvailabilityZoneNames(ids []instance.Id) ([]string, error) { 1256 // TODO(dimitern): Fix this properly. 1257 if err := env.checkBroken("InstanceAvailabilityZoneNames"); err != nil { 1258 return nil, errors.NotSupportedf("instance availability zones") 1259 } 1260 return []string{"zone1"}, nil 1261 } 1262 1263 // Subnets implements environs.Environ.Subnets. 1264 func (env *environ) Subnets(instId instance.Id, subnetIds []network.Id) ([]network.SubnetInfo, error) { 1265 if err := env.checkBroken("Subnets"); err != nil { 1266 return nil, err 1267 } 1268 1269 estate, err := env.state() 1270 if err != nil { 1271 return nil, err 1272 } 1273 estate.mu.Lock() 1274 defer estate.mu.Unlock() 1275 1276 if ok, _ := env.SupportsSpaceDiscovery(); ok { 1277 // Space discovery needs more subnets to work with. 1278 return env.subnetsForSpaceDiscovery(estate) 1279 } 1280 1281 allSubnets := []network.SubnetInfo{{ 1282 CIDR: "0.10.0.0/24", 1283 ProviderId: "dummy-private", 1284 AvailabilityZones: []string{"zone1", "zone2"}, 1285 }, { 1286 CIDR: "0.20.0.0/24", 1287 ProviderId: "dummy-public", 1288 }} 1289 1290 // Filter result by ids, if given. 1291 var result []network.SubnetInfo 1292 for _, subId := range subnetIds { 1293 switch subId { 1294 case "dummy-private": 1295 result = append(result, allSubnets[0]) 1296 case "dummy-public": 1297 result = append(result, allSubnets[1]) 1298 } 1299 } 1300 if len(subnetIds) == 0 { 1301 result = append([]network.SubnetInfo{}, allSubnets...) 1302 } 1303 if len(result) == 0 { 1304 // No results, so just return them now. 1305 estate.ops <- OpSubnets{ 1306 Env: env.name, 1307 InstanceId: instId, 1308 SubnetIds: subnetIds, 1309 Info: result, 1310 } 1311 return result, nil 1312 } 1313 1314 estate.ops <- OpSubnets{ 1315 Env: env.name, 1316 InstanceId: instId, 1317 SubnetIds: subnetIds, 1318 Info: result, 1319 } 1320 return result, nil 1321 } 1322 1323 func (env *environ) subnetsForSpaceDiscovery(estate *environState) ([]network.SubnetInfo, error) { 1324 result := []network.SubnetInfo{{ 1325 ProviderId: network.Id("1"), 1326 AvailabilityZones: []string{"zone1"}, 1327 CIDR: "192.168.1.0/24", 1328 }, { 1329 ProviderId: network.Id("2"), 1330 AvailabilityZones: []string{"zone1"}, 1331 CIDR: "192.168.2.0/24", 1332 VLANTag: 1, 1333 }, { 1334 ProviderId: network.Id("3"), 1335 AvailabilityZones: []string{"zone1"}, 1336 CIDR: "192.168.3.0/24", 1337 }, { 1338 ProviderId: network.Id("4"), 1339 AvailabilityZones: []string{"zone1"}, 1340 CIDR: "192.168.4.0/24", 1341 }, { 1342 ProviderId: network.Id("5"), 1343 AvailabilityZones: []string{"zone1"}, 1344 CIDR: "192.168.5.0/24", 1345 }} 1346 estate.ops <- OpSubnets{ 1347 Env: env.name, 1348 InstanceId: instance.UnknownId, 1349 SubnetIds: []network.Id{}, 1350 Info: result, 1351 } 1352 return result, nil 1353 } 1354 1355 func (e *environ) AllInstances() ([]instance.Instance, error) { 1356 defer delay() 1357 if err := e.checkBroken("AllInstances"); err != nil { 1358 return nil, err 1359 } 1360 var insts []instance.Instance 1361 estate, err := e.state() 1362 if err != nil { 1363 return nil, err 1364 } 1365 estate.mu.Lock() 1366 defer estate.mu.Unlock() 1367 for _, v := range estate.insts { 1368 insts = append(insts, v) 1369 } 1370 return insts, nil 1371 } 1372 1373 func (e *environ) OpenPorts(ports []network.PortRange) error { 1374 if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal { 1375 return fmt.Errorf("invalid firewall mode %q for opening ports on model", mode) 1376 } 1377 estate, err := e.state() 1378 if err != nil { 1379 return err 1380 } 1381 estate.mu.Lock() 1382 defer estate.mu.Unlock() 1383 for _, p := range ports { 1384 estate.globalPorts[p] = true 1385 } 1386 return nil 1387 } 1388 1389 func (e *environ) ClosePorts(ports []network.PortRange) error { 1390 if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal { 1391 return fmt.Errorf("invalid firewall mode %q for closing ports on model", mode) 1392 } 1393 estate, err := e.state() 1394 if err != nil { 1395 return err 1396 } 1397 estate.mu.Lock() 1398 defer estate.mu.Unlock() 1399 for _, p := range ports { 1400 delete(estate.globalPorts, p) 1401 } 1402 return nil 1403 } 1404 1405 func (e *environ) Ports() (ports []network.PortRange, err error) { 1406 if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal { 1407 return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from model", mode) 1408 } 1409 estate, err := e.state() 1410 if err != nil { 1411 return nil, err 1412 } 1413 estate.mu.Lock() 1414 defer estate.mu.Unlock() 1415 for p := range estate.globalPorts { 1416 ports = append(ports, p) 1417 } 1418 network.SortPortRanges(ports) 1419 return 1420 } 1421 1422 func (*environ) Provider() environs.EnvironProvider { 1423 return &dummy 1424 } 1425 1426 type dummyInstance struct { 1427 state *environState 1428 ports map[network.PortRange]bool 1429 id instance.Id 1430 status string 1431 machineId string 1432 series string 1433 firewallMode string 1434 controller bool 1435 1436 mu sync.Mutex 1437 addresses []network.Address 1438 broken []string 1439 } 1440 1441 func (inst *dummyInstance) Id() instance.Id { 1442 return inst.id 1443 } 1444 1445 func (inst *dummyInstance) Status() instance.InstanceStatus { 1446 inst.mu.Lock() 1447 defer inst.mu.Unlock() 1448 // TODO(perrito666) add a provider status -> juju status mapping. 1449 jujuStatus := status.Pending 1450 if inst.status != "" { 1451 dummyStatus := status.Status(inst.status) 1452 if dummyStatus.KnownInstanceStatus() { 1453 jujuStatus = dummyStatus 1454 } 1455 } 1456 1457 return instance.InstanceStatus{ 1458 Status: jujuStatus, 1459 Message: inst.status, 1460 } 1461 1462 } 1463 1464 // SetInstanceAddresses sets the addresses associated with the given 1465 // dummy instance. 1466 func SetInstanceAddresses(inst instance.Instance, addrs []network.Address) { 1467 inst0 := inst.(*dummyInstance) 1468 inst0.mu.Lock() 1469 inst0.addresses = append(inst0.addresses[:0], addrs...) 1470 logger.Debugf("setting instance %q addresses to %v", inst0.Id(), addrs) 1471 inst0.mu.Unlock() 1472 } 1473 1474 // SetInstanceStatus sets the status associated with the given 1475 // dummy instance. 1476 func SetInstanceStatus(inst instance.Instance, status string) { 1477 inst0 := inst.(*dummyInstance) 1478 inst0.mu.Lock() 1479 inst0.status = status 1480 inst0.mu.Unlock() 1481 } 1482 1483 // SetInstanceBroken marks the named methods of the instance as broken. 1484 // Any previously broken methods not in the set will no longer be broken. 1485 func SetInstanceBroken(inst instance.Instance, methods ...string) { 1486 inst0 := inst.(*dummyInstance) 1487 inst0.mu.Lock() 1488 inst0.broken = methods 1489 inst0.mu.Unlock() 1490 } 1491 1492 func (inst *dummyInstance) checkBroken(method string) error { 1493 for _, m := range inst.broken { 1494 if m == method { 1495 return fmt.Errorf("dummyInstance.%s is broken", method) 1496 } 1497 } 1498 return nil 1499 } 1500 1501 func (inst *dummyInstance) Addresses() ([]network.Address, error) { 1502 inst.mu.Lock() 1503 defer inst.mu.Unlock() 1504 if err := inst.checkBroken("Addresses"); err != nil { 1505 return nil, err 1506 } 1507 return append([]network.Address{}, inst.addresses...), nil 1508 } 1509 1510 func (inst *dummyInstance) OpenPorts(machineId string, ports []network.PortRange) error { 1511 defer delay() 1512 logger.Infof("openPorts %s, %#v", machineId, ports) 1513 if inst.firewallMode != config.FwInstance { 1514 return fmt.Errorf("invalid firewall mode %q for opening ports on instance", 1515 inst.firewallMode) 1516 } 1517 if inst.machineId != machineId { 1518 panic(fmt.Errorf("OpenPorts with mismatched machine id, expected %q got %q", inst.machineId, machineId)) 1519 } 1520 inst.state.mu.Lock() 1521 defer inst.state.mu.Unlock() 1522 if err := inst.checkBroken("OpenPorts"); err != nil { 1523 return err 1524 } 1525 inst.state.ops <- OpOpenPorts{ 1526 Env: inst.state.name, 1527 MachineId: machineId, 1528 InstanceId: inst.Id(), 1529 Ports: ports, 1530 } 1531 for _, p := range ports { 1532 inst.ports[p] = true 1533 } 1534 return nil 1535 } 1536 1537 func (inst *dummyInstance) ClosePorts(machineId string, ports []network.PortRange) error { 1538 defer delay() 1539 if inst.firewallMode != config.FwInstance { 1540 return fmt.Errorf("invalid firewall mode %q for closing ports on instance", 1541 inst.firewallMode) 1542 } 1543 if inst.machineId != machineId { 1544 panic(fmt.Errorf("ClosePorts with mismatched machine id, expected %s got %s", inst.machineId, machineId)) 1545 } 1546 inst.state.mu.Lock() 1547 defer inst.state.mu.Unlock() 1548 if err := inst.checkBroken("ClosePorts"); err != nil { 1549 return err 1550 } 1551 inst.state.ops <- OpClosePorts{ 1552 Env: inst.state.name, 1553 MachineId: machineId, 1554 InstanceId: inst.Id(), 1555 Ports: ports, 1556 } 1557 for _, p := range ports { 1558 delete(inst.ports, p) 1559 } 1560 return nil 1561 } 1562 1563 func (inst *dummyInstance) Ports(machineId string) (ports []network.PortRange, err error) { 1564 defer delay() 1565 if inst.firewallMode != config.FwInstance { 1566 return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from instance", 1567 inst.firewallMode) 1568 } 1569 if inst.machineId != machineId { 1570 panic(fmt.Errorf("Ports with mismatched machine id, expected %q got %q", inst.machineId, machineId)) 1571 } 1572 inst.state.mu.Lock() 1573 defer inst.state.mu.Unlock() 1574 if err := inst.checkBroken("Ports"); err != nil { 1575 return nil, err 1576 } 1577 for p := range inst.ports { 1578 ports = append(ports, p) 1579 } 1580 network.SortPortRanges(ports) 1581 return 1582 } 1583 1584 // providerDelay controls the delay before dummy responds. 1585 // non empty values in JUJU_DUMMY_DELAY will be parsed as 1586 // time.Durations into this value. 1587 var providerDelay, _ = time.ParseDuration(os.Getenv("JUJU_DUMMY_DELAY")) // parse errors are ignored 1588 1589 // pause execution to simulate the latency of a real provider 1590 func delay() { 1591 if providerDelay > 0 { 1592 logger.Infof("pausing for %v", providerDelay) 1593 <-time.After(providerDelay) 1594 } 1595 } 1596 1597 func (e *environ) AllocateContainerAddresses(hostInstanceID instance.Id, containerTag names.MachineTag, preparedInfo []network.InterfaceInfo) ([]network.InterfaceInfo, error) { 1598 return nil, errors.NotSupportedf("container address allocation") 1599 } 1600 1601 func (e *environ) ReleaseContainerAddresses(interfaces []network.ProviderInterfaceInfo) error { 1602 return errors.NotSupportedf("container address allocation") 1603 }