github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/provider/dummy/environs.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // The dummy provider implements an environment provider for testing 5 // purposes, registered with environs under the name "dummy". 6 // 7 // The configuration YAML for the testing environment 8 // must specify a "controller" property with a boolean 9 // value. If this is true, a controller will be started 10 // when the environment is bootstrapped. 11 // 12 // The configuration data also accepts a "broken" property 13 // of type boolean. If this is non-empty, any operation 14 // after the environment has been opened will return 15 // the error "broken environment", and will also log that. 16 // 17 // The DNS name of instances is the same as the Id, 18 // with ".dns" appended. 19 package dummy 20 21 import ( 22 "crypto/tls" 23 "fmt" 24 "io/ioutil" 25 "net" 26 "net/http/httptest" 27 "os" 28 "runtime" 29 "strconv" 30 "strings" 31 "sync" 32 "time" 33 34 "github.com/juju/clock" 35 "github.com/juju/errors" 36 "github.com/juju/jsonschema" 37 "github.com/juju/loggo" 38 "github.com/juju/pubsub" 39 "github.com/juju/retry" 40 "github.com/juju/schema" 41 gitjujutesting "github.com/juju/testing" 42 jc "github.com/juju/testing/checkers" 43 "github.com/juju/utils/arch" 44 "github.com/juju/version" 45 "github.com/prometheus/client_golang/prometheus" 46 gc "gopkg.in/check.v1" 47 "gopkg.in/juju/charm.v6" 48 "gopkg.in/juju/environschema.v1" 49 "gopkg.in/juju/names.v2" 50 "gopkg.in/juju/worker.v1" 51 52 "github.com/juju/juju/agent" 53 "github.com/juju/juju/api" 54 "github.com/juju/juju/apiserver" 55 "github.com/juju/juju/apiserver/apiserverhttp" 56 "github.com/juju/juju/apiserver/observer" 57 "github.com/juju/juju/apiserver/stateauthenticator" 58 "github.com/juju/juju/cloud" 59 "github.com/juju/juju/cloudconfig/instancecfg" 60 "github.com/juju/juju/core/auditlog" 61 "github.com/juju/juju/core/cache" 62 "github.com/juju/juju/core/constraints" 63 "github.com/juju/juju/core/instance" 64 corelease "github.com/juju/juju/core/lease" 65 "github.com/juju/juju/core/presence" 66 "github.com/juju/juju/core/status" 67 "github.com/juju/juju/environs" 68 "github.com/juju/juju/environs/config" 69 "github.com/juju/juju/environs/context" 70 "github.com/juju/juju/environs/instances" 71 jujuversion "github.com/juju/juju/juju/version" 72 "github.com/juju/juju/mongo" 73 "github.com/juju/juju/mongo/mongotest" 74 "github.com/juju/juju/network" 75 "github.com/juju/juju/provider/common" 76 "github.com/juju/juju/pubsub/centralhub" 77 "github.com/juju/juju/state" 78 "github.com/juju/juju/state/multiwatcher" 79 "github.com/juju/juju/state/stateenvirons" 80 "github.com/juju/juju/storage" 81 "github.com/juju/juju/testing" 82 coretools "github.com/juju/juju/tools" 83 "github.com/juju/juju/worker/lease" 84 "github.com/juju/juju/worker/modelcache" 85 ) 86 87 var logger = loggo.GetLogger("juju.provider.dummy") 88 89 var transientErrorInjection chan error 90 91 const BootstrapInstanceId = "localhost" 92 93 var errNotPrepared = errors.New("model is not prepared") 94 95 // SampleCloudSpec returns an environs.CloudSpec that can be used to 96 // open a dummy Environ. 97 func SampleCloudSpec() environs.CloudSpec { 98 cred := cloud.NewCredential(cloud.UserPassAuthType, map[string]string{"username": "dummy", "password": "secret"}) 99 return environs.CloudSpec{ 100 Type: "dummy", 101 Name: "dummy", 102 Endpoint: "dummy-endpoint", 103 IdentityEndpoint: "dummy-identity-endpoint", 104 Region: "dummy-region", 105 StorageEndpoint: "dummy-storage-endpoint", 106 Credential: &cred, 107 } 108 } 109 110 // SampleConfig() returns an environment configuration with all required 111 // attributes set. 112 func SampleConfig() testing.Attrs { 113 return testing.Attrs{ 114 "type": "dummy", 115 "name": "only", 116 "uuid": testing.ModelTag.Id(), 117 "authorized-keys": testing.FakeAuthKeys, 118 "firewall-mode": config.FwInstance, 119 "ssl-hostname-verification": true, 120 "development": false, 121 "default-series": jujuversion.SupportedLTS(), 122 123 "secret": "pork", 124 "controller": true, 125 } 126 } 127 128 // PatchTransientErrorInjectionChannel sets the transientInjectionError 129 // channel which can be used to inject errors into StartInstance for 130 // testing purposes 131 // The injected errors will use the string received on the channel 132 // and the instance's state will eventually go to error, while the 133 // received string will appear in the info field of the machine's status 134 func PatchTransientErrorInjectionChannel(c chan error) func() { 135 return gitjujutesting.PatchValue(&transientErrorInjection, c) 136 } 137 138 // mongoInfo returns a mongo.MongoInfo which allows clients to connect to the 139 // shared dummy state, if it exists. 140 func mongoInfo() mongo.MongoInfo { 141 if gitjujutesting.MgoServer.Addr() == "" { 142 panic("dummy environ state tests must be run with MgoTestPackage") 143 } 144 mongoPort := strconv.Itoa(gitjujutesting.MgoServer.Port()) 145 addrs := []string{net.JoinHostPort("localhost", mongoPort)} 146 return mongo.MongoInfo{ 147 Info: mongo.Info{ 148 Addrs: addrs, 149 CACert: testing.CACert, 150 DisableTLS: !gitjujutesting.MgoServer.SSLEnabled(), 151 }, 152 } 153 } 154 155 // Operation represents an action on the dummy provider. 156 type Operation interface{} 157 158 type OpBootstrap struct { 159 Context environs.BootstrapContext 160 Env string 161 Args environs.BootstrapParams 162 } 163 164 type OpFinalizeBootstrap struct { 165 Context environs.BootstrapContext 166 Env string 167 InstanceConfig *instancecfg.InstanceConfig 168 } 169 170 type OpDestroy struct { 171 Env string 172 Cloud string 173 CloudRegion string 174 Error error 175 } 176 177 type OpNetworkInterfaces struct { 178 Env string 179 InstanceId instance.Id 180 Info []network.InterfaceInfo 181 } 182 183 type OpSubnets struct { 184 Env string 185 InstanceId instance.Id 186 SubnetIds []network.Id 187 Info []network.SubnetInfo 188 } 189 190 type OpStartInstance struct { 191 Env string 192 MachineId string 193 MachineNonce string 194 PossibleTools coretools.List 195 Instance instances.Instance 196 Constraints constraints.Value 197 SubnetsToZones map[network.Id][]string 198 NetworkInfo []network.InterfaceInfo 199 Volumes []storage.Volume 200 VolumeAttachments []storage.VolumeAttachment 201 Info *mongo.MongoInfo 202 Jobs []multiwatcher.MachineJob 203 APIInfo *api.Info 204 Secret string 205 AgentEnvironment map[string]string 206 } 207 208 type OpStopInstances struct { 209 Env string 210 Ids []instance.Id 211 } 212 213 type OpOpenPorts struct { 214 Env string 215 MachineId string 216 InstanceId instance.Id 217 Rules []network.IngressRule 218 } 219 220 type OpClosePorts struct { 221 Env string 222 MachineId string 223 InstanceId instance.Id 224 Rules []network.IngressRule 225 } 226 227 type OpPutFile struct { 228 Env string 229 FileName string 230 } 231 232 // environProvider represents the dummy provider. There is only ever one 233 // instance of this type (dummy) 234 type environProvider struct { 235 mu sync.Mutex 236 ops chan<- Operation 237 newStatePolicy state.NewPolicyFunc 238 supportsSpaces bool 239 supportsSpaceDiscovery bool 240 apiPort int 241 controllerState *environState 242 state map[string]*environState 243 } 244 245 // APIPort returns the randon api port used by the given provider instance. 246 func APIPort(p environs.EnvironProvider) int { 247 return p.(*environProvider).apiPort 248 } 249 250 // environState represents the state of an environment. 251 // It can be shared between several environ values, 252 // so that a given environment can be opened several times. 253 type environState struct { 254 name string 255 ops chan<- Operation 256 newStatePolicy state.NewPolicyFunc 257 mu sync.Mutex 258 maxId int // maximum instance id allocated so far. 259 maxAddr int // maximum allocated address last byte 260 insts map[instance.Id]*dummyInstance 261 globalRules network.IngressRuleSlice 262 bootstrapped bool 263 mux *apiserverhttp.Mux 264 httpServer *httptest.Server 265 apiServer *apiserver.Server 266 apiState *state.State 267 apiStatePool *state.StatePool 268 hub *pubsub.StructuredHub 269 presence *fakePresence 270 leaseManager *lease.Manager 271 creator string 272 273 modelCacheWorker worker.Worker 274 controller *cache.Controller 275 } 276 277 // environ represents a client's connection to a given environment's 278 // state. 279 type environ struct { 280 storage.ProviderRegistry 281 name string 282 modelUUID string 283 cloud environs.CloudSpec 284 ecfgMutex sync.Mutex 285 ecfgUnlocked *environConfig 286 spacesMutex sync.RWMutex 287 } 288 289 var _ environs.Environ = (*environ)(nil) 290 var _ environs.Networking = (*environ)(nil) 291 292 // discardOperations discards all Operations written to it. 293 var discardOperations = make(chan Operation) 294 295 func init() { 296 environs.RegisterProvider("dummy", &dummy) 297 298 // Prime the first ops channel, so that naive clients can use 299 // the testing environment by simply importing it. 300 go func() { 301 for range discardOperations { 302 } 303 }() 304 } 305 306 // dummy is the dummy environmentProvider singleton. 307 var dummy = environProvider{ 308 ops: discardOperations, 309 state: make(map[string]*environState), 310 newStatePolicy: stateenvirons.GetNewPolicyFunc(), 311 supportsSpaces: true, 312 supportsSpaceDiscovery: false, 313 } 314 315 // Reset resets the entire dummy environment and forgets any registered 316 // operation listener. All opened environments after Reset will share 317 // the same underlying state. 318 func Reset(c *gc.C) { 319 logger.Infof("reset model") 320 dummy.mu.Lock() 321 dummy.ops = discardOperations 322 oldState := dummy.state 323 dummy.controllerState = nil 324 dummy.state = make(map[string]*environState) 325 dummy.newStatePolicy = stateenvirons.GetNewPolicyFunc() 326 dummy.supportsSpaces = true 327 dummy.supportsSpaceDiscovery = false 328 dummy.mu.Unlock() 329 330 // NOTE(axw) we must destroy the old states without holding 331 // the provider lock, or we risk deadlocking. Destroying 332 // state involves closing the embedded API server, which 333 // may require waiting on RPC calls that interact with the 334 // EnvironProvider (e.g. EnvironProvider.Open). 335 for _, s := range oldState { 336 if s.httpServer != nil { 337 logger.Debugf("closing httpServer") 338 s.httpServer.Close() 339 } 340 s.destroy() 341 } 342 if mongoAlive() { 343 err := retry.Call(retry.CallArgs{ 344 Func: gitjujutesting.MgoServer.Reset, 345 // Only interested in retrying the intermittent 346 // 'unexpected message'. 347 IsFatalError: func(err error) bool { 348 return !strings.HasSuffix(err.Error(), "unexpected message") 349 }, 350 Delay: time.Millisecond, 351 Clock: clock.WallClock, 352 Attempts: 5, 353 }) 354 c.Assert(err, jc.ErrorIsNil) 355 } 356 } 357 358 func (state *environState) destroy() { 359 state.mu.Lock() 360 defer state.mu.Unlock() 361 state.destroyLocked() 362 } 363 364 func (state *environState) destroyLocked() { 365 if !state.bootstrapped { 366 return 367 } 368 apiServer := state.apiServer 369 apiStatePool := state.apiStatePool 370 leaseManager := state.leaseManager 371 modelCacheWorker := state.modelCacheWorker 372 state.apiServer = nil 373 state.apiStatePool = nil 374 state.apiState = nil 375 state.controller = nil 376 state.leaseManager = nil 377 state.bootstrapped = false 378 state.hub = nil 379 state.modelCacheWorker = nil 380 381 // Release the lock while we close resources. In particular, 382 // we must not hold the lock while the API server is being 383 // closed, as it may need to interact with the Environ while 384 // shutting down. 385 state.mu.Unlock() 386 defer state.mu.Lock() 387 388 // The apiServer depends on the modelCache, so stop the apiserver first. 389 if apiServer != nil { 390 logger.Debugf("stopping apiServer") 391 if err := apiServer.Stop(); err != nil && mongoAlive() { 392 panic(err) 393 } 394 } 395 396 if modelCacheWorker != nil { 397 logger.Debugf("stopping modelCache worker") 398 if err := worker.Stop(modelCacheWorker); err != nil { 399 panic(err) 400 } 401 } 402 403 if leaseManager != nil { 404 if err := worker.Stop(leaseManager); err != nil && mongoAlive() { 405 panic(err) 406 } 407 } 408 409 if apiStatePool != nil { 410 logger.Debugf("closing apiStatePool") 411 if err := apiStatePool.Close(); err != nil && mongoAlive() { 412 panic(err) 413 } 414 } 415 416 if mongoAlive() { 417 logger.Debugf("resetting MgoServer") 418 gitjujutesting.MgoServer.Reset() 419 } 420 } 421 422 // mongoAlive reports whether the mongo server is 423 // still alive (i.e. has not been deliberately destroyed). 424 // If it has been deliberately destroyed, we will 425 // expect some errors when closing things down. 426 func mongoAlive() bool { 427 return gitjujutesting.MgoServer.Addr() != "" 428 } 429 430 // GetStateInAPIServer returns the state connection used by the API server 431 // This is so code in the test suite can trigger Syncs, etc that the API server 432 // will see, which will then trigger API watchers, etc. 433 func (e *environ) GetStateInAPIServer() *state.State { 434 st, err := e.state() 435 if err != nil { 436 panic(err) 437 } 438 return st.apiState 439 } 440 441 // GetStatePoolInAPIServer returns the StatePool used by the API 442 // server. As for GetStatePoolInAPIServer, this is so code in the 443 // test suite can trigger Syncs etc. 444 func (e *environ) GetStatePoolInAPIServer() *state.StatePool { 445 st, err := e.state() 446 if err != nil { 447 panic(err) 448 } 449 return st.apiStatePool 450 } 451 452 // GetHubInAPIServer returns the central hub used by the API server. 453 func (e *environ) GetHubInAPIServer() *pubsub.StructuredHub { 454 st, err := e.state() 455 if err != nil { 456 panic(err) 457 } 458 return st.hub 459 } 460 461 // GetLeaseManagerInAPIServer returns the channel used to update the 462 // cache.Controller used by the API server 463 func (e *environ) GetLeaseManagerInAPIServer() corelease.Manager { 464 st, err := e.state() 465 if err != nil { 466 panic(err) 467 } 468 return st.leaseManager 469 } 470 471 // GetController returns the cache.Controller used by the API server. 472 func (e *environ) GetController() *cache.Controller { 473 st, err := e.state() 474 if err != nil { 475 panic(err) 476 } 477 return st.controller 478 } 479 480 // newState creates the state for a new environment with the given name. 481 func newState(name string, ops chan<- Operation, newStatePolicy state.NewPolicyFunc) *environState { 482 buf := make([]byte, 8192) 483 buf = buf[:runtime.Stack(buf, false)] 484 s := &environState{ 485 name: name, 486 ops: ops, 487 newStatePolicy: newStatePolicy, 488 insts: make(map[instance.Id]*dummyInstance), 489 creator: string(buf), 490 } 491 return s 492 } 493 494 // listenAPI starts an HTTP server listening for API connections. 495 func (s *environState) listenAPI() int { 496 certPool, err := api.CreateCertPool(testing.CACert) 497 if err != nil { 498 panic(err) 499 } 500 tlsConfig := api.NewTLSConfig(certPool) 501 tlsConfig.ServerName = "juju-apiserver" 502 tlsConfig.Certificates = []tls.Certificate{*testing.ServerTLSCert} 503 s.mux = apiserverhttp.NewMux() 504 s.httpServer = httptest.NewUnstartedServer(s.mux) 505 s.httpServer.TLS = tlsConfig 506 return s.httpServer.Listener.Addr().(*net.TCPAddr).Port 507 } 508 509 // SetSupportsSpaces allows to enable and disable SupportsSpaces for tests. 510 func SetSupportsSpaces(supports bool) bool { 511 dummy.mu.Lock() 512 defer dummy.mu.Unlock() 513 current := dummy.supportsSpaces 514 dummy.supportsSpaces = supports 515 return current 516 } 517 518 // SetSupportsSpaceDiscovery allows to enable and disable 519 // SupportsSpaceDiscovery for tests. 520 func SetSupportsSpaceDiscovery(supports bool) bool { 521 dummy.mu.Lock() 522 defer dummy.mu.Unlock() 523 current := dummy.supportsSpaceDiscovery 524 dummy.supportsSpaceDiscovery = supports 525 return current 526 } 527 528 // Listen directs subsequent operations on any dummy environment 529 // to channel c (if not nil). 530 func Listen(c chan<- Operation) { 531 dummy.mu.Lock() 532 defer dummy.mu.Unlock() 533 if c == nil { 534 c = discardOperations 535 } 536 dummy.ops = c 537 for _, st := range dummy.state { 538 st.mu.Lock() 539 st.ops = c 540 st.mu.Unlock() 541 } 542 } 543 544 var configSchema = environschema.Fields{ 545 "controller": { 546 Description: "Whether the model should start a controller", 547 Type: environschema.Tbool, 548 }, 549 "broken": { 550 Description: "Whitespace-separated Environ methods that should return an error when called", 551 Type: environschema.Tstring, 552 }, 553 "secret": { 554 Description: "A secret", 555 Type: environschema.Tstring, 556 }, 557 } 558 559 var configFields = func() schema.Fields { 560 fs, _, err := configSchema.ValidationSchema() 561 if err != nil { 562 panic(err) 563 } 564 return fs 565 }() 566 567 var configDefaults = schema.Defaults{ 568 "broken": "", 569 "secret": "pork", 570 "controller": false, 571 } 572 573 type environConfig struct { 574 *config.Config 575 attrs map[string]interface{} 576 } 577 578 func (c *environConfig) controller() bool { 579 return c.attrs["controller"].(bool) 580 } 581 582 func (c *environConfig) broken() string { 583 return c.attrs["broken"].(string) 584 } 585 586 func (c *environConfig) secret() string { 587 return c.attrs["secret"].(string) 588 } 589 590 func (p *environProvider) newConfig(cfg *config.Config) (*environConfig, error) { 591 valid, err := p.Validate(cfg, nil) 592 if err != nil { 593 return nil, err 594 } 595 return &environConfig{valid, valid.UnknownAttrs()}, nil 596 } 597 598 func (p *environProvider) Schema() environschema.Fields { 599 fields, err := config.Schema(configSchema) 600 if err != nil { 601 panic(err) 602 } 603 return fields 604 } 605 606 var _ config.ConfigSchemaSource = (*environProvider)(nil) 607 608 // ConfigSchema returns extra config attributes specific 609 // to this provider only. 610 func (p *environProvider) ConfigSchema() schema.Fields { 611 return configFields 612 } 613 614 // ConfigDefaults returns the default values for the 615 // provider specific config attributes. 616 func (p *environProvider) ConfigDefaults() schema.Defaults { 617 return configDefaults 618 } 619 620 func (*environProvider) CredentialSchemas() map[cloud.AuthType]cloud.CredentialSchema { 621 return map[cloud.AuthType]cloud.CredentialSchema{ 622 cloud.EmptyAuthType: {}, 623 cloud.UserPassAuthType: { 624 { 625 "username", cloud.CredentialAttr{Description: "The username to authenticate with."}, 626 }, { 627 "password", cloud.CredentialAttr{ 628 Description: "The password for the specified username.", 629 Hidden: true, 630 }, 631 }, 632 }, 633 } 634 } 635 636 func (*environProvider) DetectCredentials() (*cloud.CloudCredential, error) { 637 return cloud.NewEmptyCloudCredential(), nil 638 } 639 640 func (*environProvider) FinalizeCredential(ctx environs.FinalizeCredentialContext, args environs.FinalizeCredentialParams) (*cloud.Credential, error) { 641 return &args.Credential, nil 642 } 643 644 func (*environProvider) DetectRegions() ([]cloud.Region, error) { 645 return []cloud.Region{{Name: "dummy"}}, nil 646 } 647 648 func (p *environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) { 649 // Check for valid changes for the base config values. 650 if err := config.Validate(cfg, old); err != nil { 651 return nil, err 652 } 653 validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) 654 if err != nil { 655 return nil, err 656 } 657 // Apply the coerced unknown values back into the config. 658 return cfg.Apply(validated) 659 } 660 661 func (e *environ) state() (*environState, error) { 662 dummy.mu.Lock() 663 defer dummy.mu.Unlock() 664 state, ok := dummy.state[e.modelUUID] 665 if !ok { 666 return nil, errNotPrepared 667 } 668 return state, nil 669 } 670 671 // Version is part of the EnvironProvider interface. 672 func (*environProvider) Version() int { 673 return 0 674 } 675 676 func (p *environProvider) Open(args environs.OpenParams) (environs.Environ, error) { 677 p.mu.Lock() 678 defer p.mu.Unlock() 679 ecfg, err := p.newConfig(args.Config) 680 if err != nil { 681 return nil, err 682 } 683 env := &environ{ 684 ProviderRegistry: StorageProviders(), 685 name: ecfg.Name(), 686 modelUUID: args.Config.UUID(), 687 cloud: args.Cloud, 688 ecfgUnlocked: ecfg, 689 } 690 if err := env.checkBroken("Open"); err != nil { 691 return nil, err 692 } 693 return env, nil 694 } 695 696 // CloudSchema returns the schema used to validate input for add-cloud. Since 697 // this provider does not support custom clouds, this always returns nil. 698 func (p *environProvider) CloudSchema() *jsonschema.Schema { 699 return nil 700 } 701 702 // Ping tests the connection to the cloud, to verify the endpoint is valid. 703 func (p *environProvider) Ping(ctx context.ProviderCallContext, endpoint string) error { 704 return errors.NotImplementedf("Ping") 705 } 706 707 // PrepareConfig is specified in the EnvironProvider interface. 708 func (p *environProvider) PrepareConfig(args environs.PrepareConfigParams) (*config.Config, error) { 709 if _, err := dummy.newConfig(args.Config); err != nil { 710 return nil, err 711 } 712 return args.Config, nil 713 } 714 715 // Override for testing - the data directory with which the state api server is initialised. 716 var DataDir = "" 717 var LogDir = "" 718 719 func (e *environ) ecfg() *environConfig { 720 e.ecfgMutex.Lock() 721 ecfg := e.ecfgUnlocked 722 e.ecfgMutex.Unlock() 723 return ecfg 724 } 725 726 func (e *environ) checkBroken(method string) error { 727 for _, m := range strings.Fields(e.ecfg().broken()) { 728 if m == method { 729 return fmt.Errorf("dummy.%s is broken", method) 730 } 731 } 732 return nil 733 } 734 735 // PrecheckInstance is specified in the environs.InstancePrechecker interface. 736 func (*environ) PrecheckInstance(ctx context.ProviderCallContext, args environs.PrecheckInstanceParams) error { 737 if args.Placement != "" && args.Placement != "valid" { 738 return fmt.Errorf("%s placement is invalid", args.Placement) 739 } 740 return nil 741 } 742 743 // Create is part of the Environ interface. 744 func (e *environ) Create(ctx context.ProviderCallContext, args environs.CreateParams) error { 745 dummy.mu.Lock() 746 defer dummy.mu.Unlock() 747 dummy.state[e.modelUUID] = newState(e.name, dummy.ops, dummy.newStatePolicy) 748 return nil 749 } 750 751 // PrepareForBootstrap is part of the Environ interface. 752 func (e *environ) PrepareForBootstrap(ctx environs.BootstrapContext) error { 753 dummy.mu.Lock() 754 defer dummy.mu.Unlock() 755 ecfg := e.ecfgUnlocked 756 757 if ecfg.controller() && dummy.controllerState != nil { 758 // Because of global variables, we can only have one dummy 759 // controller per process. Panic if there is an attempt to 760 // bootstrap while there is another active controller. 761 old := dummy.controllerState 762 panic(fmt.Errorf("cannot share a state between two dummy environs; old %q; new %q: %s", old.name, e.name, old.creator)) 763 } 764 765 // The environment has not been prepared, so create it and record it. 766 // We don't start listening for State or API connections until 767 // Bootstrap has been called. 768 envState := newState(e.name, dummy.ops, dummy.newStatePolicy) 769 if ecfg.controller() { 770 dummy.apiPort = envState.listenAPI() 771 dummy.controllerState = envState 772 } 773 dummy.state[e.modelUUID] = envState 774 return nil 775 } 776 777 func (e *environ) Bootstrap(ctx environs.BootstrapContext, callCtx context.ProviderCallContext, args environs.BootstrapParams) (*environs.BootstrapResult, error) { 778 series := config.PreferredSeries(e.Config()) 779 availableTools, err := args.AvailableTools.Match(coretools.Filter{Series: series}) 780 if err != nil { 781 return nil, err 782 } 783 arch := availableTools.Arches()[0] 784 785 defer delay() 786 if err := e.checkBroken("Bootstrap"); err != nil { 787 return nil, err 788 } 789 if _, ok := args.ControllerConfig.CACert(); !ok { 790 return nil, errors.New("no CA certificate in controller configuration") 791 } 792 793 logger.Infof("would pick agent binaries from %s", availableTools) 794 795 estate, err := e.state() 796 if err != nil { 797 return nil, err 798 } 799 estate.mu.Lock() 800 defer estate.mu.Unlock() 801 if estate.bootstrapped { 802 return nil, errors.New("model is already bootstrapped") 803 } 804 805 // Create an instance for the bootstrap node. 806 logger.Infof("creating bootstrap instance") 807 i := &dummyInstance{ 808 id: BootstrapInstanceId, 809 addresses: network.NewAddresses("localhost"), 810 machineId: agent.BootstrapMachineId, 811 series: series, 812 firewallMode: e.Config().FirewallMode(), 813 state: estate, 814 controller: true, 815 } 816 estate.insts[i.id] = i 817 estate.bootstrapped = true 818 estate.ops <- OpBootstrap{Context: ctx, Env: e.name, Args: args} 819 820 finalize := func(ctx environs.BootstrapContext, icfg *instancecfg.InstanceConfig, _ environs.BootstrapDialOpts) (err error) { 821 if e.ecfg().controller() { 822 icfg.Bootstrap.BootstrapMachineInstanceId = BootstrapInstanceId 823 if err := instancecfg.FinishInstanceConfig(icfg, e.Config()); err != nil { 824 return err 825 } 826 827 adminUser := names.NewUserTag("admin@local") 828 var cloudCredentialTag names.CloudCredentialTag 829 if icfg.Bootstrap.ControllerCloudCredentialName != "" { 830 cloudCredentialTag = names.NewCloudCredentialTag(fmt.Sprintf( 831 "%s/%s/%s", 832 icfg.Bootstrap.ControllerCloud.Name, 833 adminUser.Id(), 834 icfg.Bootstrap.ControllerCloudCredentialName, 835 )) 836 } 837 838 cloudCredentials := make(map[names.CloudCredentialTag]cloud.Credential) 839 if icfg.Bootstrap.ControllerCloudCredential != nil && icfg.Bootstrap.ControllerCloudCredentialName != "" { 840 cloudCredentials[cloudCredentialTag] = *icfg.Bootstrap.ControllerCloudCredential 841 } 842 843 session, err := mongo.DialWithInfo(mongoInfo(), mongotest.DialOpts()) 844 if err != nil { 845 return err 846 } 847 defer session.Close() 848 849 // Since the admin user isn't setup until after here, 850 // the password in the info structure is empty, so the admin 851 // user is constructed with an empty password here. 852 // It is set just below. 853 controller, err := state.Initialize(state.InitializeParams{ 854 Clock: clock.WallClock, 855 ControllerConfig: icfg.Controller.Config, 856 ControllerModelArgs: state.ModelArgs{ 857 Type: state.ModelTypeIAAS, 858 Owner: adminUser, 859 Config: icfg.Bootstrap.ControllerModelConfig, 860 Constraints: icfg.Bootstrap.BootstrapMachineConstraints, 861 CloudName: icfg.Bootstrap.ControllerCloud.Name, 862 CloudRegion: icfg.Bootstrap.ControllerCloudRegion, 863 CloudCredential: cloudCredentialTag, 864 StorageProviderRegistry: e, 865 }, 866 Cloud: icfg.Bootstrap.ControllerCloud, 867 CloudCredentials: cloudCredentials, 868 MongoSession: session, 869 NewPolicy: estate.newStatePolicy, 870 AdminPassword: icfg.Controller.MongoInfo.Password, 871 }) 872 if err != nil { 873 return err 874 } 875 st := controller.SystemState() 876 defer func() { 877 if err != nil { 878 controller.Close() 879 } 880 }() 881 if err := st.SetModelConstraints(args.ModelConstraints); err != nil { 882 return errors.Trace(err) 883 } 884 if err := st.SetAdminMongoPassword(icfg.Controller.MongoInfo.Password); err != nil { 885 return errors.Trace(err) 886 } 887 if err := st.MongoSession().DB("admin").Login("admin", icfg.Controller.MongoInfo.Password); err != nil { 888 return err 889 } 890 env, err := st.Model() 891 if err != nil { 892 return errors.Trace(err) 893 } 894 owner, err := st.User(env.Owner()) 895 if err != nil { 896 return errors.Trace(err) 897 } 898 // We log this out for test purposes only. No one in real life can use 899 // a dummy provider for anything other than testing, so logging the password 900 // here is fine. 901 logger.Debugf("setting password for %q to %q", owner.Name(), icfg.Controller.MongoInfo.Password) 902 owner.SetPassword(icfg.Controller.MongoInfo.Password) 903 statePool := controller.StatePool() 904 stateAuthenticator, err := stateauthenticator.NewAuthenticator(statePool, clock.WallClock) 905 if err != nil { 906 return errors.Trace(err) 907 } 908 stateAuthenticator.AddHandlers(estate.mux) 909 910 machineTag := names.NewMachineTag("0") 911 estate.httpServer.StartTLS() 912 estate.presence = &fakePresence{make(map[string]presence.Status)} 913 estate.hub = centralhub.New(machineTag) 914 915 estate.leaseManager, err = leaseManager( 916 icfg.Controller.Config.ControllerUUID(), 917 st, 918 ) 919 if err != nil { 920 return errors.Trace(err) 921 } 922 923 modelCache, err := modelcache.NewWorker(modelcache.Config{ 924 Logger: loggo.GetLogger("dummy"), 925 StatePool: statePool, 926 PrometheusRegisterer: noopRegisterer{}, 927 Cleanup: func() {}, 928 }) 929 if err != nil { 930 return errors.Trace(err) 931 } 932 estate.modelCacheWorker = modelCache 933 err = modelcache.ExtractCacheController(modelCache, &estate.controller) 934 if err != nil { 935 worker.Stop(modelCache) 936 return errors.Trace(err) 937 } 938 939 estate.apiServer, err = apiserver.NewServer(apiserver.ServerConfig{ 940 StatePool: statePool, 941 Controller: estate.controller, 942 Authenticator: stateAuthenticator, 943 Clock: clock.WallClock, 944 GetAuditConfig: func() auditlog.Config { return auditlog.Config{} }, 945 Tag: machineTag, 946 DataDir: DataDir, 947 LogDir: LogDir, 948 Mux: estate.mux, 949 Hub: estate.hub, 950 Presence: estate.presence, 951 LeaseManager: estate.leaseManager, 952 NewObserver: func() observer.Observer { 953 logger := loggo.GetLogger("juju.apiserver") 954 ctx := observer.RequestObserverContext{ 955 Clock: clock.WallClock, 956 Logger: logger, 957 Hub: estate.hub, 958 } 959 return observer.NewRequestObserver(ctx) 960 }, 961 RateLimitConfig: apiserver.DefaultRateLimitConfig(), 962 PublicDNSName: icfg.Controller.Config.AutocertDNSName(), 963 UpgradeComplete: func() bool { 964 return true 965 }, 966 RestoreStatus: func() state.RestoreStatus { 967 return state.RestoreNotActive 968 }, 969 MetricsCollector: apiserver.NewMetricsCollector(), 970 }) 971 if err != nil { 972 panic(err) 973 } 974 estate.apiState = st 975 estate.apiStatePool = statePool 976 977 // Maintain the state authenticator (time out local user interactions). 978 abort := make(chan struct{}) 979 go stateAuthenticator.Maintain(abort) 980 go func(apiServer *apiserver.Server) { 981 defer close(abort) 982 apiServer.Wait() 983 }(estate.apiServer) 984 } 985 estate.ops <- OpFinalizeBootstrap{Context: ctx, Env: e.name, InstanceConfig: icfg} 986 return nil 987 } 988 989 bsResult := &environs.BootstrapResult{ 990 Arch: arch, 991 Series: series, 992 CloudBootstrapFinalizer: finalize, 993 } 994 return bsResult, nil 995 } 996 997 func leaseManager(controllerUUID string, st *state.State) (*lease.Manager, error) { 998 target := st.LeaseNotifyTarget( 999 ioutil.Discard, 1000 loggo.GetLogger("juju.state.raftlease"), 1001 ) 1002 dummyStore := newLeaseStore(clock.WallClock, target, st.LeaseTrapdoorFunc()) 1003 return lease.NewManager(lease.ManagerConfig{ 1004 Secretary: lease.SecretaryFinder(controllerUUID), 1005 Store: dummyStore, 1006 Logger: loggo.GetLogger("juju.worker.lease.dummy"), 1007 Clock: clock.WallClock, 1008 MaxSleep: time.Minute, 1009 EntityUUID: controllerUUID, 1010 PrometheusRegisterer: noopRegisterer{}, 1011 }) 1012 } 1013 1014 func (e *environ) ControllerInstances(ctx context.ProviderCallContext, controllerUUID string) ([]instance.Id, error) { 1015 estate, err := e.state() 1016 if err != nil { 1017 return nil, err 1018 } 1019 estate.mu.Lock() 1020 defer estate.mu.Unlock() 1021 if err := e.checkBroken("ControllerInstances"); err != nil { 1022 return nil, err 1023 } 1024 if !estate.bootstrapped { 1025 return nil, environs.ErrNotBootstrapped 1026 } 1027 var controllerInstances []instance.Id 1028 for _, v := range estate.insts { 1029 if v.controller { 1030 controllerInstances = append(controllerInstances, v.Id()) 1031 } 1032 } 1033 return controllerInstances, nil 1034 } 1035 1036 func (e *environ) Config() *config.Config { 1037 return e.ecfg().Config 1038 } 1039 1040 func (e *environ) SetConfig(cfg *config.Config) error { 1041 if err := e.checkBroken("SetConfig"); err != nil { 1042 return err 1043 } 1044 ecfg, err := dummy.newConfig(cfg) 1045 if err != nil { 1046 return err 1047 } 1048 e.ecfgMutex.Lock() 1049 e.ecfgUnlocked = ecfg 1050 e.ecfgMutex.Unlock() 1051 return nil 1052 } 1053 1054 // AdoptResources is part of the Environ interface. 1055 func (e *environ) AdoptResources(ctx context.ProviderCallContext, controllerUUID string, fromVersion version.Number) error { 1056 // This provider doesn't track instance -> controller. 1057 return nil 1058 } 1059 1060 func (e *environ) Destroy(ctx context.ProviderCallContext) (res error) { 1061 defer delay() 1062 estate, err := e.state() 1063 if err != nil { 1064 if err == errNotPrepared { 1065 return nil 1066 } 1067 return err 1068 } 1069 defer func() { 1070 // The estate is a pointer to a structure that is stored in the dummy global. 1071 // The Listen method can change the ops channel of any state, and will do so 1072 // under the covers. What we need to do is use the state mutex to add a memory 1073 // barrier such that the ops channel we see here is the latest. 1074 estate.mu.Lock() 1075 ops := estate.ops 1076 name := estate.name 1077 delete(dummy.state, e.modelUUID) 1078 estate.mu.Unlock() 1079 ops <- OpDestroy{ 1080 Env: name, 1081 Cloud: e.cloud.Name, 1082 CloudRegion: e.cloud.Region, 1083 Error: res, 1084 } 1085 }() 1086 if err := e.checkBroken("Destroy"); err != nil { 1087 return err 1088 } 1089 if !e.ecfg().controller() { 1090 return nil 1091 } 1092 estate.destroy() 1093 return nil 1094 } 1095 1096 func (e *environ) DestroyController(ctx context.ProviderCallContext, controllerUUID string) error { 1097 if err := e.Destroy(ctx); err != nil { 1098 return err 1099 } 1100 dummy.mu.Lock() 1101 dummy.controllerState = nil 1102 dummy.mu.Unlock() 1103 return nil 1104 } 1105 1106 // ConstraintsValidator is defined on the Environs interface. 1107 func (e *environ) ConstraintsValidator(ctx context.ProviderCallContext) (constraints.Validator, error) { 1108 validator := constraints.NewValidator() 1109 validator.RegisterUnsupported([]string{constraints.CpuPower, constraints.VirtType}) 1110 validator.RegisterConflicts([]string{constraints.InstanceType}, []string{constraints.Mem}) 1111 validator.RegisterVocabulary(constraints.Arch, []string{arch.AMD64, arch.ARM64, arch.I386, arch.PPC64EL}) 1112 return validator, nil 1113 } 1114 1115 // MaintainInstance is specified in the InstanceBroker interface. 1116 func (*environ) MaintainInstance(ctx context.ProviderCallContext, args environs.StartInstanceParams) error { 1117 return nil 1118 } 1119 1120 // StartInstance is specified in the InstanceBroker interface. 1121 func (e *environ) StartInstance(ctx context.ProviderCallContext, args environs.StartInstanceParams) (*environs.StartInstanceResult, error) { 1122 1123 defer delay() 1124 machineId := args.InstanceConfig.MachineId 1125 logger.Infof("dummy startinstance, machine %s", machineId) 1126 if err := e.checkBroken("StartInstance"); err != nil { 1127 return nil, err 1128 } 1129 estate, err := e.state() 1130 if err != nil { 1131 return nil, err 1132 } 1133 estate.mu.Lock() 1134 defer estate.mu.Unlock() 1135 1136 // check if an error has been injected on the transientErrorInjection channel (testing purposes) 1137 select { 1138 case injectedError := <-transientErrorInjection: 1139 return nil, injectedError 1140 default: 1141 } 1142 1143 if args.InstanceConfig.MachineNonce == "" { 1144 return nil, errors.New("cannot start instance: missing machine nonce") 1145 } 1146 if args.InstanceConfig.Controller != nil { 1147 if args.InstanceConfig.Controller.MongoInfo.Tag != names.NewMachineTag(machineId) { 1148 return nil, errors.New("entity tag must match started machine") 1149 } 1150 } 1151 if args.InstanceConfig.APIInfo.Tag != names.NewMachineTag(machineId) { 1152 return nil, errors.New("entity tag must match started machine") 1153 } 1154 logger.Infof("would pick agent binaries from %s", args.Tools) 1155 series := args.Tools.OneSeries() 1156 1157 idString := fmt.Sprintf("%s-%d", e.name, estate.maxId) 1158 // Add the addresses we want to see in the machine doc. This means both 1159 // IPv4 and IPv6 loopback, as well as the DNS name. 1160 addrs := network.NewAddresses(idString+".dns", "127.0.0.1", "::1") 1161 logger.Debugf("StartInstance addresses: %v", addrs) 1162 i := &dummyInstance{ 1163 id: instance.Id(idString), 1164 addresses: addrs, 1165 machineId: machineId, 1166 series: series, 1167 firewallMode: e.Config().FirewallMode(), 1168 state: estate, 1169 } 1170 1171 var hc *instance.HardwareCharacteristics 1172 // To match current system capability, only provide hardware characteristics for 1173 // environ machines, not containers. 1174 if state.ParentId(machineId) == "" { 1175 // Assume that the provided Availability Zone won't fail, 1176 // though one is required. 1177 var zone string 1178 if args.Placement != "" { 1179 split := strings.Split(args.Placement, "=") 1180 if len(split) == 2 && split[0] == "zone" { 1181 zone = split[1] 1182 } 1183 } 1184 if zone == "" && args.AvailabilityZone != "" { 1185 zone = args.AvailabilityZone 1186 } 1187 1188 // We will just assume the instance hardware characteristics exactly matches 1189 // the supplied constraints (if specified). 1190 hc = &instance.HardwareCharacteristics{ 1191 Arch: args.Constraints.Arch, 1192 Mem: args.Constraints.Mem, 1193 RootDisk: args.Constraints.RootDisk, 1194 CpuCores: args.Constraints.CpuCores, 1195 CpuPower: args.Constraints.CpuPower, 1196 Tags: args.Constraints.Tags, 1197 AvailabilityZone: &zone, 1198 } 1199 // Fill in some expected instance hardware characteristics if constraints not specified. 1200 if hc.Arch == nil { 1201 arch := "amd64" 1202 hc.Arch = &arch 1203 } 1204 if hc.Mem == nil { 1205 mem := uint64(1024) 1206 hc.Mem = &mem 1207 } 1208 if hc.RootDisk == nil { 1209 disk := uint64(8192) 1210 hc.RootDisk = &disk 1211 } 1212 if hc.CpuCores == nil { 1213 cores := uint64(1) 1214 hc.CpuCores = &cores 1215 } 1216 } 1217 // Simulate subnetsToZones gets populated when spaces given in constraints. 1218 spaces := args.Constraints.IncludeSpaces() 1219 var subnetsToZones map[network.Id][]string 1220 for isp := range spaces { 1221 // Simulate 2 subnets per space. 1222 if subnetsToZones == nil { 1223 subnetsToZones = make(map[network.Id][]string) 1224 } 1225 for isn := 0; isn < 2; isn++ { 1226 providerId := fmt.Sprintf("subnet-%d", isp+isn) 1227 zone := fmt.Sprintf("zone%d", isp+isn) 1228 subnetsToZones[network.Id(providerId)] = []string{zone} 1229 } 1230 } 1231 // Simulate creating volumes when requested. 1232 volumes := make([]storage.Volume, len(args.Volumes)) 1233 for iv, v := range args.Volumes { 1234 persistent, _ := v.Attributes["persistent"].(bool) 1235 volumes[iv] = storage.Volume{ 1236 Tag: v.Tag, 1237 VolumeInfo: storage.VolumeInfo{ 1238 Size: v.Size, 1239 Persistent: persistent, 1240 }, 1241 } 1242 } 1243 // Simulate attaching volumes when requested. 1244 volumeAttachments := make([]storage.VolumeAttachment, len(args.VolumeAttachments)) 1245 for iv, v := range args.VolumeAttachments { 1246 volumeAttachments[iv] = storage.VolumeAttachment{ 1247 Volume: v.Volume, 1248 Machine: v.Machine, 1249 VolumeAttachmentInfo: storage.VolumeAttachmentInfo{ 1250 DeviceName: fmt.Sprintf("sd%c", 'b'+rune(iv)), 1251 ReadOnly: v.ReadOnly, 1252 }, 1253 } 1254 } 1255 var mongoInfo *mongo.MongoInfo 1256 if args.InstanceConfig.Controller != nil { 1257 mongoInfo = args.InstanceConfig.Controller.MongoInfo 1258 } 1259 estate.insts[i.id] = i 1260 estate.maxId++ 1261 estate.ops <- OpStartInstance{ 1262 Env: e.name, 1263 MachineId: machineId, 1264 MachineNonce: args.InstanceConfig.MachineNonce, 1265 PossibleTools: args.Tools, 1266 Constraints: args.Constraints, 1267 SubnetsToZones: subnetsToZones, 1268 Volumes: volumes, 1269 VolumeAttachments: volumeAttachments, 1270 Instance: i, 1271 Jobs: args.InstanceConfig.Jobs, 1272 Info: mongoInfo, 1273 APIInfo: args.InstanceConfig.APIInfo, 1274 AgentEnvironment: args.InstanceConfig.AgentEnvironment, 1275 Secret: e.ecfg().secret(), 1276 } 1277 return &environs.StartInstanceResult{ 1278 Instance: i, 1279 Hardware: hc, 1280 }, nil 1281 } 1282 1283 func (e *environ) StopInstances(ctx context.ProviderCallContext, ids ...instance.Id) error { 1284 defer delay() 1285 if err := e.checkBroken("StopInstance"); err != nil { 1286 return err 1287 } 1288 estate, err := e.state() 1289 if err != nil { 1290 return err 1291 } 1292 estate.mu.Lock() 1293 defer estate.mu.Unlock() 1294 for _, id := range ids { 1295 delete(estate.insts, id) 1296 } 1297 estate.ops <- OpStopInstances{ 1298 Env: e.name, 1299 Ids: ids, 1300 } 1301 return nil 1302 } 1303 1304 func (e *environ) Instances(ctx context.ProviderCallContext, ids []instance.Id) (insts []instances.Instance, err error) { 1305 defer delay() 1306 if err := e.checkBroken("Instances"); err != nil { 1307 return nil, err 1308 } 1309 if len(ids) == 0 { 1310 return nil, nil 1311 } 1312 estate, err := e.state() 1313 if err != nil { 1314 return nil, err 1315 } 1316 estate.mu.Lock() 1317 defer estate.mu.Unlock() 1318 notFound := 0 1319 for _, id := range ids { 1320 inst := estate.insts[id] 1321 if inst == nil { 1322 err = environs.ErrPartialInstances 1323 notFound++ 1324 insts = append(insts, nil) 1325 } else { 1326 insts = append(insts, inst) 1327 } 1328 } 1329 if notFound == len(ids) { 1330 return nil, environs.ErrNoInstances 1331 } 1332 return 1333 } 1334 1335 // SupportsSpaces is specified on environs.Networking. 1336 func (env *environ) SupportsSpaces(ctx context.ProviderCallContext) (bool, error) { 1337 dummy.mu.Lock() 1338 defer dummy.mu.Unlock() 1339 if !dummy.supportsSpaces { 1340 return false, errors.NotSupportedf("spaces") 1341 } 1342 return true, nil 1343 } 1344 1345 // SupportsSpaceDiscovery is specified on environs.Networking. 1346 func (env *environ) SupportsSpaceDiscovery(ctx context.ProviderCallContext) (bool, error) { 1347 if err := env.checkBroken("SupportsSpaceDiscovery"); err != nil { 1348 return false, err 1349 } 1350 dummy.mu.Lock() 1351 defer dummy.mu.Unlock() 1352 if !dummy.supportsSpaceDiscovery { 1353 return false, nil 1354 } 1355 return true, nil 1356 } 1357 1358 // SupportsContainerAddresses is specified on environs.Networking. 1359 func (env *environ) SupportsContainerAddresses(ctx context.ProviderCallContext) (bool, error) { 1360 return false, errors.NotSupportedf("container addresses") 1361 } 1362 1363 // Spaces is specified on environs.Networking. 1364 func (env *environ) Spaces(ctx context.ProviderCallContext) ([]network.SpaceInfo, error) { 1365 if err := env.checkBroken("Spaces"); err != nil { 1366 return []network.SpaceInfo{}, err 1367 } 1368 return []network.SpaceInfo{{ 1369 Name: "foo", 1370 ProviderId: network.Id("0"), 1371 Subnets: []network.SubnetInfo{{ 1372 ProviderId: network.Id("1"), 1373 AvailabilityZones: []string{"zone1"}, 1374 }, { 1375 ProviderId: network.Id("2"), 1376 AvailabilityZones: []string{"zone1"}, 1377 }}}, { 1378 Name: "Another Foo 99!", 1379 ProviderId: "1", 1380 Subnets: []network.SubnetInfo{{ 1381 ProviderId: network.Id("3"), 1382 AvailabilityZones: []string{"zone1"}, 1383 }}}, { 1384 Name: "foo-", 1385 ProviderId: "2", 1386 Subnets: []network.SubnetInfo{{ 1387 ProviderId: network.Id("4"), 1388 AvailabilityZones: []string{"zone1"}, 1389 }}}, { 1390 Name: "---", 1391 ProviderId: "3", 1392 Subnets: []network.SubnetInfo{{ 1393 ProviderId: network.Id("5"), 1394 AvailabilityZones: []string{"zone1"}, 1395 }}}}, nil 1396 } 1397 1398 // NetworkInterfaces implements Environ.NetworkInterfaces(). 1399 func (env *environ) NetworkInterfaces(ctx context.ProviderCallContext, instId instance.Id) ([]network.InterfaceInfo, error) { 1400 if err := env.checkBroken("NetworkInterfaces"); err != nil { 1401 return nil, err 1402 } 1403 1404 estate, err := env.state() 1405 if err != nil { 1406 return nil, err 1407 } 1408 estate.mu.Lock() 1409 defer estate.mu.Unlock() 1410 1411 // Simulate 3 NICs - primary and secondary enabled plus a disabled NIC. 1412 // all configured using DHCP and having fake DNS servers and gateway. 1413 info := make([]network.InterfaceInfo, 3) 1414 for i, netName := range []string{"private", "public", "disabled"} { 1415 info[i] = network.InterfaceInfo{ 1416 DeviceIndex: i, 1417 ProviderId: network.Id(fmt.Sprintf("dummy-eth%d", i)), 1418 ProviderSubnetId: network.Id("dummy-" + netName), 1419 InterfaceType: network.EthernetInterface, 1420 CIDR: fmt.Sprintf("0.%d.0.0/24", (i+1)*10), 1421 InterfaceName: fmt.Sprintf("eth%d", i), 1422 VLANTag: i, 1423 MACAddress: fmt.Sprintf("aa:bb:cc:dd:ee:f%d", i), 1424 Disabled: i == 2, 1425 NoAutoStart: i%2 != 0, 1426 ConfigType: network.ConfigDHCP, 1427 Address: network.NewAddress( 1428 fmt.Sprintf("0.%d.0.%d", (i+1)*10, estate.maxAddr+2), 1429 ), 1430 DNSServers: network.NewAddresses("ns1.dummy", "ns2.dummy"), 1431 GatewayAddress: network.NewAddress( 1432 fmt.Sprintf("0.%d.0.1", (i+1)*10), 1433 ), 1434 } 1435 } 1436 1437 estate.ops <- OpNetworkInterfaces{ 1438 Env: env.name, 1439 InstanceId: instId, 1440 Info: info, 1441 } 1442 1443 return info, nil 1444 } 1445 1446 type azShim struct { 1447 name string 1448 available bool 1449 } 1450 1451 func (az azShim) Name() string { 1452 return az.name 1453 } 1454 1455 func (az azShim) Available() bool { 1456 return az.available 1457 } 1458 1459 // AvailabilityZones implements environs.ZonedEnviron. 1460 func (env *environ) AvailabilityZones(ctx context.ProviderCallContext) ([]common.AvailabilityZone, error) { 1461 // TODO(dimitern): Fix this properly. 1462 return []common.AvailabilityZone{ 1463 azShim{"zone1", true}, 1464 azShim{"zone2", false}, 1465 azShim{"zone3", true}, 1466 azShim{"zone4", true}, 1467 }, nil 1468 } 1469 1470 // InstanceAvailabilityZoneNames implements environs.ZonedEnviron. 1471 func (env *environ) InstanceAvailabilityZoneNames(ctx context.ProviderCallContext, ids []instance.Id) ([]string, error) { 1472 if err := env.checkBroken("InstanceAvailabilityZoneNames"); err != nil { 1473 return nil, errors.NotSupportedf("instance availability zones") 1474 } 1475 availabilityZones, err := env.AvailabilityZones(ctx) 1476 if err != nil { 1477 return nil, err 1478 } 1479 azMaxIndex := len(availabilityZones) - 1 1480 azIndex := 0 1481 returnValue := make([]string, len(ids)) 1482 for i := range ids { 1483 if availabilityZones[azIndex].Available() { 1484 returnValue[i] = availabilityZones[azIndex].Name() 1485 } else { 1486 // Based on knowledge of how the AZs are setup above 1487 // in AvailabilityZones() 1488 azIndex += 1 1489 returnValue[i] = availabilityZones[azIndex].Name() 1490 } 1491 azIndex += 1 1492 if azIndex == azMaxIndex { 1493 azIndex = 0 1494 } 1495 } 1496 return returnValue, nil 1497 } 1498 1499 // DeriveAvailabilityZones is part of the common.ZonedEnviron interface. 1500 func (env *environ) DeriveAvailabilityZones(ctx context.ProviderCallContext, args environs.StartInstanceParams) ([]string, error) { 1501 return nil, nil 1502 } 1503 1504 // Subnets implements environs.Environ.Subnets. 1505 func (env *environ) Subnets(ctx context.ProviderCallContext, instId instance.Id, subnetIds []network.Id) ([]network.SubnetInfo, error) { 1506 if err := env.checkBroken("Subnets"); err != nil { 1507 return nil, err 1508 } 1509 1510 estate, err := env.state() 1511 if err != nil { 1512 return nil, err 1513 } 1514 estate.mu.Lock() 1515 defer estate.mu.Unlock() 1516 1517 if ok, _ := env.SupportsSpaceDiscovery(ctx); ok { 1518 // Space discovery needs more subnets to work with. 1519 return env.subnetsForSpaceDiscovery(estate) 1520 } 1521 1522 allSubnets := []network.SubnetInfo{{ 1523 CIDR: "0.10.0.0/24", 1524 ProviderId: "dummy-private", 1525 AvailabilityZones: []string{"zone1", "zone2"}, 1526 }, { 1527 CIDR: "0.20.0.0/24", 1528 ProviderId: "dummy-public", 1529 }} 1530 1531 // Filter result by ids, if given. 1532 var result []network.SubnetInfo 1533 for _, subId := range subnetIds { 1534 switch subId { 1535 case "dummy-private": 1536 result = append(result, allSubnets[0]) 1537 case "dummy-public": 1538 result = append(result, allSubnets[1]) 1539 } 1540 } 1541 if len(subnetIds) == 0 { 1542 result = append([]network.SubnetInfo{}, allSubnets...) 1543 } 1544 if len(result) == 0 { 1545 // No results, so just return them now. 1546 estate.ops <- OpSubnets{ 1547 Env: env.name, 1548 InstanceId: instId, 1549 SubnetIds: subnetIds, 1550 Info: result, 1551 } 1552 return result, nil 1553 } 1554 1555 estate.ops <- OpSubnets{ 1556 Env: env.name, 1557 InstanceId: instId, 1558 SubnetIds: subnetIds, 1559 Info: result, 1560 } 1561 return result, nil 1562 } 1563 1564 func (env *environ) subnetsForSpaceDiscovery(estate *environState) ([]network.SubnetInfo, error) { 1565 result := []network.SubnetInfo{{ 1566 ProviderId: network.Id("1"), 1567 AvailabilityZones: []string{"zone1"}, 1568 CIDR: "192.168.1.0/24", 1569 }, { 1570 ProviderId: network.Id("2"), 1571 AvailabilityZones: []string{"zone1"}, 1572 CIDR: "192.168.2.0/24", 1573 VLANTag: 1, 1574 }, { 1575 ProviderId: network.Id("3"), 1576 AvailabilityZones: []string{"zone1"}, 1577 CIDR: "192.168.3.0/24", 1578 }, { 1579 ProviderId: network.Id("4"), 1580 AvailabilityZones: []string{"zone1"}, 1581 CIDR: "192.168.4.0/24", 1582 }, { 1583 ProviderId: network.Id("5"), 1584 AvailabilityZones: []string{"zone1"}, 1585 CIDR: "192.168.5.0/24", 1586 }} 1587 estate.ops <- OpSubnets{ 1588 Env: env.name, 1589 InstanceId: instance.UnknownId, 1590 SubnetIds: []network.Id{}, 1591 Info: result, 1592 } 1593 return result, nil 1594 } 1595 1596 func (e *environ) AllInstances(ctx context.ProviderCallContext) ([]instances.Instance, error) { 1597 defer delay() 1598 if err := e.checkBroken("AllInstances"); err != nil { 1599 return nil, err 1600 } 1601 var insts []instances.Instance 1602 estate, err := e.state() 1603 if err != nil { 1604 return nil, err 1605 } 1606 estate.mu.Lock() 1607 defer estate.mu.Unlock() 1608 for _, v := range estate.insts { 1609 insts = append(insts, v) 1610 } 1611 return insts, nil 1612 } 1613 1614 func (e *environ) OpenPorts(ctx context.ProviderCallContext, rules []network.IngressRule) error { 1615 if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal { 1616 return fmt.Errorf("invalid firewall mode %q for opening ports on model", mode) 1617 } 1618 estate, err := e.state() 1619 if err != nil { 1620 return err 1621 } 1622 estate.mu.Lock() 1623 defer estate.mu.Unlock() 1624 for _, r := range rules { 1625 if len(r.SourceCIDRs) == 0 { 1626 r.SourceCIDRs = []string{"0.0.0.0/0"} 1627 } 1628 found := false 1629 for _, rule := range estate.globalRules { 1630 if r.String() == rule.String() { 1631 found = true 1632 } 1633 } 1634 if !found { 1635 estate.globalRules = append(estate.globalRules, r) 1636 } 1637 } 1638 1639 return nil 1640 } 1641 1642 func (e *environ) ClosePorts(ctx context.ProviderCallContext, rules []network.IngressRule) error { 1643 if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal { 1644 return fmt.Errorf("invalid firewall mode %q for closing ports on model", mode) 1645 } 1646 estate, err := e.state() 1647 if err != nil { 1648 return err 1649 } 1650 estate.mu.Lock() 1651 defer estate.mu.Unlock() 1652 for _, r := range rules { 1653 for i, rule := range estate.globalRules { 1654 if r.String() == rule.String() { 1655 estate.globalRules = estate.globalRules[:i+copy(estate.globalRules[i:], estate.globalRules[i+1:])] 1656 } 1657 } 1658 } 1659 return nil 1660 } 1661 1662 func (e *environ) IngressRules(ctx context.ProviderCallContext) (rules []network.IngressRule, err error) { 1663 if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal { 1664 return nil, fmt.Errorf("invalid firewall mode %q for retrieving ingress rules from model", mode) 1665 } 1666 estate, err := e.state() 1667 if err != nil { 1668 return nil, err 1669 } 1670 estate.mu.Lock() 1671 defer estate.mu.Unlock() 1672 for _, r := range estate.globalRules { 1673 rules = append(rules, r) 1674 } 1675 network.SortIngressRules(rules) 1676 return 1677 } 1678 1679 func (*environ) Provider() environs.EnvironProvider { 1680 return &dummy 1681 } 1682 1683 type dummyInstance struct { 1684 state *environState 1685 rules network.IngressRuleSlice 1686 id instance.Id 1687 status string 1688 machineId string 1689 series string 1690 firewallMode string 1691 controller bool 1692 1693 mu sync.Mutex 1694 addresses []network.Address 1695 broken []string 1696 } 1697 1698 func (inst *dummyInstance) Id() instance.Id { 1699 return inst.id 1700 } 1701 1702 func (inst *dummyInstance) Status(ctx context.ProviderCallContext) instance.Status { 1703 inst.mu.Lock() 1704 defer inst.mu.Unlock() 1705 // TODO(perrito666) add a provider status -> juju status mapping. 1706 jujuStatus := status.Pending 1707 if inst.status != "" { 1708 dummyStatus := status.Status(inst.status) 1709 if dummyStatus.KnownInstanceStatus() { 1710 jujuStatus = dummyStatus 1711 } 1712 } 1713 1714 return instance.Status{ 1715 Status: jujuStatus, 1716 Message: inst.status, 1717 } 1718 1719 } 1720 1721 // SetInstanceAddresses sets the addresses associated with the given 1722 // dummy instance. 1723 func SetInstanceAddresses(inst instances.Instance, addrs []network.Address) { 1724 inst0 := inst.(*dummyInstance) 1725 inst0.mu.Lock() 1726 inst0.addresses = append(inst0.addresses[:0], addrs...) 1727 logger.Debugf("setting instance %q addresses to %v", inst0.Id(), addrs) 1728 inst0.mu.Unlock() 1729 } 1730 1731 // SetInstanceStatus sets the status associated with the given 1732 // dummy instance. 1733 func SetInstanceStatus(inst instances.Instance, status string) { 1734 inst0 := inst.(*dummyInstance) 1735 inst0.mu.Lock() 1736 inst0.status = status 1737 inst0.mu.Unlock() 1738 } 1739 1740 // SetInstanceBroken marks the named methods of the instance as broken. 1741 // Any previously broken methods not in the set will no longer be broken. 1742 func SetInstanceBroken(inst instances.Instance, methods ...string) { 1743 inst0 := inst.(*dummyInstance) 1744 inst0.mu.Lock() 1745 inst0.broken = methods 1746 inst0.mu.Unlock() 1747 } 1748 1749 func (inst *dummyInstance) checkBroken(method string) error { 1750 for _, m := range inst.broken { 1751 if m == method { 1752 return fmt.Errorf("dummyInstance.%s is broken", method) 1753 } 1754 } 1755 return nil 1756 } 1757 1758 func (inst *dummyInstance) Addresses(ctx context.ProviderCallContext) ([]network.Address, error) { 1759 inst.mu.Lock() 1760 defer inst.mu.Unlock() 1761 if err := inst.checkBroken("Addresses"); err != nil { 1762 return nil, err 1763 } 1764 return append([]network.Address{}, inst.addresses...), nil 1765 } 1766 1767 func (inst *dummyInstance) OpenPorts(ctx context.ProviderCallContext, machineId string, rules []network.IngressRule) error { 1768 defer delay() 1769 logger.Infof("openPorts %s, %#v", machineId, rules) 1770 if inst.firewallMode != config.FwInstance { 1771 return fmt.Errorf("invalid firewall mode %q for opening ports on instance", 1772 inst.firewallMode) 1773 } 1774 if inst.machineId != machineId { 1775 panic(fmt.Errorf("OpenPorts with mismatched machine id, expected %q got %q", inst.machineId, machineId)) 1776 } 1777 inst.state.mu.Lock() 1778 defer inst.state.mu.Unlock() 1779 if err := inst.checkBroken("OpenPorts"); err != nil { 1780 return err 1781 } 1782 inst.state.ops <- OpOpenPorts{ 1783 Env: inst.state.name, 1784 MachineId: machineId, 1785 InstanceId: inst.Id(), 1786 Rules: rules, 1787 } 1788 for _, r := range rules { 1789 if len(r.SourceCIDRs) == 0 { 1790 r.SourceCIDRs = []string{"0.0.0.0/0"} 1791 } 1792 found := false 1793 for i, rule := range inst.rules { 1794 if r.PortRange == rule.PortRange { 1795 ruleCopy := r 1796 inst.rules[i] = ruleCopy 1797 found = true 1798 break 1799 } 1800 if r.String() == rule.String() { 1801 found = true 1802 break 1803 } 1804 } 1805 if !found { 1806 inst.rules = append(inst.rules, r) 1807 } 1808 } 1809 return nil 1810 } 1811 1812 func (inst *dummyInstance) ClosePorts(ctx context.ProviderCallContext, machineId string, rules []network.IngressRule) error { 1813 defer delay() 1814 if inst.firewallMode != config.FwInstance { 1815 return fmt.Errorf("invalid firewall mode %q for closing ports on instance", 1816 inst.firewallMode) 1817 } 1818 if inst.machineId != machineId { 1819 panic(fmt.Errorf("ClosePorts with mismatched machine id, expected %s got %s", inst.machineId, machineId)) 1820 } 1821 inst.state.mu.Lock() 1822 defer inst.state.mu.Unlock() 1823 if err := inst.checkBroken("ClosePorts"); err != nil { 1824 return err 1825 } 1826 inst.state.ops <- OpClosePorts{ 1827 Env: inst.state.name, 1828 MachineId: machineId, 1829 InstanceId: inst.Id(), 1830 Rules: rules, 1831 } 1832 for _, r := range rules { 1833 for i, rule := range inst.rules { 1834 if r.String() == rule.String() { 1835 inst.rules = inst.rules[:i+copy(inst.rules[i:], inst.rules[i+1:])] 1836 } 1837 } 1838 } 1839 return nil 1840 } 1841 1842 func (inst *dummyInstance) IngressRules(ctx context.ProviderCallContext, machineId string) (rules []network.IngressRule, err error) { 1843 defer delay() 1844 if inst.firewallMode != config.FwInstance { 1845 return nil, fmt.Errorf("invalid firewall mode %q for retrieving ingress rules from instance", 1846 inst.firewallMode) 1847 } 1848 if inst.machineId != machineId { 1849 panic(fmt.Errorf("Rules with mismatched machine id, expected %q got %q", inst.machineId, machineId)) 1850 } 1851 inst.state.mu.Lock() 1852 defer inst.state.mu.Unlock() 1853 if err := inst.checkBroken("IngressRules"); err != nil { 1854 return nil, err 1855 } 1856 for _, r := range inst.rules { 1857 rules = append(rules, r) 1858 } 1859 network.SortIngressRules(rules) 1860 return 1861 } 1862 1863 // providerDelay controls the delay before dummy responds. 1864 // non empty values in JUJU_DUMMY_DELAY will be parsed as 1865 // time.Durations into this value. 1866 var providerDelay, _ = time.ParseDuration(os.Getenv("JUJU_DUMMY_DELAY")) // parse errors are ignored 1867 1868 // pause execution to simulate the latency of a real provider 1869 func delay() { 1870 if providerDelay > 0 { 1871 logger.Infof("pausing for %v", providerDelay) 1872 <-time.After(providerDelay) 1873 } 1874 } 1875 1876 func (e *environ) AllocateContainerAddresses(ctx context.ProviderCallContext, hostInstanceID instance.Id, containerTag names.MachineTag, preparedInfo []network.InterfaceInfo) ([]network.InterfaceInfo, error) { 1877 return nil, errors.NotSupportedf("container address allocation") 1878 } 1879 1880 func (e *environ) ReleaseContainerAddresses(ctx context.ProviderCallContext, interfaces []network.ProviderInterfaceInfo) error { 1881 return errors.NotSupportedf("container address allocation") 1882 } 1883 1884 // ProviderSpaceInfo implements NetworkingEnviron. 1885 func (*environ) ProviderSpaceInfo(ctx context.ProviderCallContext, space *network.SpaceInfo) (*environs.ProviderSpaceInfo, error) { 1886 return nil, errors.NotSupportedf("provider space info") 1887 } 1888 1889 // AreSpacesRoutable implements NetworkingEnviron. 1890 func (*environ) AreSpacesRoutable(ctx context.ProviderCallContext, space1, space2 *environs.ProviderSpaceInfo) (bool, error) { 1891 return false, nil 1892 } 1893 1894 // MaybeWriteLXDProfile implements environs.LXDProfiler. 1895 func (env *environ) MaybeWriteLXDProfile(pName string, put *charm.LXDProfile) error { 1896 return nil 1897 } 1898 1899 // LXDProfileNames implements environs.LXDProfiler. 1900 func (env *environ) LXDProfileNames(containerName string) ([]string, error) { 1901 return nil, nil 1902 } 1903 1904 // ReplaceOrAddInstanceProfile implements environs.LXDProfiler. 1905 func (env *environ) ReplaceOrAddInstanceProfile(instId, oldProfile, newProfile string, put *charm.LXDProfile) ([]string, error) { 1906 return []string{newProfile}, nil 1907 } 1908 1909 // SSHAddresses implements environs.SSHAddresses. 1910 // For testing we cut "100.100.100.100" out of this list. 1911 func (*environ) SSHAddresses(ctx context.ProviderCallContext, addresses []network.Address) ([]network.Address, error) { 1912 var rv []network.Address 1913 for _, addr := range addresses { 1914 if addr.Value != "100.100.100.100" { 1915 rv = append(rv, addr) 1916 } 1917 } 1918 return rv, nil 1919 } 1920 1921 // SuperSubnets implements environs.SuperSubnets 1922 func (*environ) SuperSubnets(ctx context.ProviderCallContext) ([]string, error) { 1923 return nil, errors.NotSupportedf("super subnets") 1924 } 1925 1926 // SetAgentStatus sets the presence for a particular agent in the fake presence implementation. 1927 func (e *environ) SetAgentStatus(agent string, status presence.Status) { 1928 estate, err := e.state() 1929 if err != nil { 1930 panic(err) 1931 } 1932 estate.presence.agent[agent] = status 1933 } 1934 1935 // fakePresence returns alive for all agent alive requests. 1936 type fakePresence struct { 1937 agent map[string]presence.Status 1938 } 1939 1940 func (*fakePresence) Disable() {} 1941 func (*fakePresence) Enable() {} 1942 func (*fakePresence) IsEnabled() bool { return true } 1943 func (*fakePresence) Connect(server, model, agent string, id uint64, controllerAgent bool, userData string) { 1944 } 1945 func (*fakePresence) Disconnect(server string, id uint64) {} 1946 func (*fakePresence) Activity(server string, id uint64) {} 1947 func (*fakePresence) ServerDown(server string) {} 1948 func (*fakePresence) UpdateServer(server string, connections []presence.Value) error { return nil } 1949 func (f *fakePresence) Connections() presence.Connections { return f } 1950 1951 func (f *fakePresence) ForModel(model string) presence.Connections { return f } 1952 func (f *fakePresence) ForServer(server string) presence.Connections { return f } 1953 func (f *fakePresence) ForAgent(agent string) presence.Connections { return f } 1954 func (*fakePresence) Count() int { return 0 } 1955 func (*fakePresence) Models() []string { return nil } 1956 func (*fakePresence) Servers() []string { return nil } 1957 func (*fakePresence) Agents() []string { return nil } 1958 func (*fakePresence) Values() []presence.Value { return nil } 1959 1960 func (f *fakePresence) AgentStatus(agent string) (presence.Status, error) { 1961 if status, found := f.agent[agent]; found { 1962 return status, nil 1963 } 1964 return presence.Alive, nil 1965 } 1966 1967 type noopRegisterer struct { 1968 prometheus.Registerer 1969 } 1970 1971 func (noopRegisterer) Register(prometheus.Collector) error { 1972 return nil 1973 } 1974 1975 func (noopRegisterer) Unregister(prometheus.Collector) bool { 1976 return true 1977 }