github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/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 "state-server" property with a boolean 9 // value. If this is true, a state server will be started 10 // the first time StateInfo is called on a newly reset environment. 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 "errors" 27 "fmt" 28 "net" 29 "net/http" 30 "os" 31 "strconv" 32 "strings" 33 "sync" 34 "time" 35 36 "github.com/juju/loggo" 37 "github.com/juju/names" 38 "github.com/juju/schema" 39 "github.com/juju/utils" 40 41 "github.com/juju/juju/constraints" 42 "github.com/juju/juju/environs" 43 "github.com/juju/juju/environs/bootstrap" 44 "github.com/juju/juju/environs/config" 45 "github.com/juju/juju/environs/imagemetadata" 46 "github.com/juju/juju/environs/network" 47 "github.com/juju/juju/environs/simplestreams" 48 "github.com/juju/juju/environs/storage" 49 "github.com/juju/juju/environs/tools" 50 "github.com/juju/juju/instance" 51 "github.com/juju/juju/juju/arch" 52 "github.com/juju/juju/provider" 53 "github.com/juju/juju/provider/common" 54 "github.com/juju/juju/state" 55 "github.com/juju/juju/state/api" 56 "github.com/juju/juju/state/apiserver" 57 "github.com/juju/juju/testing" 58 ) 59 60 var logger = loggo.GetLogger("juju.provider.dummy") 61 62 // SampleConfig() returns an environment configuration with all required 63 // attributes set. 64 func SampleConfig() testing.Attrs { 65 return testing.Attrs{ 66 "type": "dummy", 67 "name": "only", 68 "authorized-keys": testing.FakeAuthKeys, 69 "firewall-mode": config.FwInstance, 70 "admin-secret": testing.DefaultMongoPassword, 71 "ca-cert": testing.CACert, 72 "ca-private-key": testing.CAKey, 73 "ssl-hostname-verification": true, 74 "development": false, 75 "state-port": 1234, 76 "api-port": 4321, 77 "syslog-port": 2345, 78 "default-series": "precise", 79 80 "secret": "pork", 81 "state-server": true, 82 } 83 } 84 85 // stateInfo returns a *state.Info which allows clients to connect to the 86 // shared dummy state, if it exists. 87 func stateInfo() *state.Info { 88 if testing.MgoServer.Addr() == "" { 89 panic("dummy environ state tests must be run with MgoTestPackage") 90 } 91 return &state.Info{ 92 Addrs: []string{testing.MgoServer.Addr()}, 93 CACert: testing.CACert, 94 } 95 } 96 97 // Operation represents an action on the dummy provider. 98 type Operation interface{} 99 100 type OpBootstrap struct { 101 Context environs.BootstrapContext 102 Env string 103 Args environs.BootstrapParams 104 } 105 106 type OpDestroy struct { 107 Env string 108 Error error 109 } 110 111 type OpAllocateAddress struct { 112 Env string 113 InstanceId instance.Id 114 NetworkId network.Id 115 Address instance.Address 116 } 117 118 type OpStartInstance struct { 119 Env string 120 MachineId string 121 MachineNonce string 122 Instance instance.Instance 123 Constraints constraints.Value 124 Networks []string 125 NetworkInfo []network.Info 126 Info *state.Info 127 APIInfo *api.Info 128 Secret string 129 } 130 131 type OpStopInstances struct { 132 Env string 133 Ids []instance.Id 134 } 135 136 type OpOpenPorts struct { 137 Env string 138 MachineId string 139 InstanceId instance.Id 140 Ports []instance.Port 141 } 142 143 type OpClosePorts struct { 144 Env string 145 MachineId string 146 InstanceId instance.Id 147 Ports []instance.Port 148 } 149 150 type OpPutFile struct { 151 Env string 152 FileName string 153 } 154 155 // environProvider represents the dummy provider. There is only ever one 156 // instance of this type (providerInstance) 157 type environProvider struct { 158 mu sync.Mutex 159 ops chan<- Operation 160 statePolicy state.Policy 161 // We have one state for each environment name 162 state map[int]*environState 163 maxStateId int 164 } 165 166 var providerInstance environProvider 167 168 const noStateId = 0 169 170 // environState represents the state of an environment. 171 // It can be shared between several environ values, 172 // so that a given environment can be opened several times. 173 type environState struct { 174 id int 175 name string 176 ops chan<- Operation 177 statePolicy state.Policy 178 mu sync.Mutex 179 maxId int // maximum instance id allocated so far. 180 maxAddr int // maximum allocated address last byte 181 insts map[instance.Id]*dummyInstance 182 globalPorts map[instance.Port]bool 183 bootstrapped bool 184 storageDelay time.Duration 185 storage *storageServer 186 httpListener net.Listener 187 apiServer *apiserver.Server 188 apiState *state.State 189 } 190 191 // environ represents a client's connection to a given environment's 192 // state. 193 type environ struct { 194 common.SupportsUnitPlacementPolicy 195 196 name string 197 ecfgMutex sync.Mutex 198 ecfgUnlocked *environConfig 199 } 200 201 var _ imagemetadata.SupportsCustomSources = (*environ)(nil) 202 var _ tools.SupportsCustomSources = (*environ)(nil) 203 var _ environs.Environ = (*environ)(nil) 204 205 // discardOperations discards all Operations written to it. 206 var discardOperations chan<- Operation 207 208 func init() { 209 environs.RegisterProvider("dummy", &providerInstance) 210 211 // Prime the first ops channel, so that naive clients can use 212 // the testing environment by simply importing it. 213 c := make(chan Operation) 214 go func() { 215 for _ = range c { 216 } 217 }() 218 discardOperations = c 219 Reset() 220 221 // parse errors are ignored 222 providerDelay, _ = time.ParseDuration(os.Getenv("JUJU_DUMMY_DELAY")) 223 } 224 225 // Reset resets the entire dummy environment and forgets any registered 226 // operation listener. All opened environments after Reset will share 227 // the same underlying state. 228 func Reset() { 229 logger.Infof("reset environment") 230 p := &providerInstance 231 p.mu.Lock() 232 defer p.mu.Unlock() 233 providerInstance.ops = discardOperations 234 for _, s := range p.state { 235 s.httpListener.Close() 236 s.destroy() 237 } 238 providerInstance.state = make(map[int]*environState) 239 if mongoAlive() { 240 testing.MgoServer.Reset() 241 } 242 providerInstance.statePolicy = environs.NewStatePolicy() 243 } 244 245 func (state *environState) destroy() { 246 state.storage.files = make(map[string][]byte) 247 if !state.bootstrapped { 248 return 249 } 250 if state.apiServer != nil { 251 if err := state.apiServer.Stop(); err != nil && mongoAlive() { 252 panic(err) 253 } 254 state.apiServer = nil 255 if err := state.apiState.Close(); err != nil && mongoAlive() { 256 panic(err) 257 } 258 state.apiState = nil 259 } 260 if mongoAlive() { 261 testing.MgoServer.Reset() 262 } 263 state.bootstrapped = false 264 } 265 266 // mongoAlive reports whether the mongo server is 267 // still alive (i.e. has not been deliberately destroyed). 268 // If it has been deliberately destroyed, we will 269 // expect some errors when closing things down. 270 func mongoAlive() bool { 271 return testing.MgoServer.Addr() != "" 272 } 273 274 // GetStateInAPIServer returns the state connection used by the API server 275 // This is so code in the test suite can trigger Syncs, etc that the API server 276 // will see, which will then trigger API watchers, etc. 277 func (e *environ) GetStateInAPIServer() *state.State { 278 st, err := e.state() 279 if err != nil { 280 panic(err) 281 } 282 return st.apiState 283 } 284 285 // newState creates the state for a new environment with the 286 // given name and starts an http server listening for 287 // storage requests. 288 func newState(name string, ops chan<- Operation, policy state.Policy) *environState { 289 s := &environState{ 290 name: name, 291 ops: ops, 292 statePolicy: policy, 293 insts: make(map[instance.Id]*dummyInstance), 294 globalPorts: make(map[instance.Port]bool), 295 } 296 s.storage = newStorageServer(s, "/"+name+"/private") 297 s.listen() 298 return s 299 } 300 301 // listen starts a network listener listening for http 302 // requests to retrieve files in the state's storage. 303 func (s *environState) listen() { 304 l, err := net.Listen("tcp", "127.0.0.1:0") 305 if err != nil { 306 panic(fmt.Errorf("cannot start listener: %v", err)) 307 } 308 s.httpListener = l 309 mux := http.NewServeMux() 310 mux.Handle(s.storage.path+"/", http.StripPrefix(s.storage.path+"/", s.storage)) 311 go http.Serve(l, mux) 312 } 313 314 // SetStatePolicy sets the state.Policy to use when a 315 // state server is initialised by dummy. 316 func SetStatePolicy(policy state.Policy) { 317 p := &providerInstance 318 p.mu.Lock() 319 defer p.mu.Unlock() 320 p.statePolicy = policy 321 } 322 323 // Listen closes the previously registered listener (if any). 324 // Subsequent operations on any dummy environment can be received on c 325 // (if not nil). 326 func Listen(c chan<- Operation) { 327 p := &providerInstance 328 p.mu.Lock() 329 defer p.mu.Unlock() 330 if c == nil { 331 c = discardOperations 332 } 333 if p.ops != discardOperations { 334 close(p.ops) 335 } 336 p.ops = c 337 for _, st := range p.state { 338 st.mu.Lock() 339 st.ops = c 340 st.mu.Unlock() 341 } 342 } 343 344 // SetStorageDelay causes any storage download operation in any current 345 // environment to be delayed for the given duration. 346 func SetStorageDelay(d time.Duration) { 347 p := &providerInstance 348 p.mu.Lock() 349 defer p.mu.Unlock() 350 for _, st := range p.state { 351 st.mu.Lock() 352 st.storageDelay = d 353 st.mu.Unlock() 354 } 355 } 356 357 var configFields = schema.Fields{ 358 "state-server": schema.Bool(), 359 "broken": schema.String(), 360 "secret": schema.String(), 361 "state-id": schema.String(), 362 } 363 var configDefaults = schema.Defaults{ 364 "broken": "", 365 "secret": "pork", 366 "state-id": schema.Omit, 367 } 368 369 type environConfig struct { 370 *config.Config 371 attrs map[string]interface{} 372 } 373 374 func (c *environConfig) stateServer() bool { 375 return c.attrs["state-server"].(bool) 376 } 377 378 func (c *environConfig) broken() string { 379 return c.attrs["broken"].(string) 380 } 381 382 func (c *environConfig) secret() string { 383 return c.attrs["secret"].(string) 384 } 385 386 func (c *environConfig) stateId() int { 387 idStr, ok := c.attrs["state-id"].(string) 388 if !ok { 389 return noStateId 390 } 391 id, err := strconv.Atoi(idStr) 392 if err != nil { 393 panic(fmt.Errorf("unexpected state-id %q (should have pre-checked)", idStr)) 394 } 395 return id 396 } 397 398 func (p *environProvider) newConfig(cfg *config.Config) (*environConfig, error) { 399 valid, err := p.Validate(cfg, nil) 400 if err != nil { 401 return nil, err 402 } 403 return &environConfig{valid, valid.UnknownAttrs()}, nil 404 } 405 406 func (p *environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) { 407 // Check for valid changes for the base config values. 408 if err := config.Validate(cfg, old); err != nil { 409 return nil, err 410 } 411 validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) 412 if err != nil { 413 return nil, err 414 } 415 if idStr, ok := validated["state-id"].(string); ok { 416 if _, err := strconv.Atoi(idStr); err != nil { 417 return nil, fmt.Errorf("invalid state-id %q", idStr) 418 } 419 } 420 // Apply the coerced unknown values back into the config. 421 return cfg.Apply(validated) 422 } 423 424 func (e *environ) state() (*environState, error) { 425 stateId := e.ecfg().stateId() 426 if stateId == noStateId { 427 return nil, provider.ErrNotPrepared 428 } 429 p := &providerInstance 430 p.mu.Lock() 431 defer p.mu.Unlock() 432 if state := p.state[stateId]; state != nil { 433 return state, nil 434 } 435 return nil, provider.ErrDestroyed 436 } 437 438 func (p *environProvider) Open(cfg *config.Config) (environs.Environ, error) { 439 p.mu.Lock() 440 defer p.mu.Unlock() 441 ecfg, err := p.newConfig(cfg) 442 if err != nil { 443 return nil, err 444 } 445 if ecfg.stateId() == noStateId { 446 return nil, provider.ErrNotPrepared 447 } 448 env := &environ{ 449 name: ecfg.Name(), 450 ecfgUnlocked: ecfg, 451 } 452 if err := env.checkBroken("Open"); err != nil { 453 return nil, err 454 } 455 return env, nil 456 } 457 458 func (p *environProvider) Prepare(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) { 459 cfg, err := p.prepare(cfg) 460 if err != nil { 461 return nil, err 462 } 463 return p.Open(cfg) 464 } 465 466 // prepare is the internal version of Prepare - it prepares the 467 // environment but does not open it. 468 func (p *environProvider) prepare(cfg *config.Config) (*config.Config, error) { 469 ecfg, err := p.newConfig(cfg) 470 if err != nil { 471 return nil, err 472 } 473 p.mu.Lock() 474 defer p.mu.Unlock() 475 name := cfg.Name() 476 if ecfg.stateId() != noStateId { 477 return cfg, nil 478 } 479 // The environment has not been prepared, 480 // so create it and set its state identifier accordingly. 481 if ecfg.stateServer() && len(p.state) != 0 { 482 for _, old := range p.state { 483 panic(fmt.Errorf("cannot share a state between two dummy environs; old %q; new %q", old.name, name)) 484 } 485 } 486 state := newState(name, p.ops, p.statePolicy) 487 p.maxStateId++ 488 state.id = p.maxStateId 489 p.state[state.id] = state 490 // Add the state id to the configuration we use to 491 // in the returned environment. 492 return cfg.Apply(map[string]interface{}{ 493 "state-id": fmt.Sprint(state.id), 494 }) 495 } 496 497 func (*environProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) { 498 ecfg, err := providerInstance.newConfig(cfg) 499 if err != nil { 500 return nil, err 501 } 502 return map[string]string{ 503 "secret": ecfg.secret(), 504 }, nil 505 } 506 507 func (*environProvider) BoilerplateConfig() string { 508 return ` 509 # Fake configuration for dummy provider. 510 dummy: 511 type: dummy 512 513 `[1:] 514 } 515 516 var errBroken = errors.New("broken environment") 517 518 // Override for testing - the data directory with which the state api server is initialised. 519 var DataDir = "" 520 var LogDir = "" 521 522 func (e *environ) ecfg() *environConfig { 523 e.ecfgMutex.Lock() 524 ecfg := e.ecfgUnlocked 525 e.ecfgMutex.Unlock() 526 return ecfg 527 } 528 529 func (e *environ) checkBroken(method string) error { 530 for _, m := range strings.Fields(e.ecfg().broken()) { 531 if m == method { 532 return fmt.Errorf("dummy.%s is broken", method) 533 } 534 } 535 return nil 536 } 537 538 func (e *environ) Name() string { 539 return e.name 540 } 541 542 // SupportedArchitectures is specified on the EnvironCapability interface. 543 func (*environ) SupportedArchitectures() ([]string, error) { 544 return []string{arch.AMD64, arch.I386, arch.PPC64}, nil 545 } 546 547 // SupportNetworks is specified on the EnvironCapability interface. 548 func (*environ) SupportNetworks() bool { 549 return true 550 } 551 552 // PrecheckInstance is specified in the state.Prechecker interface. 553 func (*environ) PrecheckInstance(series string, cons constraints.Value, placement string) error { 554 if placement != "" && placement != "valid" { 555 return fmt.Errorf("%s placement is invalid", placement) 556 } 557 return nil 558 } 559 560 // GetImageSources returns a list of sources which are used to search for simplestreams image metadata. 561 func (e *environ) GetImageSources() ([]simplestreams.DataSource, error) { 562 return []simplestreams.DataSource{ 563 storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Storage(), storage.BaseImagesPath)}, nil 564 } 565 566 // GetToolsSources returns a list of sources which are used to search for simplestreams tools metadata. 567 func (e *environ) GetToolsSources() ([]simplestreams.DataSource, error) { 568 return []simplestreams.DataSource{ 569 storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Storage(), storage.BaseToolsPath)}, nil 570 } 571 572 func (e *environ) Bootstrap(ctx environs.BootstrapContext, args environs.BootstrapParams) error { 573 selectedTools, err := common.EnsureBootstrapTools(ctx, e, config.PreferredSeries(e.Config()), args.Constraints.Arch) 574 if err != nil { 575 return err 576 } 577 578 defer delay() 579 if err := e.checkBroken("Bootstrap"); err != nil { 580 return err 581 } 582 password := e.Config().AdminSecret() 583 if password == "" { 584 return fmt.Errorf("admin-secret is required for bootstrap") 585 } 586 if _, ok := e.Config().CACert(); !ok { 587 return fmt.Errorf("no CA certificate in environment configuration") 588 } 589 590 logger.Infof("would pick tools from %s", selectedTools) 591 cfg, err := environs.BootstrapConfig(e.Config()) 592 if err != nil { 593 return fmt.Errorf("cannot make bootstrap config: %v", err) 594 } 595 596 estate, err := e.state() 597 if err != nil { 598 return err 599 } 600 estate.mu.Lock() 601 defer estate.mu.Unlock() 602 if estate.bootstrapped { 603 return fmt.Errorf("environment is already bootstrapped") 604 } 605 // Write the bootstrap file just like a normal provider. However 606 // we need to release the mutex for the save state to work, so regain 607 // it after the call. 608 estate.mu.Unlock() 609 if err := bootstrap.SaveState(e.Storage(), &bootstrap.BootstrapState{StateInstances: []instance.Id{"localhost"}}); err != nil { 610 logger.Errorf("failed to save state instances: %v", err) 611 estate.mu.Lock() // otherwise defered unlock will fail 612 return err 613 } 614 estate.mu.Lock() // back at it 615 616 if e.ecfg().stateServer() { 617 // TODO(rog) factor out relevant code from cmd/jujud/bootstrap.go 618 // so that we can call it here. 619 620 info := stateInfo() 621 st, err := state.Initialize(info, cfg, state.DefaultDialOpts(), estate.statePolicy) 622 if err != nil { 623 panic(err) 624 } 625 if err := st.SetEnvironConstraints(args.Constraints); err != nil { 626 panic(err) 627 } 628 if err := st.SetAdminMongoPassword(utils.UserPasswordHash(password, utils.CompatSalt)); err != nil { 629 panic(err) 630 } 631 _, err = st.AddUser("admin", "", password) 632 if err != nil { 633 panic(err) 634 } 635 estate.apiServer, err = apiserver.NewServer(st, "localhost:0", []byte(testing.ServerCert), []byte(testing.ServerKey), DataDir, LogDir) 636 if err != nil { 637 panic(err) 638 } 639 estate.apiState = st 640 } 641 estate.bootstrapped = true 642 estate.ops <- OpBootstrap{Context: ctx, Env: e.name, Args: args} 643 return nil 644 } 645 646 func (e *environ) StateInfo() (*state.Info, *api.Info, error) { 647 estate, err := e.state() 648 if err != nil { 649 return nil, nil, err 650 } 651 estate.mu.Lock() 652 defer estate.mu.Unlock() 653 if err := e.checkBroken("StateInfo"); err != nil { 654 return nil, nil, err 655 } 656 if !e.ecfg().stateServer() { 657 return nil, nil, errors.New("dummy environment has no state configured") 658 } 659 if !estate.bootstrapped { 660 return nil, nil, environs.ErrNotBootstrapped 661 } 662 return stateInfo(), &api.Info{ 663 Addrs: []string{estate.apiServer.Addr()}, 664 CACert: testing.CACert, 665 }, nil 666 } 667 668 func (e *environ) Config() *config.Config { 669 return e.ecfg().Config 670 } 671 672 func (e *environ) SetConfig(cfg *config.Config) error { 673 if err := e.checkBroken("SetConfig"); err != nil { 674 return err 675 } 676 ecfg, err := providerInstance.newConfig(cfg) 677 if err != nil { 678 return err 679 } 680 e.ecfgMutex.Lock() 681 e.ecfgUnlocked = ecfg 682 e.ecfgMutex.Unlock() 683 return nil 684 } 685 686 func (e *environ) Destroy() (res error) { 687 defer delay() 688 estate, err := e.state() 689 if err != nil { 690 if err == provider.ErrDestroyed { 691 return nil 692 } 693 return err 694 } 695 defer func() { estate.ops <- OpDestroy{Env: estate.name, Error: res} }() 696 if err := e.checkBroken("Destroy"); err != nil { 697 return err 698 } 699 p := &providerInstance 700 p.mu.Lock() 701 delete(p.state, estate.id) 702 p.mu.Unlock() 703 704 estate.mu.Lock() 705 defer estate.mu.Unlock() 706 estate.destroy() 707 return nil 708 } 709 710 // ConstraintsValidator is defined on the Environs interface. 711 func (e *environ) ConstraintsValidator() (constraints.Validator, error) { 712 validator := constraints.NewValidator() 713 validator.RegisterUnsupported([]string{constraints.CpuPower}) 714 validator.RegisterConflicts([]string{constraints.InstanceType}, []string{constraints.Mem}) 715 return validator, nil 716 } 717 718 // StartInstance is specified in the InstanceBroker interface. 719 func (e *environ) StartInstance(args environs.StartInstanceParams) (instance.Instance, *instance.HardwareCharacteristics, []network.Info, error) { 720 721 defer delay() 722 machineId := args.MachineConfig.MachineId 723 logger.Infof("dummy startinstance, machine %s", machineId) 724 if err := e.checkBroken("StartInstance"); err != nil { 725 return nil, nil, nil, err 726 } 727 estate, err := e.state() 728 if err != nil { 729 return nil, nil, nil, err 730 } 731 estate.mu.Lock() 732 defer estate.mu.Unlock() 733 if args.MachineConfig.MachineNonce == "" { 734 return nil, nil, nil, fmt.Errorf("cannot start instance: missing machine nonce") 735 } 736 if _, ok := e.Config().CACert(); !ok { 737 return nil, nil, nil, fmt.Errorf("no CA certificate in environment configuration") 738 } 739 if args.MachineConfig.StateInfo.Tag != names.MachineTag(machineId) { 740 return nil, nil, nil, fmt.Errorf("entity tag must match started machine") 741 } 742 if args.MachineConfig.APIInfo.Tag != names.MachineTag(machineId) { 743 return nil, nil, nil, fmt.Errorf("entity tag must match started machine") 744 } 745 logger.Infof("would pick tools from %s", args.Tools) 746 series := args.Tools.OneSeries() 747 748 idString := fmt.Sprintf("%s-%d", e.name, estate.maxId) 749 i := &dummyInstance{ 750 id: instance.Id(idString), 751 addresses: instance.NewAddresses(idString + ".dns"), 752 ports: make(map[instance.Port]bool), 753 machineId: machineId, 754 series: series, 755 firewallMode: e.Config().FirewallMode(), 756 state: estate, 757 } 758 759 var hc *instance.HardwareCharacteristics 760 // To match current system capability, only provide hardware characteristics for 761 // environ machines, not containers. 762 if state.ParentId(machineId) == "" { 763 // We will just assume the instance hardware characteristics exactly matches 764 // the supplied constraints (if specified). 765 hc = &instance.HardwareCharacteristics{ 766 Arch: args.Constraints.Arch, 767 Mem: args.Constraints.Mem, 768 RootDisk: args.Constraints.RootDisk, 769 CpuCores: args.Constraints.CpuCores, 770 CpuPower: args.Constraints.CpuPower, 771 Tags: args.Constraints.Tags, 772 } 773 // Fill in some expected instance hardware characteristics if constraints not specified. 774 if hc.Arch == nil { 775 arch := "amd64" 776 hc.Arch = &arch 777 } 778 if hc.Mem == nil { 779 mem := uint64(1024) 780 hc.Mem = &mem 781 } 782 if hc.RootDisk == nil { 783 disk := uint64(8192) 784 hc.RootDisk = &disk 785 } 786 if hc.CpuCores == nil { 787 cores := uint64(1) 788 hc.CpuCores = &cores 789 } 790 } 791 // Simulate networks added when requested. 792 networks := append(args.Constraints.IncludeNetworks(), args.MachineConfig.Networks...) 793 networkInfo := make([]network.Info, len(networks)) 794 for i, netName := range networks { 795 if strings.HasPrefix(netName, "bad-") { 796 // Simulate we didn't get correct information for the network. 797 networkInfo[i] = network.Info{ 798 ProviderId: network.Id(netName), 799 NetworkName: netName, 800 CIDR: "invalid", 801 } 802 } else { 803 networkInfo[i] = network.Info{ 804 ProviderId: network.Id(netName), 805 NetworkName: netName, 806 CIDR: fmt.Sprintf("0.%d.2.0/24", i+1), 807 InterfaceName: fmt.Sprintf("eth%d", i), 808 VLANTag: i, 809 MACAddress: fmt.Sprintf("aa:bb:cc:dd:ee:f%d", i), 810 IsVirtual: i > 0, 811 } 812 } 813 } 814 estate.insts[i.id] = i 815 estate.maxId++ 816 estate.ops <- OpStartInstance{ 817 Env: e.name, 818 MachineId: machineId, 819 MachineNonce: args.MachineConfig.MachineNonce, 820 Constraints: args.Constraints, 821 Networks: args.MachineConfig.Networks, 822 NetworkInfo: networkInfo, 823 Instance: i, 824 Info: args.MachineConfig.StateInfo, 825 APIInfo: args.MachineConfig.APIInfo, 826 Secret: e.ecfg().secret(), 827 } 828 return i, hc, networkInfo, nil 829 } 830 831 func (e *environ) StopInstances(ids ...instance.Id) error { 832 defer delay() 833 if err := e.checkBroken("StopInstance"); err != nil { 834 return err 835 } 836 estate, err := e.state() 837 if err != nil { 838 return err 839 } 840 estate.mu.Lock() 841 defer estate.mu.Unlock() 842 for _, id := range ids { 843 delete(estate.insts, id) 844 } 845 estate.ops <- OpStopInstances{ 846 Env: e.name, 847 Ids: ids, 848 } 849 return nil 850 } 851 852 func (e *environ) Instances(ids []instance.Id) (insts []instance.Instance, err error) { 853 defer delay() 854 if err := e.checkBroken("Instances"); err != nil { 855 return nil, err 856 } 857 if len(ids) == 0 { 858 return nil, nil 859 } 860 estate, err := e.state() 861 if err != nil { 862 return nil, err 863 } 864 estate.mu.Lock() 865 defer estate.mu.Unlock() 866 notFound := 0 867 for _, id := range ids { 868 inst := estate.insts[id] 869 if inst == nil { 870 err = environs.ErrPartialInstances 871 notFound++ 872 insts = append(insts, nil) 873 } else { 874 insts = append(insts, inst) 875 } 876 } 877 if notFound == len(ids) { 878 return nil, environs.ErrNoInstances 879 } 880 return 881 } 882 883 // AllocateAddress requests a new address to be allocated for the 884 // given instance on the given network. 885 func (env *environ) AllocateAddress(instId instance.Id, netId network.Id) (instance.Address, error) { 886 if err := env.checkBroken("AllocateAddress"); err != nil { 887 return instance.Address{}, err 888 } 889 890 estate, err := env.state() 891 if err != nil { 892 return instance.Address{}, err 893 } 894 estate.mu.Lock() 895 defer estate.mu.Unlock() 896 estate.maxAddr++ 897 // TODO(dimitern) Once we have integrated networks 898 // and addresses, make sure we return a valid address 899 // for the given network, and we also have the network 900 // already registered. 901 newAddress := instance.NewAddress( 902 fmt.Sprintf("0.1.2.%d", estate.maxAddr), 903 instance.NetworkCloudLocal, 904 ) 905 estate.ops <- OpAllocateAddress{ 906 Env: env.name, 907 InstanceId: instId, 908 NetworkId: netId, 909 Address: newAddress, 910 } 911 return newAddress, nil 912 } 913 914 func (e *environ) AllInstances() ([]instance.Instance, error) { 915 defer delay() 916 if err := e.checkBroken("AllInstances"); err != nil { 917 return nil, err 918 } 919 var insts []instance.Instance 920 estate, err := e.state() 921 if err != nil { 922 return nil, err 923 } 924 estate.mu.Lock() 925 defer estate.mu.Unlock() 926 for _, v := range estate.insts { 927 insts = append(insts, v) 928 } 929 return insts, nil 930 } 931 932 func (e *environ) OpenPorts(ports []instance.Port) error { 933 if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal { 934 return fmt.Errorf("invalid firewall mode %q for opening ports on environment", mode) 935 } 936 estate, err := e.state() 937 if err != nil { 938 return err 939 } 940 estate.mu.Lock() 941 defer estate.mu.Unlock() 942 for _, p := range ports { 943 estate.globalPorts[p] = true 944 } 945 return nil 946 } 947 948 func (e *environ) ClosePorts(ports []instance.Port) error { 949 if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal { 950 return fmt.Errorf("invalid firewall mode %q for closing ports on environment", mode) 951 } 952 estate, err := e.state() 953 if err != nil { 954 return err 955 } 956 estate.mu.Lock() 957 defer estate.mu.Unlock() 958 for _, p := range ports { 959 delete(estate.globalPorts, p) 960 } 961 return nil 962 } 963 964 func (e *environ) Ports() (ports []instance.Port, err error) { 965 if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal { 966 return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from environment", mode) 967 } 968 estate, err := e.state() 969 if err != nil { 970 return nil, err 971 } 972 estate.mu.Lock() 973 defer estate.mu.Unlock() 974 for p := range estate.globalPorts { 975 ports = append(ports, p) 976 } 977 instance.SortPorts(ports) 978 return 979 } 980 981 func (*environ) Provider() environs.EnvironProvider { 982 return &providerInstance 983 } 984 985 type dummyInstance struct { 986 state *environState 987 ports map[instance.Port]bool 988 id instance.Id 989 status string 990 machineId string 991 series string 992 firewallMode string 993 994 mu sync.Mutex 995 addresses []instance.Address 996 } 997 998 func (inst *dummyInstance) Id() instance.Id { 999 return inst.id 1000 } 1001 1002 func (inst *dummyInstance) Status() string { 1003 return inst.status 1004 } 1005 1006 // SetInstanceAddresses sets the addresses associated with the given 1007 // dummy instance. 1008 func SetInstanceAddresses(inst instance.Instance, addrs []instance.Address) { 1009 inst0 := inst.(*dummyInstance) 1010 inst0.mu.Lock() 1011 inst0.addresses = append(inst0.addresses[:0], addrs...) 1012 inst0.mu.Unlock() 1013 } 1014 1015 // SetInstanceStatus sets the status associated with the given 1016 // dummy instance. 1017 func SetInstanceStatus(inst instance.Instance, status string) { 1018 inst0 := inst.(*dummyInstance) 1019 inst0.mu.Lock() 1020 inst0.status = status 1021 inst0.mu.Unlock() 1022 } 1023 1024 func (*dummyInstance) Refresh() error { 1025 return nil 1026 } 1027 1028 func (inst *dummyInstance) Addresses() ([]instance.Address, error) { 1029 inst.mu.Lock() 1030 defer inst.mu.Unlock() 1031 return append([]instance.Address{}, inst.addresses...), nil 1032 } 1033 1034 func (inst *dummyInstance) OpenPorts(machineId string, ports []instance.Port) error { 1035 defer delay() 1036 logger.Infof("openPorts %s, %#v", machineId, ports) 1037 if inst.firewallMode != config.FwInstance { 1038 return fmt.Errorf("invalid firewall mode %q for opening ports on instance", 1039 inst.firewallMode) 1040 } 1041 if inst.machineId != machineId { 1042 panic(fmt.Errorf("OpenPorts with mismatched machine id, expected %q got %q", inst.machineId, machineId)) 1043 } 1044 inst.state.mu.Lock() 1045 defer inst.state.mu.Unlock() 1046 inst.state.ops <- OpOpenPorts{ 1047 Env: inst.state.name, 1048 MachineId: machineId, 1049 InstanceId: inst.Id(), 1050 Ports: ports, 1051 } 1052 for _, p := range ports { 1053 inst.ports[p] = true 1054 } 1055 return nil 1056 } 1057 1058 func (inst *dummyInstance) ClosePorts(machineId string, ports []instance.Port) error { 1059 defer delay() 1060 if inst.firewallMode != config.FwInstance { 1061 return fmt.Errorf("invalid firewall mode %q for closing ports on instance", 1062 inst.firewallMode) 1063 } 1064 if inst.machineId != machineId { 1065 panic(fmt.Errorf("ClosePorts with mismatched machine id, expected %s got %s", inst.machineId, machineId)) 1066 } 1067 inst.state.mu.Lock() 1068 defer inst.state.mu.Unlock() 1069 inst.state.ops <- OpClosePorts{ 1070 Env: inst.state.name, 1071 MachineId: machineId, 1072 InstanceId: inst.Id(), 1073 Ports: ports, 1074 } 1075 for _, p := range ports { 1076 delete(inst.ports, p) 1077 } 1078 return nil 1079 } 1080 1081 func (inst *dummyInstance) Ports(machineId string) (ports []instance.Port, err error) { 1082 defer delay() 1083 if inst.firewallMode != config.FwInstance { 1084 return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from instance", 1085 inst.firewallMode) 1086 } 1087 if inst.machineId != machineId { 1088 panic(fmt.Errorf("Ports with mismatched machine id, expected %q got %q", inst.machineId, machineId)) 1089 } 1090 inst.state.mu.Lock() 1091 defer inst.state.mu.Unlock() 1092 for p := range inst.ports { 1093 ports = append(ports, p) 1094 } 1095 instance.SortPorts(ports) 1096 return 1097 } 1098 1099 // providerDelay controls the delay before dummy responds. 1100 // non empty values in JUJU_DUMMY_DELAY will be parsed as 1101 // time.Durations into this value. 1102 var providerDelay time.Duration 1103 1104 // pause execution to simulate the latency of a real provider 1105 func delay() { 1106 if providerDelay > 0 { 1107 logger.Infof("pausing for %v", providerDelay) 1108 <-time.After(providerDelay) 1109 } 1110 }