github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/provider/dummy/environs.go (about)

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