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