github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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  //
    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  	"os"
    29  	"strconv"
    30  	"strings"
    31  	"sync"
    32  	"time"
    33  
    34  	"github.com/juju/errors"
    35  	"github.com/juju/loggo"
    36  	"github.com/juju/names"
    37  	"github.com/juju/schema"
    38  	gitjujutesting "github.com/juju/testing"
    39  	jc "github.com/juju/testing/checkers"
    40  	"github.com/juju/utils/arch"
    41  	gc "gopkg.in/check.v1"
    42  	"gopkg.in/juju/environschema.v1"
    43  
    44  	"github.com/juju/juju/agent"
    45  	"github.com/juju/juju/api"
    46  	"github.com/juju/juju/apiserver"
    47  	"github.com/juju/juju/cloud"
    48  	"github.com/juju/juju/cloudconfig/instancecfg"
    49  	"github.com/juju/juju/constraints"
    50  	"github.com/juju/juju/environs"
    51  	"github.com/juju/juju/environs/config"
    52  	"github.com/juju/juju/instance"
    53  	"github.com/juju/juju/mongo"
    54  	"github.com/juju/juju/network"
    55  	"github.com/juju/juju/provider/common"
    56  	"github.com/juju/juju/state"
    57  	"github.com/juju/juju/state/multiwatcher"
    58  	"github.com/juju/juju/status"
    59  	"github.com/juju/juju/storage"
    60  	"github.com/juju/juju/testing"
    61  	coretools "github.com/juju/juju/tools"
    62  )
    63  
    64  var logger = loggo.GetLogger("juju.provider.dummy")
    65  
    66  var transientErrorInjection chan error
    67  
    68  const BootstrapInstanceId = "localhost"
    69  
    70  var errNotPrepared = errors.New("model is not prepared")
    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.ModelTag.Id(),
    79  		"controller-uuid":           testing.ModelTag.Id(),
    80  		"authorized-keys":           testing.FakeAuthKeys,
    81  		"firewall-mode":             config.FwInstance,
    82  		"admin-secret":              testing.DefaultMongoPassword,
    83  		"ca-cert":                   testing.CACert,
    84  		"ca-private-key":            testing.CAKey,
    85  		"ssl-hostname-verification": true,
    86  		"development":               false,
    87  		"state-port":                1234,
    88  		"api-port":                  4321,
    89  		"default-series":            config.LatestLtsSeries(),
    90  
    91  		"secret":      "pork",
    92  		"controller":  true,
    93  		"prefer-ipv6": false,
    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  // stateInfo returns a *state.Info which allows clients to connect to the
   108  // shared dummy state, if it exists. If preferIPv6 is true, an IPv6 endpoint
   109  // will be added as primary.
   110  func stateInfo(preferIPv6 bool) *mongo.MongoInfo {
   111  	if gitjujutesting.MgoServer.Addr() == "" {
   112  		panic("dummy environ state tests must be run with MgoTestPackage")
   113  	}
   114  	mongoPort := strconv.Itoa(gitjujutesting.MgoServer.Port())
   115  	var addrs []string
   116  	if preferIPv6 {
   117  		addrs = []string{
   118  			net.JoinHostPort("::1", mongoPort),
   119  			net.JoinHostPort("localhost", mongoPort),
   120  		}
   121  	} else {
   122  		addrs = []string{net.JoinHostPort("localhost", mongoPort)}
   123  	}
   124  	return &mongo.MongoInfo{
   125  		Info: mongo.Info{
   126  			Addrs:  addrs,
   127  			CACert: testing.CACert,
   128  		},
   129  	}
   130  }
   131  
   132  // Operation represents an action on the dummy provider.
   133  type Operation interface{}
   134  
   135  type OpBootstrap struct {
   136  	Context environs.BootstrapContext
   137  	Env     string
   138  	Args    environs.BootstrapParams
   139  }
   140  
   141  type OpFinalizeBootstrap struct {
   142  	Context        environs.BootstrapContext
   143  	Env            string
   144  	InstanceConfig *instancecfg.InstanceConfig
   145  }
   146  
   147  type OpDestroy struct {
   148  	Env   string
   149  	Error error
   150  }
   151  
   152  type OpAllocateAddress struct {
   153  	Env        string
   154  	InstanceId instance.Id
   155  	SubnetId   network.Id
   156  	Address    network.Address
   157  	MACAddress string
   158  	HostName   string
   159  }
   160  
   161  type OpReleaseAddress struct {
   162  	Env        string
   163  	InstanceId instance.Id
   164  	SubnetId   network.Id
   165  	Address    network.Address
   166  	MACAddress string
   167  	HostName   string
   168  }
   169  
   170  type OpNetworkInterfaces struct {
   171  	Env        string
   172  	InstanceId instance.Id
   173  	Info       []network.InterfaceInfo
   174  }
   175  
   176  type OpSubnets struct {
   177  	Env        string
   178  	InstanceId instance.Id
   179  	SubnetIds  []network.Id
   180  	Info       []network.SubnetInfo
   181  }
   182  
   183  type OpStartInstance struct {
   184  	Env              string
   185  	MachineId        string
   186  	MachineNonce     string
   187  	PossibleTools    coretools.List
   188  	Instance         instance.Instance
   189  	Constraints      constraints.Value
   190  	SubnetsToZones   map[network.Id][]string
   191  	NetworkInfo      []network.InterfaceInfo
   192  	Volumes          []storage.Volume
   193  	Info             *mongo.MongoInfo
   194  	Jobs             []multiwatcher.MachineJob
   195  	APIInfo          *api.Info
   196  	Secret           string
   197  	AgentEnvironment map[string]string
   198  }
   199  
   200  type OpStopInstances struct {
   201  	Env string
   202  	Ids []instance.Id
   203  }
   204  
   205  type OpOpenPorts struct {
   206  	Env        string
   207  	MachineId  string
   208  	InstanceId instance.Id
   209  	Ports      []network.PortRange
   210  }
   211  
   212  type OpClosePorts struct {
   213  	Env        string
   214  	MachineId  string
   215  	InstanceId instance.Id
   216  	Ports      []network.PortRange
   217  }
   218  
   219  type OpPutFile struct {
   220  	Env      string
   221  	FileName string
   222  }
   223  
   224  // environProvider represents the dummy provider.  There is only ever one
   225  // instance of this type (dummy)
   226  type environProvider struct {
   227  	mu                     sync.Mutex
   228  	ops                    chan<- Operation
   229  	statePolicy            state.Policy
   230  	supportsSpaces         bool
   231  	supportsSpaceDiscovery bool
   232  	// We have one state for each prepared controller.
   233  	state map[string]*environState
   234  }
   235  
   236  // environState represents the state of an environment.
   237  // It can be shared between several environ values,
   238  // so that a given environment can be opened several times.
   239  type environState struct {
   240  	name            string
   241  	ops             chan<- Operation
   242  	statePolicy     state.Policy
   243  	mu              sync.Mutex
   244  	maxId           int // maximum instance id allocated so far.
   245  	maxAddr         int // maximum allocated address last byte
   246  	insts           map[instance.Id]*dummyInstance
   247  	globalPorts     map[network.PortRange]bool
   248  	bootstrapped    bool
   249  	apiListener     net.Listener
   250  	apiServer       *apiserver.Server
   251  	apiState        *state.State
   252  	apiStatePool    *state.StatePool
   253  	bootstrapConfig *config.Config
   254  	preferIPv6      bool
   255  }
   256  
   257  // environ represents a client's connection to a given environment's
   258  // state.
   259  type environ struct {
   260  	common.SupportsUnitPlacementPolicy
   261  
   262  	name         string
   263  	ecfgMutex    sync.Mutex
   264  	ecfgUnlocked *environConfig
   265  	spacesMutex  sync.RWMutex
   266  }
   267  
   268  // discardOperations discards all Operations written to it.
   269  var discardOperations = make(chan Operation)
   270  
   271  func init() {
   272  	environs.RegisterProvider("dummy", &dummy)
   273  
   274  	// Prime the first ops channel, so that naive clients can use
   275  	// the testing environment by simply importing it.
   276  	go func() {
   277  		for _ = range discardOperations {
   278  		}
   279  	}()
   280  }
   281  
   282  // dummy is the dummy environmentProvider singleton.
   283  var dummy = environProvider{
   284  	ops:                    discardOperations,
   285  	state:                  make(map[string]*environState),
   286  	statePolicy:            environs.NewStatePolicy(),
   287  	supportsSpaces:         true,
   288  	supportsSpaceDiscovery: false,
   289  }
   290  
   291  // Reset resets the entire dummy environment and forgets any registered
   292  // operation listener. All opened environments after Reset will share
   293  // the same underlying state.
   294  func Reset(c *gc.C) {
   295  	logger.Infof("reset model")
   296  	dummy.mu.Lock()
   297  	defer dummy.mu.Unlock()
   298  	dummy.ops = discardOperations
   299  	for _, s := range dummy.state {
   300  		if s.apiListener != nil {
   301  			s.apiListener.Close()
   302  		}
   303  		s.destroy()
   304  	}
   305  	dummy.state = make(map[string]*environState)
   306  	if mongoAlive() {
   307  		err := gitjujutesting.MgoServer.Reset()
   308  		c.Assert(err, jc.ErrorIsNil)
   309  	}
   310  	dummy.statePolicy = environs.NewStatePolicy()
   311  	dummy.supportsSpaces = true
   312  	dummy.supportsSpaceDiscovery = false
   313  }
   314  
   315  func (state *environState) destroy() {
   316  	if !state.bootstrapped {
   317  		return
   318  	}
   319  
   320  	if state.apiServer != nil {
   321  		if err := state.apiServer.Stop(); err != nil && mongoAlive() {
   322  			panic(err)
   323  		}
   324  		state.apiServer = nil
   325  	}
   326  
   327  	if state.apiStatePool != nil {
   328  		if err := state.apiStatePool.Close(); err != nil && mongoAlive() {
   329  			panic(err)
   330  		}
   331  		state.apiStatePool = nil
   332  	}
   333  
   334  	if state.apiState != nil {
   335  		if err := state.apiState.Close(); err != nil && mongoAlive() {
   336  			panic(err)
   337  		}
   338  		state.apiState = nil
   339  	}
   340  
   341  	if mongoAlive() {
   342  		gitjujutesting.MgoServer.Reset()
   343  	}
   344  	state.bootstrapped = false
   345  }
   346  
   347  // mongoAlive reports whether the mongo server is
   348  // still alive (i.e. has not been deliberately destroyed).
   349  // If it has been deliberately destroyed, we will
   350  // expect some errors when closing things down.
   351  func mongoAlive() bool {
   352  	return gitjujutesting.MgoServer.Addr() != ""
   353  }
   354  
   355  // GetStateInAPIServer returns the state connection used by the API server
   356  // This is so code in the test suite can trigger Syncs, etc that the API server
   357  // will see, which will then trigger API watchers, etc.
   358  func (e *environ) GetStateInAPIServer() *state.State {
   359  	st, err := e.state()
   360  	if err != nil {
   361  		panic(err)
   362  	}
   363  	return st.apiState
   364  }
   365  
   366  // GetStatePoolInAPIServer returns the StatePool used by the API
   367  // server.  As for GetStatePoolInAPIServer, this is so code in the
   368  // test suite can trigger Syncs etc.
   369  func (e *environ) GetStatePoolInAPIServer() *state.StatePool {
   370  	st, err := e.state()
   371  	if err != nil {
   372  		panic(err)
   373  	}
   374  	return st.apiStatePool
   375  }
   376  
   377  // newState creates the state for a new environment with the given name.
   378  func newState(name string, ops chan<- Operation, policy state.Policy) *environState {
   379  	s := &environState{
   380  		name:        name,
   381  		ops:         ops,
   382  		statePolicy: policy,
   383  		insts:       make(map[instance.Id]*dummyInstance),
   384  		globalPorts: make(map[network.PortRange]bool),
   385  	}
   386  	return s
   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  // controller is initialised by dummy.
   402  func SetStatePolicy(policy state.Policy) {
   403  	dummy.mu.Lock()
   404  	dummy.statePolicy = policy
   405  	dummy.mu.Unlock()
   406  }
   407  
   408  // SetSupportsSpaces allows to enable and disable SupportsSpaces for tests.
   409  func SetSupportsSpaces(supports bool) bool {
   410  	dummy.mu.Lock()
   411  	defer dummy.mu.Unlock()
   412  	current := dummy.supportsSpaces
   413  	dummy.supportsSpaces = supports
   414  	return current
   415  }
   416  
   417  // SetSupportsSpaceDiscovery allows to enable and disable
   418  // SupportsSpaceDiscovery for tests.
   419  func SetSupportsSpaceDiscovery(supports bool) bool {
   420  	dummy.mu.Lock()
   421  	defer dummy.mu.Unlock()
   422  	current := dummy.supportsSpaceDiscovery
   423  	dummy.supportsSpaceDiscovery = supports
   424  	return current
   425  }
   426  
   427  // Listen closes the previously registered listener (if any).
   428  // Subsequent operations on any dummy environment can be received on c
   429  // (if not nil).
   430  func Listen(c chan<- Operation) {
   431  	dummy.mu.Lock()
   432  	defer dummy.mu.Unlock()
   433  	if c == nil {
   434  		c = discardOperations
   435  	}
   436  	if dummy.ops != discardOperations {
   437  		close(dummy.ops)
   438  	}
   439  	dummy.ops = c
   440  	for _, st := range dummy.state {
   441  		st.mu.Lock()
   442  		st.ops = c
   443  		st.mu.Unlock()
   444  	}
   445  }
   446  
   447  var configSchema = environschema.Fields{
   448  	"controller": {
   449  		Description: "Whether the model should start a controller",
   450  		Type:        environschema.Tbool,
   451  	},
   452  	"broken": {
   453  		Description: "Whitespace-separated Environ methods that should return an error when called",
   454  		Type:        environschema.Tstring,
   455  	},
   456  	"secret": {
   457  		Description: "A secret",
   458  		Type:        environschema.Tstring,
   459  	},
   460  }
   461  
   462  var configFields = func() schema.Fields {
   463  	fs, _, err := configSchema.ValidationSchema()
   464  	if err != nil {
   465  		panic(err)
   466  	}
   467  	return fs
   468  }()
   469  
   470  var configDefaults = schema.Defaults{
   471  	"broken":     "",
   472  	"secret":     "pork",
   473  	"controller": false,
   474  }
   475  
   476  type environConfig struct {
   477  	*config.Config
   478  	attrs map[string]interface{}
   479  }
   480  
   481  func (c *environConfig) controller() bool {
   482  	return c.attrs["controller"].(bool)
   483  }
   484  
   485  func (c *environConfig) broken() string {
   486  	return c.attrs["broken"].(string)
   487  }
   488  
   489  func (c *environConfig) secret() string {
   490  	return c.attrs["secret"].(string)
   491  }
   492  
   493  func (p *environProvider) newConfig(cfg *config.Config) (*environConfig, error) {
   494  	valid, err := p.Validate(cfg, nil)
   495  	if err != nil {
   496  		return nil, err
   497  	}
   498  	return &environConfig{valid, valid.UnknownAttrs()}, nil
   499  }
   500  
   501  func (p *environProvider) Schema() environschema.Fields {
   502  	fields, err := config.Schema(configSchema)
   503  	if err != nil {
   504  		panic(err)
   505  	}
   506  	return fields
   507  }
   508  
   509  func (p *environProvider) CredentialSchemas() map[cloud.AuthType]cloud.CredentialSchema {
   510  	return map[cloud.AuthType]cloud.CredentialSchema{cloud.EmptyAuthType: {}}
   511  }
   512  
   513  func (*environProvider) DetectCredentials() (*cloud.CloudCredential, error) {
   514  	return cloud.NewEmptyCloudCredential(), nil
   515  }
   516  
   517  func (*environProvider) DetectRegions() ([]cloud.Region, error) {
   518  	return []cloud.Region{{Name: "dummy"}}, nil
   519  }
   520  
   521  func (p *environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) {
   522  	// Check for valid changes for the base config values.
   523  	if err := config.Validate(cfg, old); err != nil {
   524  		return nil, err
   525  	}
   526  	validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults)
   527  	if err != nil {
   528  		return nil, err
   529  	}
   530  	// Apply the coerced unknown values back into the config.
   531  	return cfg.Apply(validated)
   532  }
   533  
   534  func (e *environ) state() (*environState, error) {
   535  	dummy.mu.Lock()
   536  	defer dummy.mu.Unlock()
   537  	state, ok := dummy.state[e.Config().ControllerUUID()]
   538  	if !ok {
   539  		return nil, errNotPrepared
   540  	}
   541  	return state, nil
   542  }
   543  
   544  func (p *environProvider) Open(cfg *config.Config) (environs.Environ, error) {
   545  	p.mu.Lock()
   546  	defer p.mu.Unlock()
   547  	ecfg, err := p.newConfig(cfg)
   548  	if err != nil {
   549  		return nil, err
   550  	}
   551  	if _, ok := p.state[cfg.ControllerUUID()]; !ok {
   552  		return nil, errNotPrepared
   553  	}
   554  	env := &environ{
   555  		name:         ecfg.Name(),
   556  		ecfgUnlocked: ecfg,
   557  	}
   558  	if err := env.checkBroken("Open"); err != nil {
   559  		return nil, err
   560  	}
   561  	return env, nil
   562  }
   563  
   564  // RestrictedConfigAttributes is specified in the EnvironProvider interface.
   565  func (p *environProvider) RestrictedConfigAttributes() []string {
   566  	return nil
   567  }
   568  
   569  // PrepareForCreateEnvironment is specified in the EnvironProvider interface.
   570  func (p *environProvider) PrepareForCreateEnvironment(cfg *config.Config) (*config.Config, error) {
   571  	// NOTE: this check might appear redundant, but it's not: some tests
   572  	// (apiserver/modelmanager) inject a string value and determine that
   573  	// the config is validated later; validating here would render that
   574  	// test meaningless.
   575  	if cfg.AllAttrs()["controller"] == true {
   576  		// NOTE: cfg.Apply *does* validate, but we're only adding a
   577  		// valid value so it doesn't matter.
   578  		return cfg.Apply(map[string]interface{}{
   579  			"controller": false,
   580  		})
   581  	}
   582  	return cfg, nil
   583  }
   584  
   585  // PrepareForBootstrap is specified in the EnvironProvider interface.
   586  func (p *environProvider) PrepareForBootstrap(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) {
   587  	return p.Open(cfg)
   588  }
   589  
   590  // BootstrapConfig is specified in the EnvironProvider interface.
   591  func (p *environProvider) BootstrapConfig(args environs.BootstrapConfigParams) (*config.Config, error) {
   592  	ecfg, err := p.newConfig(args.Config)
   593  	if err != nil {
   594  		return nil, err
   595  	}
   596  	p.mu.Lock()
   597  	defer p.mu.Unlock()
   598  
   599  	envState, ok := p.state[args.Config.ControllerUUID()]
   600  	if ok {
   601  		// BootstrapConfig is expected to return the same result given
   602  		// the same input. We assume that the args are the same for a
   603  		// previously prepared/bootstrapped controller.
   604  		return envState.bootstrapConfig, nil
   605  	}
   606  
   607  	name := args.Config.Name()
   608  	if ecfg.controller() && len(p.state) != 0 {
   609  		for _, old := range p.state {
   610  			panic(fmt.Errorf("cannot share a state between two dummy environs; old %q; new %q", old.name, name))
   611  		}
   612  	}
   613  
   614  	// The environment has not been prepared, so create it and record it.
   615  	// We don't start listening for State or API connections until
   616  	// PrepareForBootstrapConfig has been called.
   617  	envState = newState(name, p.ops, p.statePolicy)
   618  	cfg := args.Config
   619  	if ecfg.controller() {
   620  		apiPort := envState.listenAPI()
   621  		cfg, err = cfg.Apply(map[string]interface{}{
   622  			"api-port": apiPort,
   623  		})
   624  		if err != nil {
   625  			return nil, err
   626  		}
   627  	}
   628  	envState.bootstrapConfig = cfg
   629  	p.state[cfg.ControllerUUID()] = envState
   630  	return cfg, nil
   631  }
   632  
   633  func (*environProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) {
   634  	ecfg, err := dummy.newConfig(cfg)
   635  	if err != nil {
   636  		return nil, err
   637  	}
   638  	return map[string]string{
   639  		"secret": ecfg.secret(),
   640  	}, nil
   641  }
   642  
   643  // Override for testing - the data directory with which the state api server is initialised.
   644  var DataDir = ""
   645  var LogDir = ""
   646  
   647  func (e *environ) ecfg() *environConfig {
   648  	e.ecfgMutex.Lock()
   649  	ecfg := e.ecfgUnlocked
   650  	e.ecfgMutex.Unlock()
   651  	return ecfg
   652  }
   653  
   654  func (e *environ) checkBroken(method string) error {
   655  	for _, m := range strings.Fields(e.ecfg().broken()) {
   656  		if m == method {
   657  			return fmt.Errorf("dummy.%s is broken", method)
   658  		}
   659  	}
   660  	return nil
   661  }
   662  
   663  // SupportedArchitectures is specified on the EnvironCapability interface.
   664  func (*environ) SupportedArchitectures() ([]string, error) {
   665  	return []string{arch.AMD64, arch.I386, arch.PPC64EL, arch.ARM64}, nil
   666  }
   667  
   668  // PrecheckInstance is specified in the state.Prechecker interface.
   669  func (*environ) PrecheckInstance(series string, cons constraints.Value, placement string) error {
   670  	if placement != "" && placement != "valid" {
   671  		return fmt.Errorf("%s placement is invalid", placement)
   672  	}
   673  	return nil
   674  }
   675  
   676  func (e *environ) Bootstrap(ctx environs.BootstrapContext, args environs.BootstrapParams) (*environs.BootstrapResult, error) {
   677  	series := config.PreferredSeries(e.Config())
   678  	availableTools, err := args.AvailableTools.Match(coretools.Filter{Series: series})
   679  	if err != nil {
   680  		return nil, err
   681  	}
   682  	arch := availableTools.Arches()[0]
   683  
   684  	defer delay()
   685  	if err := e.checkBroken("Bootstrap"); err != nil {
   686  		return nil, err
   687  	}
   688  	network.SetPreferIPv6(e.Config().PreferIPv6())
   689  	password := e.Config().AdminSecret()
   690  	if password == "" {
   691  		return nil, fmt.Errorf("admin-secret is required for bootstrap")
   692  	}
   693  	if _, ok := e.Config().CACert(); !ok {
   694  		return nil, fmt.Errorf("no CA certificate in model configuration")
   695  	}
   696  
   697  	logger.Infof("would pick tools from %s", availableTools)
   698  	cfg, err := environs.BootstrapConfig(e.Config())
   699  	if err != nil {
   700  		return nil, fmt.Errorf("cannot make bootstrap config: %v", err)
   701  	}
   702  
   703  	estate, err := e.state()
   704  	if err != nil {
   705  		return nil, err
   706  	}
   707  	estate.mu.Lock()
   708  	defer estate.mu.Unlock()
   709  	if estate.bootstrapped {
   710  		return nil, fmt.Errorf("model is already bootstrapped")
   711  	}
   712  	estate.preferIPv6 = e.Config().PreferIPv6()
   713  
   714  	// Create an instance for the bootstrap node.
   715  	logger.Infof("creating bootstrap instance")
   716  	i := &dummyInstance{
   717  		id:           BootstrapInstanceId,
   718  		addresses:    network.NewAddresses("localhost"),
   719  		ports:        make(map[network.PortRange]bool),
   720  		machineId:    agent.BootstrapMachineId,
   721  		series:       series,
   722  		firewallMode: e.Config().FirewallMode(),
   723  		state:        estate,
   724  		controller:   true,
   725  	}
   726  	estate.insts[i.id] = i
   727  
   728  	if e.ecfg().controller() {
   729  		// TODO(rog) factor out relevant code from cmd/jujud/bootstrap.go
   730  		// so that we can call it here.
   731  
   732  		info := stateInfo(estate.preferIPv6)
   733  		// Since the admin user isn't setup until after here,
   734  		// the password in the info structure is empty, so the admin
   735  		// user is constructed with an empty password here.
   736  		// It is set just below.
   737  		st, err := state.Initialize(
   738  			names.NewUserTag("admin@local"), info, cfg,
   739  			mongo.DefaultDialOpts(), estate.statePolicy)
   740  		if err != nil {
   741  			panic(err)
   742  		}
   743  		if err := st.SetModelConstraints(args.ModelConstraints); err != nil {
   744  			panic(err)
   745  		}
   746  		if err := st.SetAdminMongoPassword(password); err != nil {
   747  			panic(err)
   748  		}
   749  		if err := st.MongoSession().DB("admin").Login("admin", password); err != nil {
   750  			panic(err)
   751  		}
   752  		env, err := st.Model()
   753  		if err != nil {
   754  			panic(err)
   755  		}
   756  		owner, err := st.User(env.Owner())
   757  		if err != nil {
   758  			panic(err)
   759  		}
   760  		// We log this out for test purposes only. No one in real life can use
   761  		// a dummy provider for anything other than testing, so logging the password
   762  		// here is fine.
   763  		logger.Debugf("setting password for %q to %q", owner.Name(), password)
   764  		owner.SetPassword(password)
   765  
   766  		estate.apiStatePool = state.NewStatePool(st)
   767  
   768  		estate.apiServer, err = apiserver.NewServer(st, estate.apiListener, apiserver.ServerConfig{
   769  			Cert:      []byte(testing.ServerCert),
   770  			Key:       []byte(testing.ServerKey),
   771  			Tag:       names.NewMachineTag("0"),
   772  			DataDir:   DataDir,
   773  			LogDir:    LogDir,
   774  			StatePool: estate.apiStatePool,
   775  		})
   776  		if err != nil {
   777  			panic(err)
   778  		}
   779  		estate.apiState = st
   780  	}
   781  	estate.bootstrapped = true
   782  	estate.ops <- OpBootstrap{Context: ctx, Env: e.name, Args: args}
   783  	finalize := func(ctx environs.BootstrapContext, icfg *instancecfg.InstanceConfig) error {
   784  		estate.ops <- OpFinalizeBootstrap{Context: ctx, Env: e.name, InstanceConfig: icfg}
   785  		return nil
   786  	}
   787  
   788  	bsResult := &environs.BootstrapResult{
   789  		Arch:     arch,
   790  		Series:   series,
   791  		Finalize: finalize,
   792  	}
   793  	return bsResult, nil
   794  }
   795  
   796  func (e *environ) ControllerInstances() ([]instance.Id, error) {
   797  	estate, err := e.state()
   798  	if err != nil {
   799  		return nil, err
   800  	}
   801  	estate.mu.Lock()
   802  	defer estate.mu.Unlock()
   803  	if err := e.checkBroken("ControllerInstances"); err != nil {
   804  		return nil, err
   805  	}
   806  	if !estate.bootstrapped {
   807  		return nil, environs.ErrNotBootstrapped
   808  	}
   809  	var controllerInstances []instance.Id
   810  	for _, v := range estate.insts {
   811  		if v.controller {
   812  			controllerInstances = append(controllerInstances, v.Id())
   813  		}
   814  	}
   815  	return controllerInstances, nil
   816  }
   817  
   818  func (e *environ) Config() *config.Config {
   819  	return e.ecfg().Config
   820  }
   821  
   822  func (e *environ) SetConfig(cfg *config.Config) error {
   823  	if err := e.checkBroken("SetConfig"); err != nil {
   824  		return err
   825  	}
   826  	ecfg, err := dummy.newConfig(cfg)
   827  	if err != nil {
   828  		return err
   829  	}
   830  	e.ecfgMutex.Lock()
   831  	e.ecfgUnlocked = ecfg
   832  	e.ecfgMutex.Unlock()
   833  	return nil
   834  }
   835  
   836  func (e *environ) Destroy() (res error) {
   837  	defer delay()
   838  	estate, err := e.state()
   839  	if err != nil {
   840  		if err == errNotPrepared {
   841  			return nil
   842  		}
   843  		return err
   844  	}
   845  	defer func() { estate.ops <- OpDestroy{Env: estate.name, Error: res} }()
   846  	if err := e.checkBroken("Destroy"); err != nil {
   847  		return err
   848  	}
   849  	if !e.ecfg().controller() {
   850  		return nil
   851  	}
   852  	dummy.mu.Lock()
   853  	delete(dummy.state, estate.bootstrapConfig.ControllerUUID())
   854  	dummy.mu.Unlock()
   855  
   856  	estate.mu.Lock()
   857  	defer estate.mu.Unlock()
   858  	estate.destroy()
   859  	return nil
   860  }
   861  
   862  // ConstraintsValidator is defined on the Environs interface.
   863  func (e *environ) ConstraintsValidator() (constraints.Validator, error) {
   864  	validator := constraints.NewValidator()
   865  	validator.RegisterUnsupported([]string{constraints.CpuPower, constraints.VirtType})
   866  	validator.RegisterConflicts([]string{constraints.InstanceType}, []string{constraints.Mem})
   867  	return validator, nil
   868  }
   869  
   870  // MaintainInstance is specified in the InstanceBroker interface.
   871  func (*environ) MaintainInstance(args environs.StartInstanceParams) error {
   872  	return nil
   873  }
   874  
   875  // StartInstance is specified in the InstanceBroker interface.
   876  func (e *environ) StartInstance(args environs.StartInstanceParams) (*environs.StartInstanceResult, error) {
   877  
   878  	defer delay()
   879  	machineId := args.InstanceConfig.MachineId
   880  	logger.Infof("dummy startinstance, machine %s", machineId)
   881  	if err := e.checkBroken("StartInstance"); err != nil {
   882  		return nil, err
   883  	}
   884  	estate, err := e.state()
   885  	if err != nil {
   886  		return nil, err
   887  	}
   888  	estate.mu.Lock()
   889  	defer estate.mu.Unlock()
   890  
   891  	// check if an error has been injected on the transientErrorInjection channel (testing purposes)
   892  	select {
   893  	case injectedError := <-transientErrorInjection:
   894  		return nil, injectedError
   895  	default:
   896  	}
   897  
   898  	if args.InstanceConfig.MachineNonce == "" {
   899  		return nil, errors.New("cannot start instance: missing machine nonce")
   900  	}
   901  	if _, ok := e.Config().CACert(); !ok {
   902  		return nil, errors.New("no CA certificate in model configuration")
   903  	}
   904  	if args.InstanceConfig.MongoInfo.Tag != names.NewMachineTag(machineId) {
   905  		return nil, errors.New("entity tag must match started machine")
   906  	}
   907  	if args.InstanceConfig.APIInfo.Tag != names.NewMachineTag(machineId) {
   908  		return nil, errors.New("entity tag must match started machine")
   909  	}
   910  	logger.Infof("would pick tools from %s", args.Tools)
   911  	series := args.Tools.OneSeries()
   912  
   913  	idString := fmt.Sprintf("%s-%d", e.name, estate.maxId)
   914  	addrs := network.NewAddresses(idString+".dns", "127.0.0.1")
   915  	if estate.preferIPv6 {
   916  		addrs = append(addrs, network.NewAddress(fmt.Sprintf("fc00::%x", estate.maxId+1)))
   917  	}
   918  	logger.Debugf("StartInstance addresses: %v", addrs)
   919  	i := &dummyInstance{
   920  		id:           instance.Id(idString),
   921  		addresses:    addrs,
   922  		ports:        make(map[network.PortRange]bool),
   923  		machineId:    machineId,
   924  		series:       series,
   925  		firewallMode: e.Config().FirewallMode(),
   926  		state:        estate,
   927  	}
   928  
   929  	var hc *instance.HardwareCharacteristics
   930  	// To match current system capability, only provide hardware characteristics for
   931  	// environ machines, not containers.
   932  	if state.ParentId(machineId) == "" {
   933  		// We will just assume the instance hardware characteristics exactly matches
   934  		// the supplied constraints (if specified).
   935  		hc = &instance.HardwareCharacteristics{
   936  			Arch:     args.Constraints.Arch,
   937  			Mem:      args.Constraints.Mem,
   938  			RootDisk: args.Constraints.RootDisk,
   939  			CpuCores: args.Constraints.CpuCores,
   940  			CpuPower: args.Constraints.CpuPower,
   941  			Tags:     args.Constraints.Tags,
   942  		}
   943  		// Fill in some expected instance hardware characteristics if constraints not specified.
   944  		if hc.Arch == nil {
   945  			arch := "amd64"
   946  			hc.Arch = &arch
   947  		}
   948  		if hc.Mem == nil {
   949  			mem := uint64(1024)
   950  			hc.Mem = &mem
   951  		}
   952  		if hc.RootDisk == nil {
   953  			disk := uint64(8192)
   954  			hc.RootDisk = &disk
   955  		}
   956  		if hc.CpuCores == nil {
   957  			cores := uint64(1)
   958  			hc.CpuCores = &cores
   959  		}
   960  	}
   961  	// Simulate subnetsToZones gets populated when spaces given in constraints.
   962  	spaces := args.Constraints.IncludeSpaces()
   963  	var subnetsToZones map[network.Id][]string
   964  	for isp := range spaces {
   965  		// Simulate 2 subnets per space.
   966  		if subnetsToZones == nil {
   967  			subnetsToZones = make(map[network.Id][]string)
   968  		}
   969  		for isn := 0; isn < 2; isn++ {
   970  			providerId := fmt.Sprintf("subnet-%d", isp+isn)
   971  			zone := fmt.Sprintf("zone%d", isp+isn)
   972  			subnetsToZones[network.Id(providerId)] = []string{zone}
   973  		}
   974  	}
   975  	// Simulate creating volumes when requested.
   976  	volumes := make([]storage.Volume, len(args.Volumes))
   977  	for iv, v := range args.Volumes {
   978  		persistent, _ := v.Attributes["persistent"].(bool)
   979  		volumes[iv] = storage.Volume{
   980  			Tag: names.NewVolumeTag(strconv.Itoa(iv + 1)),
   981  			VolumeInfo: storage.VolumeInfo{
   982  				Size:       v.Size,
   983  				Persistent: persistent,
   984  			},
   985  		}
   986  	}
   987  	estate.insts[i.id] = i
   988  	estate.maxId++
   989  	estate.ops <- OpStartInstance{
   990  		Env:              e.name,
   991  		MachineId:        machineId,
   992  		MachineNonce:     args.InstanceConfig.MachineNonce,
   993  		PossibleTools:    args.Tools,
   994  		Constraints:      args.Constraints,
   995  		SubnetsToZones:   subnetsToZones,
   996  		Volumes:          volumes,
   997  		Instance:         i,
   998  		Jobs:             args.InstanceConfig.Jobs,
   999  		Info:             args.InstanceConfig.MongoInfo,
  1000  		APIInfo:          args.InstanceConfig.APIInfo,
  1001  		AgentEnvironment: args.InstanceConfig.AgentEnvironment,
  1002  		Secret:           e.ecfg().secret(),
  1003  	}
  1004  	return &environs.StartInstanceResult{
  1005  		Instance: i,
  1006  		Hardware: hc,
  1007  	}, nil
  1008  }
  1009  
  1010  func (e *environ) StopInstances(ids ...instance.Id) error {
  1011  	defer delay()
  1012  	if err := e.checkBroken("StopInstance"); err != nil {
  1013  		return err
  1014  	}
  1015  	estate, err := e.state()
  1016  	if err != nil {
  1017  		return err
  1018  	}
  1019  	estate.mu.Lock()
  1020  	defer estate.mu.Unlock()
  1021  	for _, id := range ids {
  1022  		delete(estate.insts, id)
  1023  	}
  1024  	estate.ops <- OpStopInstances{
  1025  		Env: e.name,
  1026  		Ids: ids,
  1027  	}
  1028  	return nil
  1029  }
  1030  
  1031  func (e *environ) Instances(ids []instance.Id) (insts []instance.Instance, err error) {
  1032  	defer delay()
  1033  	if err := e.checkBroken("Instances"); err != nil {
  1034  		return nil, err
  1035  	}
  1036  	if len(ids) == 0 {
  1037  		return nil, nil
  1038  	}
  1039  	estate, err := e.state()
  1040  	if err != nil {
  1041  		return nil, err
  1042  	}
  1043  	estate.mu.Lock()
  1044  	defer estate.mu.Unlock()
  1045  	notFound := 0
  1046  	for _, id := range ids {
  1047  		inst := estate.insts[id]
  1048  		if inst == nil {
  1049  			err = environs.ErrPartialInstances
  1050  			notFound++
  1051  			insts = append(insts, nil)
  1052  		} else {
  1053  			insts = append(insts, inst)
  1054  		}
  1055  	}
  1056  	if notFound == len(ids) {
  1057  		return nil, environs.ErrNoInstances
  1058  	}
  1059  	return
  1060  }
  1061  
  1062  // SupportsSpaces is specified on environs.Networking.
  1063  func (env *environ) SupportsSpaces() (bool, error) {
  1064  	dummy.mu.Lock()
  1065  	defer dummy.mu.Unlock()
  1066  	if !dummy.supportsSpaces {
  1067  		return false, errors.NotSupportedf("spaces")
  1068  	}
  1069  	return true, nil
  1070  }
  1071  
  1072  // SupportsSpaceDiscovery is specified on environs.Networking.
  1073  func (env *environ) SupportsSpaceDiscovery() (bool, error) {
  1074  	if err := env.checkBroken("SupportsSpaceDiscovery"); err != nil {
  1075  		return false, err
  1076  	}
  1077  	dummy.mu.Lock()
  1078  	defer dummy.mu.Unlock()
  1079  	if !dummy.supportsSpaceDiscovery {
  1080  		return false, nil
  1081  	}
  1082  	return true, nil
  1083  }
  1084  
  1085  // Spaces is specified on environs.Networking.
  1086  func (env *environ) Spaces() ([]network.SpaceInfo, error) {
  1087  	if err := env.checkBroken("Spaces"); err != nil {
  1088  		return []network.SpaceInfo{}, err
  1089  	}
  1090  	return []network.SpaceInfo{{
  1091  		Name:       "foo",
  1092  		ProviderId: network.Id("0"),
  1093  		Subnets: []network.SubnetInfo{{
  1094  			ProviderId:        network.Id("1"),
  1095  			AvailabilityZones: []string{"zone1"},
  1096  		}, {
  1097  			ProviderId:        network.Id("2"),
  1098  			AvailabilityZones: []string{"zone1"},
  1099  		}}}, {
  1100  		Name:       "Another Foo 99!",
  1101  		ProviderId: "1",
  1102  		Subnets: []network.SubnetInfo{{
  1103  			ProviderId:        network.Id("3"),
  1104  			AvailabilityZones: []string{"zone1"},
  1105  		}}}, {
  1106  		Name:       "foo-",
  1107  		ProviderId: "2",
  1108  		Subnets: []network.SubnetInfo{{
  1109  			ProviderId:        network.Id("4"),
  1110  			AvailabilityZones: []string{"zone1"},
  1111  		}}}, {
  1112  		Name:       "---",
  1113  		ProviderId: "3",
  1114  		Subnets: []network.SubnetInfo{{
  1115  			ProviderId:        network.Id("5"),
  1116  			AvailabilityZones: []string{"zone1"},
  1117  		}}}}, nil
  1118  }
  1119  
  1120  // SupportsAddressAllocation is specified on environs.Networking.
  1121  func (env *environ) SupportsAddressAllocation(subnetId network.Id) (bool, error) {
  1122  	if !environs.AddressAllocationEnabled("dummy") {
  1123  		return false, errors.NotSupportedf("address allocation")
  1124  	}
  1125  
  1126  	if err := env.checkBroken("SupportsAddressAllocation"); err != nil {
  1127  		return false, err
  1128  	}
  1129  	// Any subnetId starting with "noalloc-" will cause this to return
  1130  	// false, so it can be used in tests.
  1131  	if strings.HasPrefix(string(subnetId), "noalloc-") {
  1132  		return false, nil
  1133  	}
  1134  	return true, nil
  1135  }
  1136  
  1137  // AllocateAddress requests an address to be allocated for the
  1138  // given instance on the given subnet.
  1139  func (env *environ) AllocateAddress(instId instance.Id, subnetId network.Id, addr *network.Address, macAddress, hostname string) error {
  1140  	if !environs.AddressAllocationEnabled("dummy") {
  1141  		// Any instId starting with "i-alloc-" when the feature flag is off will
  1142  		// still work, in order to be able to test MAAS 1.8+ environment where
  1143  		// we can use devices for containers.
  1144  		if !strings.HasPrefix(string(instId), "i-alloc-") {
  1145  			return errors.NotSupportedf("address allocation")
  1146  		}
  1147  		// Also, in this case we expect addr to be non-nil, but empty, so it can
  1148  		// be used as an output argument (same as in provider/maas).
  1149  		if addr == nil || addr.Value != "" {
  1150  			return errors.NewNotValid(nil, "invalid address: nil or non-empty")
  1151  		}
  1152  	}
  1153  
  1154  	if err := env.checkBroken("AllocateAddress"); err != nil {
  1155  		return err
  1156  	}
  1157  
  1158  	estate, err := env.state()
  1159  	if err != nil {
  1160  		return err
  1161  	}
  1162  	estate.mu.Lock()
  1163  	defer estate.mu.Unlock()
  1164  	estate.maxAddr++
  1165  
  1166  	if addr.Value == "" {
  1167  		*addr = network.NewAddress(fmt.Sprintf("0.10.0.%v", estate.maxAddr))
  1168  	}
  1169  
  1170  	estate.ops <- OpAllocateAddress{
  1171  		Env:        env.name,
  1172  		InstanceId: instId,
  1173  		SubnetId:   subnetId,
  1174  		Address:    *addr,
  1175  		MACAddress: macAddress,
  1176  		HostName:   hostname,
  1177  	}
  1178  	return nil
  1179  }
  1180  
  1181  // ReleaseAddress releases a specific address previously allocated with
  1182  // AllocateAddress.
  1183  func (env *environ) ReleaseAddress(instId instance.Id, subnetId network.Id, addr network.Address, macAddress, hostname string) error {
  1184  	if !environs.AddressAllocationEnabled("dummy") {
  1185  		return errors.NotSupportedf("address allocation")
  1186  	}
  1187  
  1188  	if err := env.checkBroken("ReleaseAddress"); err != nil {
  1189  		return err
  1190  	}
  1191  	estate, err := env.state()
  1192  	if err != nil {
  1193  		return err
  1194  	}
  1195  	estate.mu.Lock()
  1196  	defer estate.mu.Unlock()
  1197  	estate.maxAddr--
  1198  	estate.ops <- OpReleaseAddress{
  1199  		Env:        env.name,
  1200  		InstanceId: instId,
  1201  		SubnetId:   subnetId,
  1202  		Address:    addr,
  1203  		MACAddress: macAddress,
  1204  		HostName:   hostname,
  1205  	}
  1206  	return nil
  1207  }
  1208  
  1209  // NetworkInterfaces implements Environ.NetworkInterfaces().
  1210  func (env *environ) NetworkInterfaces(instId instance.Id) ([]network.InterfaceInfo, error) {
  1211  	if err := env.checkBroken("NetworkInterfaces"); err != nil {
  1212  		return nil, err
  1213  	}
  1214  
  1215  	estate, err := env.state()
  1216  	if err != nil {
  1217  		return nil, err
  1218  	}
  1219  	estate.mu.Lock()
  1220  	defer estate.mu.Unlock()
  1221  
  1222  	// Simulate 3 NICs - primary and secondary enabled plus a disabled NIC.
  1223  	// all configured using DHCP and having fake DNS servers and gateway.
  1224  	info := make([]network.InterfaceInfo, 3)
  1225  	for i, netName := range []string{"private", "public", "disabled"} {
  1226  		info[i] = network.InterfaceInfo{
  1227  			DeviceIndex:      i,
  1228  			ProviderId:       network.Id(fmt.Sprintf("dummy-eth%d", i)),
  1229  			ProviderSubnetId: network.Id("dummy-" + netName),
  1230  			InterfaceType:    network.EthernetInterface,
  1231  			CIDR:             fmt.Sprintf("0.%d.0.0/24", (i+1)*10),
  1232  			InterfaceName:    fmt.Sprintf("eth%d", i),
  1233  			VLANTag:          i,
  1234  			MACAddress:       fmt.Sprintf("aa:bb:cc:dd:ee:f%d", i),
  1235  			Disabled:         i == 2,
  1236  			NoAutoStart:      i%2 != 0,
  1237  			ConfigType:       network.ConfigDHCP,
  1238  			Address: network.NewAddress(
  1239  				fmt.Sprintf("0.%d.0.%d", (i+1)*10, estate.maxAddr+2),
  1240  			),
  1241  			DNSServers: network.NewAddresses("ns1.dummy", "ns2.dummy"),
  1242  			GatewayAddress: network.NewAddress(
  1243  				fmt.Sprintf("0.%d.0.1", (i+1)*10),
  1244  			),
  1245  		}
  1246  	}
  1247  
  1248  	if strings.HasPrefix(string(instId), "i-no-nics-") {
  1249  		// Simulate no NICs on instances with id prefix "i-no-nics-".
  1250  		info = info[:0]
  1251  	} else if strings.HasPrefix(string(instId), "i-nic-no-subnet-") {
  1252  		// Simulate a nic with no subnet on instances with id prefix
  1253  		// "i-nic-no-subnet-"
  1254  		info = []network.InterfaceInfo{{
  1255  			DeviceIndex:   0,
  1256  			ProviderId:    network.Id("dummy-eth0"),
  1257  			InterfaceName: "eth0",
  1258  			MACAddress:    "aa:bb:cc:dd:ee:f0",
  1259  			Disabled:      false,
  1260  			NoAutoStart:   false,
  1261  			ConfigType:    network.ConfigDHCP,
  1262  		}}
  1263  	} else if strings.HasPrefix(string(instId), "i-disabled-nic-") {
  1264  		info = info[2:]
  1265  	}
  1266  
  1267  	estate.ops <- OpNetworkInterfaces{
  1268  		Env:        env.name,
  1269  		InstanceId: instId,
  1270  		Info:       info,
  1271  	}
  1272  
  1273  	return info, nil
  1274  }
  1275  
  1276  type azShim struct {
  1277  	name      string
  1278  	available bool
  1279  }
  1280  
  1281  func (az azShim) Name() string {
  1282  	return az.name
  1283  }
  1284  
  1285  func (az azShim) Available() bool {
  1286  	return az.available
  1287  }
  1288  
  1289  // AvailabilityZones implements environs.ZonedEnviron.
  1290  func (env *environ) AvailabilityZones() ([]common.AvailabilityZone, error) {
  1291  	// TODO(dimitern): Fix this properly.
  1292  	return []common.AvailabilityZone{
  1293  		azShim{"zone1", true},
  1294  		azShim{"zone2", false},
  1295  	}, nil
  1296  }
  1297  
  1298  // InstanceAvailabilityZoneNames implements environs.ZonedEnviron.
  1299  func (env *environ) InstanceAvailabilityZoneNames(ids []instance.Id) ([]string, error) {
  1300  	// TODO(dimitern): Fix this properly.
  1301  	if err := env.checkBroken("InstanceAvailabilityZoneNames"); err != nil {
  1302  		return nil, errors.NotSupportedf("instance availability zones")
  1303  	}
  1304  	return []string{"zone1"}, nil
  1305  }
  1306  
  1307  // Subnets implements environs.Environ.Subnets.
  1308  func (env *environ) Subnets(instId instance.Id, subnetIds []network.Id) ([]network.SubnetInfo, error) {
  1309  	if err := env.checkBroken("Subnets"); err != nil {
  1310  		return nil, err
  1311  	}
  1312  
  1313  	estate, err := env.state()
  1314  	if err != nil {
  1315  		return nil, err
  1316  	}
  1317  	estate.mu.Lock()
  1318  	defer estate.mu.Unlock()
  1319  
  1320  	if ok, _ := env.SupportsSpaceDiscovery(); ok {
  1321  		// Space discovery needs more subnets to work with.
  1322  		return env.subnetsForSpaceDiscovery(estate)
  1323  	}
  1324  
  1325  	allSubnets := []network.SubnetInfo{{
  1326  		CIDR:              "0.10.0.0/24",
  1327  		ProviderId:        "dummy-private",
  1328  		AllocatableIPLow:  net.ParseIP("0.10.0.0"),
  1329  		AllocatableIPHigh: net.ParseIP("0.10.0.255"),
  1330  		AvailabilityZones: []string{"zone1", "zone2"},
  1331  	}, {
  1332  		CIDR:              "0.20.0.0/24",
  1333  		ProviderId:        "dummy-public",
  1334  		AllocatableIPLow:  net.ParseIP("0.20.0.0"),
  1335  		AllocatableIPHigh: net.ParseIP("0.20.0.255"),
  1336  	}}
  1337  
  1338  	// Filter result by ids, if given.
  1339  	var result []network.SubnetInfo
  1340  	for _, subId := range subnetIds {
  1341  		switch subId {
  1342  		case "dummy-private":
  1343  			result = append(result, allSubnets[0])
  1344  		case "dummy-public":
  1345  			result = append(result, allSubnets[1])
  1346  		}
  1347  	}
  1348  	if len(subnetIds) == 0 {
  1349  		result = append([]network.SubnetInfo{}, allSubnets...)
  1350  	}
  1351  
  1352  	noSubnets := strings.HasPrefix(string(instId), "i-no-subnets")
  1353  	noNICSubnets := strings.HasPrefix(string(instId), "i-nic-no-subnet-")
  1354  	if noSubnets || noNICSubnets {
  1355  		// Simulate no subnets available if the instance id has prefix
  1356  		// "i-no-subnets-".
  1357  		result = result[:0]
  1358  	}
  1359  	if len(result) == 0 {
  1360  		// No results, so just return them now.
  1361  		estate.ops <- OpSubnets{
  1362  			Env:        env.name,
  1363  			InstanceId: instId,
  1364  			SubnetIds:  subnetIds,
  1365  			Info:       result,
  1366  		}
  1367  		return result, nil
  1368  	}
  1369  
  1370  	makeNoAlloc := func(info network.SubnetInfo) network.SubnetInfo {
  1371  		// Remove the allocatable range and change the provider id
  1372  		// prefix.
  1373  		pid := string(info.ProviderId)
  1374  		pid = strings.TrimPrefix(pid, "dummy-")
  1375  		info.ProviderId = network.Id("noalloc-" + pid)
  1376  		info.AllocatableIPLow = nil
  1377  		info.AllocatableIPHigh = nil
  1378  		return info
  1379  	}
  1380  	if strings.HasPrefix(string(instId), "i-no-alloc-") {
  1381  		iid := strings.TrimPrefix(string(instId), "i-no-alloc-")
  1382  		lastIdx := len(result) - 1
  1383  		if strings.HasPrefix(iid, "all") {
  1384  			// Simulate all subnets have no allocatable ranges set.
  1385  			for i := range result {
  1386  				result[i] = makeNoAlloc(result[i])
  1387  			}
  1388  		} else if idx, err := strconv.Atoi(iid); err == nil {
  1389  			// Simulate only the subnet with index idx in the result
  1390  			// have no allocatable range set.
  1391  			if idx < 0 || idx > lastIdx {
  1392  				err = errors.Errorf("index %d out of range; expected 0..%d", idx, lastIdx)
  1393  				return nil, err
  1394  			}
  1395  			result[idx] = makeNoAlloc(result[idx])
  1396  		} else {
  1397  			err = errors.Errorf("invalid index %q; expected int", iid)
  1398  			return nil, err
  1399  		}
  1400  	}
  1401  
  1402  	estate.ops <- OpSubnets{
  1403  		Env:        env.name,
  1404  		InstanceId: instId,
  1405  		SubnetIds:  subnetIds,
  1406  		Info:       result,
  1407  	}
  1408  	return result, nil
  1409  }
  1410  
  1411  func (env *environ) subnetsForSpaceDiscovery(estate *environState) ([]network.SubnetInfo, error) {
  1412  	result := []network.SubnetInfo{{
  1413  		ProviderId:        network.Id("1"),
  1414  		AvailabilityZones: []string{"zone1"},
  1415  		CIDR:              "192.168.1.0/24",
  1416  	}, {
  1417  		ProviderId:        network.Id("2"),
  1418  		AvailabilityZones: []string{"zone1"},
  1419  		CIDR:              "192.168.2.0/24",
  1420  		VLANTag:           1,
  1421  	}, {
  1422  		ProviderId:        network.Id("3"),
  1423  		AvailabilityZones: []string{"zone1"},
  1424  		CIDR:              "192.168.3.0/24",
  1425  	}, {
  1426  		ProviderId:        network.Id("4"),
  1427  		AvailabilityZones: []string{"zone1"},
  1428  		CIDR:              "192.168.4.0/24",
  1429  	}, {
  1430  		ProviderId:        network.Id("5"),
  1431  		AvailabilityZones: []string{"zone1"},
  1432  		CIDR:              "192.168.5.0/24",
  1433  	}}
  1434  	estate.ops <- OpSubnets{
  1435  		Env:        env.name,
  1436  		InstanceId: instance.UnknownId,
  1437  		SubnetIds:  []network.Id{},
  1438  		Info:       result,
  1439  	}
  1440  	return result, nil
  1441  }
  1442  
  1443  func (e *environ) AllInstances() ([]instance.Instance, error) {
  1444  	defer delay()
  1445  	if err := e.checkBroken("AllInstances"); err != nil {
  1446  		return nil, err
  1447  	}
  1448  	var insts []instance.Instance
  1449  	estate, err := e.state()
  1450  	if err != nil {
  1451  		return nil, err
  1452  	}
  1453  	estate.mu.Lock()
  1454  	defer estate.mu.Unlock()
  1455  	for _, v := range estate.insts {
  1456  		insts = append(insts, v)
  1457  	}
  1458  	return insts, nil
  1459  }
  1460  
  1461  func (e *environ) OpenPorts(ports []network.PortRange) error {
  1462  	if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal {
  1463  		return fmt.Errorf("invalid firewall mode %q for opening ports on model", mode)
  1464  	}
  1465  	estate, err := e.state()
  1466  	if err != nil {
  1467  		return err
  1468  	}
  1469  	estate.mu.Lock()
  1470  	defer estate.mu.Unlock()
  1471  	for _, p := range ports {
  1472  		estate.globalPorts[p] = true
  1473  	}
  1474  	return nil
  1475  }
  1476  
  1477  func (e *environ) ClosePorts(ports []network.PortRange) error {
  1478  	if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal {
  1479  		return fmt.Errorf("invalid firewall mode %q for closing ports on model", mode)
  1480  	}
  1481  	estate, err := e.state()
  1482  	if err != nil {
  1483  		return err
  1484  	}
  1485  	estate.mu.Lock()
  1486  	defer estate.mu.Unlock()
  1487  	for _, p := range ports {
  1488  		delete(estate.globalPorts, p)
  1489  	}
  1490  	return nil
  1491  }
  1492  
  1493  func (e *environ) Ports() (ports []network.PortRange, err error) {
  1494  	if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal {
  1495  		return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from model", mode)
  1496  	}
  1497  	estate, err := e.state()
  1498  	if err != nil {
  1499  		return nil, err
  1500  	}
  1501  	estate.mu.Lock()
  1502  	defer estate.mu.Unlock()
  1503  	for p := range estate.globalPorts {
  1504  		ports = append(ports, p)
  1505  	}
  1506  	network.SortPortRanges(ports)
  1507  	return
  1508  }
  1509  
  1510  func (*environ) Provider() environs.EnvironProvider {
  1511  	return &dummy
  1512  }
  1513  
  1514  type dummyInstance struct {
  1515  	state        *environState
  1516  	ports        map[network.PortRange]bool
  1517  	id           instance.Id
  1518  	status       string
  1519  	machineId    string
  1520  	series       string
  1521  	firewallMode string
  1522  	controller   bool
  1523  
  1524  	mu        sync.Mutex
  1525  	addresses []network.Address
  1526  	broken    []string
  1527  }
  1528  
  1529  func (inst *dummyInstance) Id() instance.Id {
  1530  	return inst.id
  1531  }
  1532  
  1533  func (inst *dummyInstance) Status() instance.InstanceStatus {
  1534  	inst.mu.Lock()
  1535  	defer inst.mu.Unlock()
  1536  	// TODO(perrito666) add a provider status -> juju status mapping.
  1537  	jujuStatus := status.StatusPending
  1538  	if inst.status != "" {
  1539  		dummyStatus := status.Status(inst.status)
  1540  		if dummyStatus.KnownInstanceStatus() {
  1541  			jujuStatus = dummyStatus
  1542  		}
  1543  	}
  1544  
  1545  	return instance.InstanceStatus{
  1546  		Status:  jujuStatus,
  1547  		Message: inst.status,
  1548  	}
  1549  
  1550  }
  1551  
  1552  // SetInstanceAddresses sets the addresses associated with the given
  1553  // dummy instance.
  1554  func SetInstanceAddresses(inst instance.Instance, addrs []network.Address) {
  1555  	inst0 := inst.(*dummyInstance)
  1556  	inst0.mu.Lock()
  1557  	inst0.addresses = append(inst0.addresses[:0], addrs...)
  1558  	logger.Debugf("setting instance %q addresses to %v", inst0.Id(), addrs)
  1559  	inst0.mu.Unlock()
  1560  }
  1561  
  1562  // SetInstanceStatus sets the status associated with the given
  1563  // dummy instance.
  1564  func SetInstanceStatus(inst instance.Instance, status string) {
  1565  	inst0 := inst.(*dummyInstance)
  1566  	inst0.mu.Lock()
  1567  	inst0.status = status
  1568  	inst0.mu.Unlock()
  1569  }
  1570  
  1571  // SetInstanceBroken marks the named methods of the instance as broken.
  1572  // Any previously broken methods not in the set will no longer be broken.
  1573  func SetInstanceBroken(inst instance.Instance, methods ...string) {
  1574  	inst0 := inst.(*dummyInstance)
  1575  	inst0.mu.Lock()
  1576  	inst0.broken = methods
  1577  	inst0.mu.Unlock()
  1578  }
  1579  
  1580  func (inst *dummyInstance) checkBroken(method string) error {
  1581  	for _, m := range inst.broken {
  1582  		if m == method {
  1583  			return fmt.Errorf("dummyInstance.%s is broken", method)
  1584  		}
  1585  	}
  1586  	return nil
  1587  }
  1588  
  1589  func (inst *dummyInstance) Addresses() ([]network.Address, error) {
  1590  	inst.mu.Lock()
  1591  	defer inst.mu.Unlock()
  1592  	if err := inst.checkBroken("Addresses"); err != nil {
  1593  		return nil, err
  1594  	}
  1595  	return append([]network.Address{}, inst.addresses...), nil
  1596  }
  1597  
  1598  func (inst *dummyInstance) OpenPorts(machineId string, ports []network.PortRange) error {
  1599  	defer delay()
  1600  	logger.Infof("openPorts %s, %#v", machineId, ports)
  1601  	if inst.firewallMode != config.FwInstance {
  1602  		return fmt.Errorf("invalid firewall mode %q for opening ports on instance",
  1603  			inst.firewallMode)
  1604  	}
  1605  	if inst.machineId != machineId {
  1606  		panic(fmt.Errorf("OpenPorts with mismatched machine id, expected %q got %q", inst.machineId, machineId))
  1607  	}
  1608  	inst.state.mu.Lock()
  1609  	defer inst.state.mu.Unlock()
  1610  	if err := inst.checkBroken("OpenPorts"); err != nil {
  1611  		return err
  1612  	}
  1613  	inst.state.ops <- OpOpenPorts{
  1614  		Env:        inst.state.name,
  1615  		MachineId:  machineId,
  1616  		InstanceId: inst.Id(),
  1617  		Ports:      ports,
  1618  	}
  1619  	for _, p := range ports {
  1620  		inst.ports[p] = true
  1621  	}
  1622  	return nil
  1623  }
  1624  
  1625  func (inst *dummyInstance) ClosePorts(machineId string, ports []network.PortRange) error {
  1626  	defer delay()
  1627  	if inst.firewallMode != config.FwInstance {
  1628  		return fmt.Errorf("invalid firewall mode %q for closing ports on instance",
  1629  			inst.firewallMode)
  1630  	}
  1631  	if inst.machineId != machineId {
  1632  		panic(fmt.Errorf("ClosePorts with mismatched machine id, expected %s got %s", inst.machineId, machineId))
  1633  	}
  1634  	inst.state.mu.Lock()
  1635  	defer inst.state.mu.Unlock()
  1636  	if err := inst.checkBroken("ClosePorts"); err != nil {
  1637  		return err
  1638  	}
  1639  	inst.state.ops <- OpClosePorts{
  1640  		Env:        inst.state.name,
  1641  		MachineId:  machineId,
  1642  		InstanceId: inst.Id(),
  1643  		Ports:      ports,
  1644  	}
  1645  	for _, p := range ports {
  1646  		delete(inst.ports, p)
  1647  	}
  1648  	return nil
  1649  }
  1650  
  1651  func (inst *dummyInstance) Ports(machineId string) (ports []network.PortRange, err error) {
  1652  	defer delay()
  1653  	if inst.firewallMode != config.FwInstance {
  1654  		return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from instance",
  1655  			inst.firewallMode)
  1656  	}
  1657  	if inst.machineId != machineId {
  1658  		panic(fmt.Errorf("Ports with mismatched machine id, expected %q got %q", inst.machineId, machineId))
  1659  	}
  1660  	inst.state.mu.Lock()
  1661  	defer inst.state.mu.Unlock()
  1662  	if err := inst.checkBroken("Ports"); err != nil {
  1663  		return nil, err
  1664  	}
  1665  	for p := range inst.ports {
  1666  		ports = append(ports, p)
  1667  	}
  1668  	network.SortPortRanges(ports)
  1669  	return
  1670  }
  1671  
  1672  // providerDelay controls the delay before dummy responds.
  1673  // non empty values in JUJU_DUMMY_DELAY will be parsed as
  1674  // time.Durations into this value.
  1675  var providerDelay, _ = time.ParseDuration(os.Getenv("JUJU_DUMMY_DELAY")) // parse errors are ignored
  1676  
  1677  // pause execution to simulate the latency of a real provider
  1678  func delay() {
  1679  	if providerDelay > 0 {
  1680  		logger.Infof("pausing for %v", providerDelay)
  1681  		<-time.After(providerDelay)
  1682  	}
  1683  }
  1684  
  1685  func (e *environ) AllocateContainerAddresses(hostInstanceID instance.Id, preparedInfo []network.InterfaceInfo) ([]network.InterfaceInfo, error) {
  1686  	return nil, errors.NotSupportedf("container address allocation")
  1687  }
  1688  
  1689  // MigrationConfigUpdate implements MigrationConfigUpdater.
  1690  func (*environ) MigrationConfigUpdate(controllerConfig *config.Config) map[string]interface{} {
  1691  	return map[string]interface{}{
  1692  		"controller-uuid": controllerConfig.UUID(),
  1693  	}
  1694  }