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