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