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