github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/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 // when the environment is bootstrapped. 11 // 12 // The configuration data also accepts a "broken" property 13 // of type boolean. If this is non-empty, any operation 14 // after the environment has been opened will return 15 // the error "broken environment", and will also log that. 16 // 17 // The DNS name of instances is the same as the Id, 18 // with ".dns" appended. 19 // 20 // To avoid enumerating all possible series and architectures, 21 // any series or architecture with the prefix "unknown" is 22 // treated as bad when starting a new instance. 23 package dummy 24 25 import ( 26 "fmt" 27 "net" 28 "net/http" 29 "os" 30 "strconv" 31 "strings" 32 "sync" 33 "time" 34 35 "github.com/juju/errors" 36 "github.com/juju/loggo" 37 "github.com/juju/names" 38 "github.com/juju/schema" 39 gitjujutesting "github.com/juju/testing" 40 41 "github.com/juju/juju/agent" 42 "github.com/juju/juju/api" 43 "github.com/juju/juju/apiserver" 44 "github.com/juju/juju/constraints" 45 "github.com/juju/juju/environs" 46 "github.com/juju/juju/environs/cloudinit" 47 "github.com/juju/juju/environs/config" 48 "github.com/juju/juju/instance" 49 "github.com/juju/juju/juju/arch" 50 "github.com/juju/juju/mongo" 51 "github.com/juju/juju/network" 52 "github.com/juju/juju/provider/common" 53 "github.com/juju/juju/state" 54 "github.com/juju/juju/state/multiwatcher" 55 "github.com/juju/juju/testing" 56 coretools "github.com/juju/juju/tools" 57 ) 58 59 var logger = loggo.GetLogger("juju.provider.dummy") 60 61 var transientErrorInjection chan error 62 63 const ( 64 BootstrapInstanceId = instance.Id("localhost") 65 ) 66 67 var ( 68 ErrNotPrepared = errors.New("environment is not prepared") 69 ErrDestroyed = errors.New("environment has been destroyed") 70 ) 71 72 // SampleConfig() returns an environment configuration with all required 73 // attributes set. 74 func SampleConfig() testing.Attrs { 75 return testing.Attrs{ 76 "type": "dummy", 77 "name": "only", 78 "uuid": testing.EnvironmentTag.Id(), 79 "authorized-keys": testing.FakeAuthKeys, 80 "firewall-mode": config.FwInstance, 81 "admin-secret": testing.DefaultMongoPassword, 82 "ca-cert": testing.CACert, 83 "ca-private-key": testing.CAKey, 84 "ssl-hostname-verification": true, 85 "development": false, 86 "state-port": 1234, 87 "api-port": 4321, 88 "syslog-port": 2345, 89 "default-series": config.LatestLtsSeries(), 90 91 "secret": "pork", 92 "state-server": true, 93 "prefer-ipv6": true, 94 } 95 } 96 97 // PatchTransientErrorInjectionChannel sets the transientInjectionError 98 // channel which can be used to inject errors into StartInstance for 99 // testing purposes 100 // The injected errors will use the string received on the channel 101 // and the instance's state will eventually go to error, while the 102 // received string will appear in the info field of the machine's status 103 func PatchTransientErrorInjectionChannel(c chan error) func() { 104 return gitjujutesting.PatchValue(&transientErrorInjection, c) 105 } 106 107 // AdminUserTag returns the user tag used to bootstrap the dummy environment. 108 // The dummy bootstrapping is handled slightly differently, and the user is 109 // created as part of the bootstrap process. This method is used to provide 110 // tests a way to get to the user name that was used to initialise the 111 // database, and as such, is the owner of the initial environment. 112 func AdminUserTag() names.UserTag { 113 return names.NewLocalUserTag("dummy-admin") 114 } 115 116 // stateInfo returns a *state.Info which allows clients to connect to the 117 // shared dummy state, if it exists. If preferIPv6 is true, an IPv6 endpoint 118 // will be added as primary. 119 func stateInfo(preferIPv6 bool) *mongo.MongoInfo { 120 if gitjujutesting.MgoServer.Addr() == "" { 121 panic("dummy environ state tests must be run with MgoTestPackage") 122 } 123 mongoPort := strconv.Itoa(gitjujutesting.MgoServer.Port()) 124 var addrs []string 125 if preferIPv6 { 126 addrs = []string{ 127 net.JoinHostPort("::1", mongoPort), 128 net.JoinHostPort("localhost", mongoPort), 129 } 130 } else { 131 addrs = []string{net.JoinHostPort("localhost", mongoPort)} 132 } 133 return &mongo.MongoInfo{ 134 Info: mongo.Info{ 135 Addrs: addrs, 136 CACert: testing.CACert, 137 }, 138 } 139 } 140 141 // Operation represents an action on the dummy provider. 142 type Operation interface{} 143 144 type OpBootstrap struct { 145 Context environs.BootstrapContext 146 Env string 147 Args environs.BootstrapParams 148 } 149 150 type OpFinalizeBootstrap struct { 151 Context environs.BootstrapContext 152 Env string 153 MachineConfig *cloudinit.MachineConfig 154 } 155 156 type OpDestroy struct { 157 Env string 158 Error error 159 } 160 161 type OpAllocateAddress struct { 162 Env string 163 InstanceId instance.Id 164 SubnetId network.Id 165 Address network.Address 166 } 167 168 type OpReleaseAddress struct { 169 Env string 170 InstanceId instance.Id 171 SubnetId network.Id 172 Address network.Address 173 } 174 175 type OpNetworkInterfaces struct { 176 Env string 177 InstanceId instance.Id 178 Info []network.InterfaceInfo 179 } 180 181 // TODO(dimitern) Rename this to OpSubnets and add InstanceId field. 182 type OpListNetworks struct { 183 Env string 184 Info []network.SubnetInfo 185 } 186 187 type OpStartInstance struct { 188 Env string 189 MachineId string 190 MachineNonce string 191 PossibleTools coretools.List 192 Instance instance.Instance 193 Constraints constraints.Value 194 Networks []string 195 NetworkInfo []network.InterfaceInfo 196 Info *mongo.MongoInfo 197 Jobs []multiwatcher.MachineJob 198 APIInfo *api.Info 199 Secret string 200 AgentEnvironment map[string]string 201 } 202 203 type OpStopInstances struct { 204 Env string 205 Ids []instance.Id 206 } 207 208 type OpOpenPorts struct { 209 Env string 210 MachineId string 211 InstanceId instance.Id 212 Ports []network.PortRange 213 } 214 215 type OpClosePorts struct { 216 Env string 217 MachineId string 218 InstanceId instance.Id 219 Ports []network.PortRange 220 } 221 222 type OpPutFile struct { 223 Env string 224 FileName string 225 } 226 227 // environProvider represents the dummy provider. There is only ever one 228 // instance of this type (providerInstance) 229 type environProvider struct { 230 mu sync.Mutex 231 ops chan<- Operation 232 statePolicy state.Policy 233 // We have one state for each environment name 234 state map[int]*environState 235 maxStateId int 236 } 237 238 var providerInstance environProvider 239 240 const noStateId = 0 241 242 // environState represents the state of an environment. 243 // It can be shared between several environ values, 244 // so that a given environment can be opened several times. 245 type environState struct { 246 id int 247 name string 248 ops chan<- Operation 249 statePolicy state.Policy 250 mu sync.Mutex 251 maxId int // maximum instance id allocated so far. 252 maxAddr int // maximum allocated address last byte 253 insts map[instance.Id]*dummyInstance 254 globalPorts map[network.PortRange]bool 255 bootstrapped bool 256 storageDelay time.Duration 257 storage *storageServer 258 apiListener net.Listener 259 httpListener net.Listener 260 apiServer *apiserver.Server 261 apiState *state.State 262 preferIPv6 bool 263 } 264 265 // environ represents a client's connection to a given environment's 266 // state. 267 type environ struct { 268 common.SupportsUnitPlacementPolicy 269 270 name string 271 ecfgMutex sync.Mutex 272 ecfgUnlocked *environConfig 273 } 274 275 var _ environs.Environ = (*environ)(nil) 276 277 // discardOperations discards all Operations written to it. 278 var discardOperations chan<- Operation 279 280 func init() { 281 environs.RegisterProvider("dummy", &providerInstance) 282 283 // Prime the first ops channel, so that naive clients can use 284 // the testing environment by simply importing it. 285 c := make(chan Operation) 286 go func() { 287 for _ = range c { 288 } 289 }() 290 discardOperations = c 291 Reset() 292 293 // parse errors are ignored 294 providerDelay, _ = time.ParseDuration(os.Getenv("JUJU_DUMMY_DELAY")) 295 } 296 297 // Reset resets the entire dummy environment and forgets any registered 298 // operation listener. All opened environments after Reset will share 299 // the same underlying state. 300 func Reset() { 301 logger.Infof("reset environment") 302 p := &providerInstance 303 p.mu.Lock() 304 defer p.mu.Unlock() 305 providerInstance.ops = discardOperations 306 for _, s := range p.state { 307 s.httpListener.Close() 308 if s.apiListener != nil { 309 s.apiListener.Close() 310 } 311 s.destroy() 312 } 313 providerInstance.state = make(map[int]*environState) 314 if mongoAlive() { 315 gitjujutesting.MgoServer.Reset() 316 } 317 providerInstance.statePolicy = environs.NewStatePolicy() 318 } 319 320 func (state *environState) destroy() { 321 state.storage.files = make(map[string][]byte) 322 if !state.bootstrapped { 323 return 324 } 325 if state.apiServer != nil { 326 if err := state.apiServer.Stop(); err != nil && mongoAlive() { 327 panic(err) 328 } 329 state.apiServer = nil 330 if err := state.apiState.Close(); err != nil && mongoAlive() { 331 panic(err) 332 } 333 state.apiState = nil 334 } 335 if mongoAlive() { 336 gitjujutesting.MgoServer.Reset() 337 } 338 state.bootstrapped = false 339 } 340 341 // mongoAlive reports whether the mongo server is 342 // still alive (i.e. has not been deliberately destroyed). 343 // If it has been deliberately destroyed, we will 344 // expect some errors when closing things down. 345 func mongoAlive() bool { 346 return gitjujutesting.MgoServer.Addr() != "" 347 } 348 349 // GetStateInAPIServer returns the state connection used by the API server 350 // This is so code in the test suite can trigger Syncs, etc that the API server 351 // will see, which will then trigger API watchers, etc. 352 func (e *environ) GetStateInAPIServer() *state.State { 353 st, err := e.state() 354 if err != nil { 355 panic(err) 356 } 357 return st.apiState 358 } 359 360 // newState creates the state for a new environment with the 361 // given name and starts an http server listening for 362 // storage requests. 363 func newState(name string, ops chan<- Operation, policy state.Policy) *environState { 364 s := &environState{ 365 name: name, 366 ops: ops, 367 statePolicy: policy, 368 insts: make(map[instance.Id]*dummyInstance), 369 globalPorts: make(map[network.PortRange]bool), 370 } 371 s.storage = newStorageServer(s, "/"+name+"/private") 372 s.listenStorage() 373 return s 374 } 375 376 // listenStorage starts a network listener listening for http 377 // requests to retrieve files in the state's storage. 378 func (s *environState) listenStorage() { 379 l, err := net.Listen("tcp", ":0") 380 if err != nil { 381 panic(fmt.Errorf("cannot start listener: %v", err)) 382 } 383 s.httpListener = l 384 mux := http.NewServeMux() 385 mux.Handle(s.storage.path+"/", http.StripPrefix(s.storage.path+"/", s.storage)) 386 go http.Serve(l, mux) 387 } 388 389 // listenAPI starts a network listener listening for API 390 // connections and proxies them to the API server port. 391 func (s *environState) listenAPI() int { 392 l, err := net.Listen("tcp", ":0") 393 if err != nil { 394 panic(fmt.Errorf("cannot start listener: %v", err)) 395 } 396 s.apiListener = l 397 return l.Addr().(*net.TCPAddr).Port 398 } 399 400 // SetStatePolicy sets the state.Policy to use when a 401 // state server is initialised by dummy. 402 func SetStatePolicy(policy state.Policy) { 403 p := &providerInstance 404 p.mu.Lock() 405 defer p.mu.Unlock() 406 p.statePolicy = policy 407 } 408 409 // Listen closes the previously registered listener (if any). 410 // Subsequent operations on any dummy environment can be received on c 411 // (if not nil). 412 func Listen(c chan<- Operation) { 413 p := &providerInstance 414 p.mu.Lock() 415 defer p.mu.Unlock() 416 if c == nil { 417 c = discardOperations 418 } 419 if p.ops != discardOperations { 420 close(p.ops) 421 } 422 p.ops = c 423 for _, st := range p.state { 424 st.mu.Lock() 425 st.ops = c 426 st.mu.Unlock() 427 } 428 } 429 430 // SetStorageDelay causes any storage download operation in any current 431 // environment to be delayed for the given duration. 432 func SetStorageDelay(d time.Duration) { 433 p := &providerInstance 434 p.mu.Lock() 435 defer p.mu.Unlock() 436 for _, st := range p.state { 437 st.mu.Lock() 438 st.storageDelay = d 439 st.mu.Unlock() 440 } 441 } 442 443 var configFields = schema.Fields{ 444 "state-server": schema.Bool(), 445 "broken": schema.String(), 446 "secret": schema.String(), 447 "state-id": schema.String(), 448 } 449 var configDefaults = schema.Defaults{ 450 "broken": "", 451 "secret": "pork", 452 "state-id": schema.Omit, 453 } 454 455 type environConfig struct { 456 *config.Config 457 attrs map[string]interface{} 458 } 459 460 func (c *environConfig) stateServer() bool { 461 return c.attrs["state-server"].(bool) 462 } 463 464 func (c *environConfig) broken() string { 465 return c.attrs["broken"].(string) 466 } 467 468 func (c *environConfig) secret() string { 469 return c.attrs["secret"].(string) 470 } 471 472 func (c *environConfig) stateId() int { 473 idStr, ok := c.attrs["state-id"].(string) 474 if !ok { 475 return noStateId 476 } 477 id, err := strconv.Atoi(idStr) 478 if err != nil { 479 panic(fmt.Errorf("unexpected state-id %q (should have pre-checked)", idStr)) 480 } 481 return id 482 } 483 484 func (p *environProvider) newConfig(cfg *config.Config) (*environConfig, error) { 485 valid, err := p.Validate(cfg, nil) 486 if err != nil { 487 return nil, err 488 } 489 return &environConfig{valid, valid.UnknownAttrs()}, nil 490 } 491 492 func (p *environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) { 493 // Check for valid changes for the base config values. 494 if err := config.Validate(cfg, old); err != nil { 495 return nil, err 496 } 497 validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) 498 if err != nil { 499 return nil, err 500 } 501 if idStr, ok := validated["state-id"].(string); ok { 502 if _, err := strconv.Atoi(idStr); err != nil { 503 return nil, fmt.Errorf("invalid state-id %q", idStr) 504 } 505 } 506 // Apply the coerced unknown values back into the config. 507 return cfg.Apply(validated) 508 } 509 510 func (e *environ) state() (*environState, error) { 511 stateId := e.ecfg().stateId() 512 if stateId == noStateId { 513 return nil, ErrNotPrepared 514 } 515 p := &providerInstance 516 p.mu.Lock() 517 defer p.mu.Unlock() 518 if state := p.state[stateId]; state != nil { 519 return state, nil 520 } 521 return nil, ErrDestroyed 522 } 523 524 func (p *environProvider) Open(cfg *config.Config) (environs.Environ, error) { 525 p.mu.Lock() 526 defer p.mu.Unlock() 527 ecfg, err := p.newConfig(cfg) 528 if err != nil { 529 return nil, err 530 } 531 if ecfg.stateId() == noStateId { 532 return nil, ErrNotPrepared 533 } 534 env := &environ{ 535 name: ecfg.Name(), 536 ecfgUnlocked: ecfg, 537 } 538 if err := env.checkBroken("Open"); err != nil { 539 return nil, err 540 } 541 return env, nil 542 } 543 544 // RestrictedConfigAttributes is specified in the EnvironProvider interface. 545 func (p *environProvider) RestrictedConfigAttributes() []string { 546 return nil 547 } 548 549 // PrepareForCreateEnvironment is specified in the EnvironProvider interface. 550 func (p *environProvider) PrepareForCreateEnvironment(cfg *config.Config) (*config.Config, error) { 551 return p.prepare(cfg) 552 } 553 554 func (p *environProvider) PrepareForBootstrap(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) { 555 cfg, err := p.prepare(cfg) 556 if err != nil { 557 return nil, err 558 } 559 return p.Open(cfg) 560 } 561 562 // prepare is the internal version of Prepare - it prepares the 563 // environment but does not open it. 564 func (p *environProvider) prepare(cfg *config.Config) (*config.Config, error) { 565 ecfg, err := p.newConfig(cfg) 566 if err != nil { 567 return nil, err 568 } 569 p.mu.Lock() 570 defer p.mu.Unlock() 571 name := cfg.Name() 572 if ecfg.stateId() != noStateId { 573 return cfg, nil 574 } 575 if ecfg.stateServer() && len(p.state) != 0 { 576 for _, old := range p.state { 577 panic(fmt.Errorf("cannot share a state between two dummy environs; old %q; new %q", old.name, name)) 578 } 579 } 580 // The environment has not been prepared, 581 // so create it and set its state identifier accordingly. 582 state := newState(name, p.ops, p.statePolicy) 583 p.maxStateId++ 584 state.id = p.maxStateId 585 p.state[state.id] = state 586 587 attrs := map[string]interface{}{"state-id": fmt.Sprint(state.id)} 588 if ecfg.stateServer() { 589 attrs["api-port"] = state.listenAPI() 590 } 591 return cfg.Apply(attrs) 592 } 593 594 func (*environProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) { 595 ecfg, err := providerInstance.newConfig(cfg) 596 if err != nil { 597 return nil, err 598 } 599 return map[string]string{ 600 "secret": ecfg.secret(), 601 }, nil 602 } 603 604 func (*environProvider) BoilerplateConfig() string { 605 return ` 606 # Fake configuration for dummy provider. 607 dummy: 608 type: dummy 609 610 `[1:] 611 } 612 613 var errBroken = errors.New("broken environment") 614 615 // Override for testing - the data directory with which the state api server is initialised. 616 var DataDir = "" 617 var LogDir = "" 618 619 func (e *environ) ecfg() *environConfig { 620 e.ecfgMutex.Lock() 621 ecfg := e.ecfgUnlocked 622 e.ecfgMutex.Unlock() 623 return ecfg 624 } 625 626 func (e *environ) checkBroken(method string) error { 627 for _, m := range strings.Fields(e.ecfg().broken()) { 628 if m == method { 629 return fmt.Errorf("dummy.%s is broken", method) 630 } 631 } 632 return nil 633 } 634 635 // SupportedArchitectures is specified on the EnvironCapability interface. 636 func (*environ) SupportedArchitectures() ([]string, error) { 637 return []string{arch.AMD64, arch.I386, arch.PPC64EL}, nil 638 } 639 640 // PrecheckInstance is specified in the state.Prechecker interface. 641 func (*environ) PrecheckInstance(series string, cons constraints.Value, placement string) error { 642 if placement != "" && placement != "valid" { 643 return fmt.Errorf("%s placement is invalid", placement) 644 } 645 return nil 646 } 647 648 func (e *environ) Bootstrap(ctx environs.BootstrapContext, args environs.BootstrapParams) (arch, series string, _ environs.BootstrapFinalizer, _ error) { 649 series = config.PreferredSeries(e.Config()) 650 availableTools, err := args.AvailableTools.Match(coretools.Filter{Series: series}) 651 if err != nil { 652 return "", "", nil, err 653 } 654 arch = availableTools.Arches()[0] 655 656 defer delay() 657 if err := e.checkBroken("Bootstrap"); err != nil { 658 return "", "", nil, err 659 } 660 network.InitializeFromConfig(e.Config()) 661 password := e.Config().AdminSecret() 662 if password == "" { 663 return "", "", nil, fmt.Errorf("admin-secret is required for bootstrap") 664 } 665 if _, ok := e.Config().CACert(); !ok { 666 return "", "", nil, fmt.Errorf("no CA certificate in environment configuration") 667 } 668 669 logger.Infof("would pick tools from %s", availableTools) 670 cfg, err := environs.BootstrapConfig(e.Config()) 671 if err != nil { 672 return "", "", nil, fmt.Errorf("cannot make bootstrap config: %v", err) 673 } 674 675 estate, err := e.state() 676 if err != nil { 677 return "", "", nil, err 678 } 679 estate.mu.Lock() 680 defer estate.mu.Unlock() 681 if estate.bootstrapped { 682 return "", "", nil, fmt.Errorf("environment is already bootstrapped") 683 } 684 estate.preferIPv6 = e.Config().PreferIPv6() 685 686 // Create an instance for the bootstrap node. 687 logger.Infof("creating bootstrap instance") 688 i := &dummyInstance{ 689 id: BootstrapInstanceId, 690 addresses: network.NewAddresses("localhost"), 691 ports: make(map[network.PortRange]bool), 692 machineId: agent.BootstrapMachineId, 693 series: series, 694 firewallMode: e.Config().FirewallMode(), 695 state: estate, 696 stateServer: true, 697 } 698 estate.insts[i.id] = i 699 700 if e.ecfg().stateServer() { 701 // TODO(rog) factor out relevant code from cmd/jujud/bootstrap.go 702 // so that we can call it here. 703 704 info := stateInfo(estate.preferIPv6) 705 // Since the admin user isn't setup until after here, 706 // the password in the info structure is empty, so the admin 707 // user is constructed with an empty password here. 708 // It is set just below. 709 st, err := state.Initialize( 710 AdminUserTag(), info, cfg, 711 mongo.DefaultDialOpts(), estate.statePolicy) 712 if err != nil { 713 panic(err) 714 } 715 if err := st.SetEnvironConstraints(args.Constraints); err != nil { 716 panic(err) 717 } 718 if err := st.SetAdminMongoPassword(password); err != nil { 719 panic(err) 720 } 721 if err := st.MongoSession().DB("admin").Login("admin", password); err != nil { 722 panic(err) 723 } 724 env, err := st.Environment() 725 if err != nil { 726 panic(err) 727 } 728 owner, err := st.User(env.Owner()) 729 if err != nil { 730 panic(err) 731 } 732 // We log this out for test purposes only. No one in real life can use 733 // a dummy provider for anything other than testing, so logging the password 734 // here is fine. 735 logger.Debugf("setting password for %q to %q", owner.Name(), password) 736 owner.SetPassword(password) 737 738 estate.apiServer, err = apiserver.NewServer(st, estate.apiListener, apiserver.ServerConfig{ 739 Cert: []byte(testing.ServerCert), 740 Key: []byte(testing.ServerKey), 741 Tag: names.NewMachineTag("0"), 742 DataDir: DataDir, 743 LogDir: LogDir, 744 }) 745 if err != nil { 746 panic(err) 747 } 748 estate.apiState = st 749 } 750 estate.bootstrapped = true 751 estate.ops <- OpBootstrap{Context: ctx, Env: e.name, Args: args} 752 finalize := func(ctx environs.BootstrapContext, mcfg *cloudinit.MachineConfig) error { 753 estate.ops <- OpFinalizeBootstrap{Context: ctx, Env: e.name, MachineConfig: mcfg} 754 return nil 755 } 756 return arch, series, finalize, nil 757 } 758 759 func (e *environ) StateServerInstances() ([]instance.Id, error) { 760 estate, err := e.state() 761 if err != nil { 762 return nil, err 763 } 764 estate.mu.Lock() 765 defer estate.mu.Unlock() 766 if err := e.checkBroken("StateServerInstances"); err != nil { 767 return nil, err 768 } 769 if !estate.bootstrapped { 770 return nil, environs.ErrNotBootstrapped 771 } 772 var stateServerInstances []instance.Id 773 for _, v := range estate.insts { 774 if v.stateServer { 775 stateServerInstances = append(stateServerInstances, v.Id()) 776 } 777 } 778 return stateServerInstances, nil 779 } 780 781 func (e *environ) Config() *config.Config { 782 return e.ecfg().Config 783 } 784 785 func (e *environ) SetConfig(cfg *config.Config) error { 786 if err := e.checkBroken("SetConfig"); err != nil { 787 return err 788 } 789 ecfg, err := providerInstance.newConfig(cfg) 790 if err != nil { 791 return err 792 } 793 e.ecfgMutex.Lock() 794 e.ecfgUnlocked = ecfg 795 e.ecfgMutex.Unlock() 796 return nil 797 } 798 799 func (e *environ) Destroy() (res error) { 800 defer delay() 801 estate, err := e.state() 802 if err != nil { 803 if err == ErrDestroyed { 804 return nil 805 } 806 return err 807 } 808 defer func() { estate.ops <- OpDestroy{Env: estate.name, Error: res} }() 809 if err := e.checkBroken("Destroy"); err != nil { 810 return err 811 } 812 p := &providerInstance 813 p.mu.Lock() 814 delete(p.state, estate.id) 815 p.mu.Unlock() 816 817 estate.mu.Lock() 818 defer estate.mu.Unlock() 819 estate.destroy() 820 return nil 821 } 822 823 // ConstraintsValidator is defined on the Environs interface. 824 func (e *environ) ConstraintsValidator() (constraints.Validator, error) { 825 validator := constraints.NewValidator() 826 validator.RegisterUnsupported([]string{constraints.CpuPower}) 827 validator.RegisterConflicts([]string{constraints.InstanceType}, []string{constraints.Mem}) 828 return validator, nil 829 } 830 831 // StartInstance is specified in the InstanceBroker interface. 832 func (e *environ) StartInstance(args environs.StartInstanceParams) (*environs.StartInstanceResult, error) { 833 834 defer delay() 835 machineId := args.MachineConfig.MachineId 836 logger.Infof("dummy startinstance, machine %s", machineId) 837 if err := e.checkBroken("StartInstance"); err != nil { 838 return nil, err 839 } 840 estate, err := e.state() 841 if err != nil { 842 return nil, err 843 } 844 estate.mu.Lock() 845 defer estate.mu.Unlock() 846 847 // check if an error has been injected on the transientErrorInjection channel (testing purposes) 848 select { 849 case injectedError := <-transientErrorInjection: 850 return nil, injectedError 851 default: 852 } 853 854 if args.MachineConfig.MachineNonce == "" { 855 return nil, errors.New("cannot start instance: missing machine nonce") 856 } 857 if _, ok := e.Config().CACert(); !ok { 858 return nil, errors.New("no CA certificate in environment configuration") 859 } 860 if args.MachineConfig.MongoInfo.Tag != names.NewMachineTag(machineId) { 861 return nil, errors.New("entity tag must match started machine") 862 } 863 if args.MachineConfig.APIInfo.Tag != names.NewMachineTag(machineId) { 864 return nil, errors.New("entity tag must match started machine") 865 } 866 logger.Infof("would pick tools from %s", args.Tools) 867 series := args.Tools.OneSeries() 868 869 idString := fmt.Sprintf("%s-%d", e.name, estate.maxId) 870 addrs := network.NewAddresses(idString+".dns", "127.0.0.1") 871 if estate.preferIPv6 { 872 addrs = append(addrs, network.NewAddress(fmt.Sprintf("fc00::%x", estate.maxId+1), network.ScopeUnknown)) 873 } 874 logger.Debugf("StartInstance addresses: %v", addrs) 875 i := &dummyInstance{ 876 id: instance.Id(idString), 877 addresses: addrs, 878 ports: make(map[network.PortRange]bool), 879 machineId: machineId, 880 series: series, 881 firewallMode: e.Config().FirewallMode(), 882 state: estate, 883 } 884 885 var hc *instance.HardwareCharacteristics 886 // To match current system capability, only provide hardware characteristics for 887 // environ machines, not containers. 888 if state.ParentId(machineId) == "" { 889 // We will just assume the instance hardware characteristics exactly matches 890 // the supplied constraints (if specified). 891 hc = &instance.HardwareCharacteristics{ 892 Arch: args.Constraints.Arch, 893 Mem: args.Constraints.Mem, 894 RootDisk: args.Constraints.RootDisk, 895 CpuCores: args.Constraints.CpuCores, 896 CpuPower: args.Constraints.CpuPower, 897 Tags: args.Constraints.Tags, 898 } 899 // Fill in some expected instance hardware characteristics if constraints not specified. 900 if hc.Arch == nil { 901 arch := "amd64" 902 hc.Arch = &arch 903 } 904 if hc.Mem == nil { 905 mem := uint64(1024) 906 hc.Mem = &mem 907 } 908 if hc.RootDisk == nil { 909 disk := uint64(8192) 910 hc.RootDisk = &disk 911 } 912 if hc.CpuCores == nil { 913 cores := uint64(1) 914 hc.CpuCores = &cores 915 } 916 } 917 // Simulate networks added when requested. 918 networks := append(args.Constraints.IncludeNetworks(), args.MachineConfig.Networks...) 919 networkInfo := make([]network.InterfaceInfo, len(networks)) 920 for i, netName := range networks { 921 if strings.HasPrefix(netName, "bad-") { 922 // Simulate we didn't get correct information for the network. 923 networkInfo[i] = network.InterfaceInfo{ 924 ProviderId: network.Id(netName), 925 NetworkName: netName, 926 CIDR: "invalid", 927 } 928 } else { 929 networkInfo[i] = network.InterfaceInfo{ 930 ProviderId: network.Id(netName), 931 NetworkName: netName, 932 CIDR: fmt.Sprintf("0.%d.2.0/24", i+1), 933 InterfaceName: fmt.Sprintf("eth%d", i), 934 VLANTag: i, 935 MACAddress: fmt.Sprintf("aa:bb:cc:dd:ee:f%d", i), 936 } 937 } 938 // TODO(dimitern) Add the rest of the network.InterfaceInfo 939 // fields when we can use them. 940 } 941 estate.insts[i.id] = i 942 estate.maxId++ 943 estate.ops <- OpStartInstance{ 944 Env: e.name, 945 MachineId: machineId, 946 MachineNonce: args.MachineConfig.MachineNonce, 947 PossibleTools: args.Tools, 948 Constraints: args.Constraints, 949 Networks: args.MachineConfig.Networks, 950 NetworkInfo: networkInfo, 951 Instance: i, 952 Jobs: args.MachineConfig.Jobs, 953 Info: args.MachineConfig.MongoInfo, 954 APIInfo: args.MachineConfig.APIInfo, 955 AgentEnvironment: args.MachineConfig.AgentEnvironment, 956 Secret: e.ecfg().secret(), 957 } 958 return &environs.StartInstanceResult{ 959 Instance: i, 960 Hardware: hc, 961 NetworkInfo: networkInfo, 962 }, nil 963 } 964 965 func (e *environ) StopInstances(ids ...instance.Id) error { 966 defer delay() 967 if err := e.checkBroken("StopInstance"); err != nil { 968 return err 969 } 970 estate, err := e.state() 971 if err != nil { 972 return err 973 } 974 estate.mu.Lock() 975 defer estate.mu.Unlock() 976 for _, id := range ids { 977 delete(estate.insts, id) 978 } 979 estate.ops <- OpStopInstances{ 980 Env: e.name, 981 Ids: ids, 982 } 983 return nil 984 } 985 986 func (e *environ) Instances(ids []instance.Id) (insts []instance.Instance, err error) { 987 defer delay() 988 if err := e.checkBroken("Instances"); err != nil { 989 return nil, err 990 } 991 if len(ids) == 0 { 992 return nil, nil 993 } 994 estate, err := e.state() 995 if err != nil { 996 return nil, err 997 } 998 estate.mu.Lock() 999 defer estate.mu.Unlock() 1000 notFound := 0 1001 for _, id := range ids { 1002 inst := estate.insts[id] 1003 if inst == nil { 1004 err = environs.ErrPartialInstances 1005 notFound++ 1006 insts = append(insts, nil) 1007 } else { 1008 insts = append(insts, inst) 1009 } 1010 } 1011 if notFound == len(ids) { 1012 return nil, environs.ErrNoInstances 1013 } 1014 return 1015 } 1016 1017 // SupportsAddressAllocation is specified on environs.Networking. 1018 func (env *environ) SupportsAddressAllocation(subnetId network.Id) (bool, error) { 1019 return true, nil 1020 } 1021 1022 // AllocateAddress requests an address to be allocated for the 1023 // given instance on the given subnet. 1024 func (env *environ) AllocateAddress(instId instance.Id, subnetId network.Id, addr network.Address) error { 1025 if err := env.checkBroken("AllocateAddress"); err != nil { 1026 return err 1027 } 1028 1029 estate, err := env.state() 1030 if err != nil { 1031 return err 1032 } 1033 estate.mu.Lock() 1034 defer estate.mu.Unlock() 1035 estate.maxAddr++ 1036 estate.ops <- OpAllocateAddress{ 1037 Env: env.name, 1038 InstanceId: instId, 1039 SubnetId: subnetId, 1040 Address: addr, 1041 } 1042 return nil 1043 } 1044 1045 // ReleaseAddress releases a specific address previously allocated with 1046 // AllocateAddress. 1047 func (env *environ) ReleaseAddress(instId instance.Id, subnetId network.Id, addr network.Address) error { 1048 if err := env.checkBroken("ReleaseAddress"); err != nil { 1049 return err 1050 } 1051 1052 estate, err := env.state() 1053 if err != nil { 1054 return err 1055 } 1056 estate.mu.Lock() 1057 defer estate.mu.Unlock() 1058 estate.maxAddr-- 1059 estate.ops <- OpReleaseAddress{ 1060 Env: env.name, 1061 InstanceId: instId, 1062 SubnetId: subnetId, 1063 Address: addr, 1064 } 1065 return nil 1066 } 1067 1068 // NetworkInterfaces implements Environ.NetworkInterfaces(). 1069 func (env *environ) NetworkInterfaces(instId instance.Id) ([]network.InterfaceInfo, error) { 1070 if err := env.checkBroken("NetworkInterfaces"); err != nil { 1071 return nil, err 1072 } 1073 1074 estate, err := env.state() 1075 if err != nil { 1076 return nil, err 1077 } 1078 estate.mu.Lock() 1079 defer estate.mu.Unlock() 1080 1081 // Simulate 2 NICs - primary enabled, secondary disabled with VLAN 1082 // tag 1; both configured using DHCP and having fake DNS servers 1083 // and gateway. 1084 info := make([]network.InterfaceInfo, 2) 1085 for i, netName := range []string{"private", "public"} { 1086 info[i] = network.InterfaceInfo{ 1087 ProviderId: network.Id("dummy-" + netName), 1088 NetworkName: "juju-" + netName, 1089 CIDR: fmt.Sprintf("0.%d.2.0/24", i+1), 1090 InterfaceName: fmt.Sprintf("eth%d", i), 1091 VLANTag: i, 1092 MACAddress: fmt.Sprintf("aa:bb:cc:dd:ee:f%d", i), 1093 Disabled: i%2 != 0, 1094 NoAutoStart: i%2 != 0, 1095 ConfigType: network.ConfigDHCP, 1096 Address: network.NewAddress( 1097 fmt.Sprintf("0.%d.2.%d", i+1, estate.maxAddr+1), 1098 network.ScopeUnknown, 1099 ), 1100 DNSServers: network.NewAddresses("ns1.dummy", "ns2.dummy"), 1101 GatewayAddress: network.NewAddress( 1102 fmt.Sprintf("0.%d.2.1", i+1), 1103 network.ScopeUnknown, 1104 ), 1105 } 1106 } 1107 1108 estate.ops <- OpNetworkInterfaces{ 1109 Env: env.name, 1110 InstanceId: instId, 1111 Info: info, 1112 } 1113 return info, nil 1114 } 1115 1116 // Subnets implements environs.Environ.Subnets. 1117 func (env *environ) Subnets(_ instance.Id, _ []network.Id) ([]network.SubnetInfo, error) { 1118 if err := env.checkBroken("Subnets"); err != nil { 1119 return nil, err 1120 } 1121 1122 estate, err := env.state() 1123 if err != nil { 1124 return nil, err 1125 } 1126 estate.mu.Lock() 1127 defer estate.mu.Unlock() 1128 1129 // TODO(dimitern) Populate AllocatableIPLow/High below. 1130 netInfo := []network.SubnetInfo{ 1131 {CIDR: "0.10.0.0/8", ProviderId: "dummy-private"}, 1132 {CIDR: "0.20.0.0/24", ProviderId: "dummy-public"}, 1133 } 1134 estate.ops <- OpListNetworks{ 1135 Env: env.name, 1136 Info: netInfo, 1137 } 1138 return netInfo, nil 1139 } 1140 1141 func (e *environ) AllInstances() ([]instance.Instance, error) { 1142 defer delay() 1143 if err := e.checkBroken("AllInstances"); err != nil { 1144 return nil, err 1145 } 1146 var insts []instance.Instance 1147 estate, err := e.state() 1148 if err != nil { 1149 return nil, err 1150 } 1151 estate.mu.Lock() 1152 defer estate.mu.Unlock() 1153 for _, v := range estate.insts { 1154 insts = append(insts, v) 1155 } 1156 return insts, nil 1157 } 1158 1159 func (e *environ) OpenPorts(ports []network.PortRange) error { 1160 if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal { 1161 return fmt.Errorf("invalid firewall mode %q for opening ports on environment", mode) 1162 } 1163 estate, err := e.state() 1164 if err != nil { 1165 return err 1166 } 1167 estate.mu.Lock() 1168 defer estate.mu.Unlock() 1169 for _, p := range ports { 1170 estate.globalPorts[p] = true 1171 } 1172 return nil 1173 } 1174 1175 func (e *environ) ClosePorts(ports []network.PortRange) error { 1176 if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal { 1177 return fmt.Errorf("invalid firewall mode %q for closing ports on environment", mode) 1178 } 1179 estate, err := e.state() 1180 if err != nil { 1181 return err 1182 } 1183 estate.mu.Lock() 1184 defer estate.mu.Unlock() 1185 for _, p := range ports { 1186 delete(estate.globalPorts, p) 1187 } 1188 return nil 1189 } 1190 1191 func (e *environ) Ports() (ports []network.PortRange, err error) { 1192 if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal { 1193 return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from environment", mode) 1194 } 1195 estate, err := e.state() 1196 if err != nil { 1197 return nil, err 1198 } 1199 estate.mu.Lock() 1200 defer estate.mu.Unlock() 1201 for p := range estate.globalPorts { 1202 ports = append(ports, p) 1203 } 1204 network.SortPortRanges(ports) 1205 return 1206 } 1207 1208 func (*environ) Provider() environs.EnvironProvider { 1209 return &providerInstance 1210 } 1211 1212 type dummyInstance struct { 1213 state *environState 1214 ports map[network.PortRange]bool 1215 id instance.Id 1216 status string 1217 machineId string 1218 series string 1219 firewallMode string 1220 stateServer bool 1221 1222 mu sync.Mutex 1223 addresses []network.Address 1224 } 1225 1226 func (inst *dummyInstance) Id() instance.Id { 1227 return inst.id 1228 } 1229 1230 func (inst *dummyInstance) Status() string { 1231 return inst.status 1232 } 1233 1234 // SetInstanceAddresses sets the addresses associated with the given 1235 // dummy instance. 1236 func SetInstanceAddresses(inst instance.Instance, addrs []network.Address) { 1237 inst0 := inst.(*dummyInstance) 1238 inst0.mu.Lock() 1239 inst0.addresses = append(inst0.addresses[:0], addrs...) 1240 logger.Debugf("setting instance %q addresses to %v", inst0.Id(), addrs) 1241 inst0.mu.Unlock() 1242 } 1243 1244 // SetInstanceStatus sets the status associated with the given 1245 // dummy instance. 1246 func SetInstanceStatus(inst instance.Instance, status string) { 1247 inst0 := inst.(*dummyInstance) 1248 inst0.mu.Lock() 1249 inst0.status = status 1250 inst0.mu.Unlock() 1251 } 1252 1253 func (*dummyInstance) Refresh() error { 1254 return nil 1255 } 1256 1257 func (inst *dummyInstance) Addresses() ([]network.Address, error) { 1258 inst.mu.Lock() 1259 defer inst.mu.Unlock() 1260 return append([]network.Address{}, inst.addresses...), nil 1261 } 1262 1263 func (inst *dummyInstance) OpenPorts(machineId string, ports []network.PortRange) error { 1264 defer delay() 1265 logger.Infof("openPorts %s, %#v", machineId, ports) 1266 if inst.firewallMode != config.FwInstance { 1267 return fmt.Errorf("invalid firewall mode %q for opening ports on instance", 1268 inst.firewallMode) 1269 } 1270 if inst.machineId != machineId { 1271 panic(fmt.Errorf("OpenPorts with mismatched machine id, expected %q got %q", inst.machineId, machineId)) 1272 } 1273 inst.state.mu.Lock() 1274 defer inst.state.mu.Unlock() 1275 inst.state.ops <- OpOpenPorts{ 1276 Env: inst.state.name, 1277 MachineId: machineId, 1278 InstanceId: inst.Id(), 1279 Ports: ports, 1280 } 1281 for _, p := range ports { 1282 inst.ports[p] = true 1283 } 1284 return nil 1285 } 1286 1287 func (inst *dummyInstance) ClosePorts(machineId string, ports []network.PortRange) error { 1288 defer delay() 1289 if inst.firewallMode != config.FwInstance { 1290 return fmt.Errorf("invalid firewall mode %q for closing ports on instance", 1291 inst.firewallMode) 1292 } 1293 if inst.machineId != machineId { 1294 panic(fmt.Errorf("ClosePorts with mismatched machine id, expected %s got %s", inst.machineId, machineId)) 1295 } 1296 inst.state.mu.Lock() 1297 defer inst.state.mu.Unlock() 1298 inst.state.ops <- OpClosePorts{ 1299 Env: inst.state.name, 1300 MachineId: machineId, 1301 InstanceId: inst.Id(), 1302 Ports: ports, 1303 } 1304 for _, p := range ports { 1305 delete(inst.ports, p) 1306 } 1307 return nil 1308 } 1309 1310 func (inst *dummyInstance) Ports(machineId string) (ports []network.PortRange, err error) { 1311 defer delay() 1312 if inst.firewallMode != config.FwInstance { 1313 return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from instance", 1314 inst.firewallMode) 1315 } 1316 if inst.machineId != machineId { 1317 panic(fmt.Errorf("Ports with mismatched machine id, expected %q got %q", inst.machineId, machineId)) 1318 } 1319 inst.state.mu.Lock() 1320 defer inst.state.mu.Unlock() 1321 for p := range inst.ports { 1322 ports = append(ports, p) 1323 } 1324 network.SortPortRanges(ports) 1325 return 1326 } 1327 1328 // providerDelay controls the delay before dummy responds. 1329 // non empty values in JUJU_DUMMY_DELAY will be parsed as 1330 // time.Durations into this value. 1331 var providerDelay time.Duration 1332 1333 // pause execution to simulate the latency of a real provider 1334 func delay() { 1335 if providerDelay > 0 { 1336 logger.Infof("pausing for %v", providerDelay) 1337 <-time.After(providerDelay) 1338 } 1339 }