github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/provider/dummy/environs.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // The dummy provider implements an environment provider for testing
     5  // purposes, registered with environs under the name "dummy".
     6  //
     7  // The configuration YAML for the testing environment
     8  // must specify a "state-server" property with a boolean
     9  // value. If this is true, a state server will be started
    10  // the first time StateInfo is called on a newly reset environment.
    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  	"errors"
    27  	"fmt"
    28  	"net"
    29  	"net/http"
    30  	"os"
    31  	"strconv"
    32  	"strings"
    33  	"sync"
    34  	"time"
    35  
    36  	"github.com/juju/loggo"
    37  
    38  	"launchpad.net/juju-core/constraints"
    39  	"launchpad.net/juju-core/environs"
    40  	"launchpad.net/juju-core/environs/bootstrap"
    41  	"launchpad.net/juju-core/environs/cloudinit"
    42  	"launchpad.net/juju-core/environs/config"
    43  	"launchpad.net/juju-core/environs/imagemetadata"
    44  	"launchpad.net/juju-core/environs/simplestreams"
    45  	"launchpad.net/juju-core/environs/storage"
    46  	"launchpad.net/juju-core/environs/tools"
    47  	"launchpad.net/juju-core/instance"
    48  	"launchpad.net/juju-core/names"
    49  	"launchpad.net/juju-core/provider"
    50  	"launchpad.net/juju-core/provider/common"
    51  	"launchpad.net/juju-core/schema"
    52  	"launchpad.net/juju-core/state"
    53  	"launchpad.net/juju-core/state/api"
    54  	"launchpad.net/juju-core/state/apiserver"
    55  	"launchpad.net/juju-core/testing"
    56  	coretools "launchpad.net/juju-core/tools"
    57  	"launchpad.net/juju-core/utils"
    58  )
    59  
    60  var logger = loggo.GetLogger("juju.provider.dummy")
    61  
    62  // SampleConfig() returns an environment configuration with all required
    63  // attributes set.
    64  func SampleConfig() testing.Attrs {
    65  	return testing.Attrs{
    66  		"type":                      "dummy",
    67  		"name":                      "only",
    68  		"authorized-keys":           testing.FakeAuthKeys,
    69  		"firewall-mode":             config.FwInstance,
    70  		"admin-secret":              testing.DefaultMongoPassword,
    71  		"ca-cert":                   testing.CACert,
    72  		"ca-private-key":            testing.CAKey,
    73  		"ssl-hostname-verification": true,
    74  		"development":               false,
    75  		"state-port":                1234,
    76  		"api-port":                  4321,
    77  		"syslog-port":               2345,
    78  		"default-series":            "precise",
    79  
    80  		"secret":       "pork",
    81  		"state-server": true,
    82  	}
    83  }
    84  
    85  // stateInfo returns a *state.Info which allows clients to connect to the
    86  // shared dummy state, if it exists.
    87  func stateInfo() *state.Info {
    88  	if testing.MgoServer.Addr() == "" {
    89  		panic("dummy environ state tests must be run with MgoTestPackage")
    90  	}
    91  	return &state.Info{
    92  		Addrs:  []string{testing.MgoServer.Addr()},
    93  		CACert: []byte(testing.CACert),
    94  	}
    95  }
    96  
    97  // Operation represents an action on the dummy provider.
    98  type Operation interface{}
    99  
   100  type OpBootstrap struct {
   101  	Context     environs.BootstrapContext
   102  	Env         string
   103  	Constraints constraints.Value
   104  }
   105  
   106  type OpDestroy struct {
   107  	Env   string
   108  	Error error
   109  }
   110  
   111  type OpStartInstance struct {
   112  	Env          string
   113  	MachineId    string
   114  	MachineNonce string
   115  	Instance     instance.Instance
   116  	Constraints  constraints.Value
   117  	Info         *state.Info
   118  	APIInfo      *api.Info
   119  	Secret       string
   120  }
   121  
   122  type OpStopInstances struct {
   123  	Env       string
   124  	Instances []instance.Instance
   125  }
   126  
   127  type OpOpenPorts struct {
   128  	Env        string
   129  	MachineId  string
   130  	InstanceId instance.Id
   131  	Ports      []instance.Port
   132  }
   133  
   134  type OpClosePorts struct {
   135  	Env        string
   136  	MachineId  string
   137  	InstanceId instance.Id
   138  	Ports      []instance.Port
   139  }
   140  
   141  type OpPutFile struct {
   142  	Env      string
   143  	FileName string
   144  }
   145  
   146  // environProvider represents the dummy provider.  There is only ever one
   147  // instance of this type (providerInstance)
   148  type environProvider struct {
   149  	mu          sync.Mutex
   150  	ops         chan<- Operation
   151  	statePolicy state.Policy
   152  	// We have one state for each environment name
   153  	state      map[int]*environState
   154  	maxStateId int
   155  }
   156  
   157  var providerInstance environProvider
   158  
   159  const noStateId = 0
   160  
   161  // environState represents the state of an environment.
   162  // It can be shared between several environ values,
   163  // so that a given environment can be opened several times.
   164  type environState struct {
   165  	id           int
   166  	name         string
   167  	ops          chan<- Operation
   168  	statePolicy  state.Policy
   169  	mu           sync.Mutex
   170  	maxId        int // maximum instance id allocated so far.
   171  	insts        map[instance.Id]*dummyInstance
   172  	globalPorts  map[instance.Port]bool
   173  	bootstrapped bool
   174  	storageDelay time.Duration
   175  	storage      *storageServer
   176  	httpListener net.Listener
   177  	apiServer    *apiserver.Server
   178  	apiState     *state.State
   179  }
   180  
   181  // environ represents a client's connection to a given environment's
   182  // state.
   183  type environ struct {
   184  	name         string
   185  	ecfgMutex    sync.Mutex
   186  	ecfgUnlocked *environConfig
   187  }
   188  
   189  var _ imagemetadata.SupportsCustomSources = (*environ)(nil)
   190  var _ tools.SupportsCustomSources = (*environ)(nil)
   191  var _ environs.Environ = (*environ)(nil)
   192  
   193  // discardOperations discards all Operations written to it.
   194  var discardOperations chan<- Operation
   195  
   196  func init() {
   197  	environs.RegisterProvider("dummy", &providerInstance)
   198  
   199  	// Prime the first ops channel, so that naive clients can use
   200  	// the testing environment by simply importing it.
   201  	c := make(chan Operation)
   202  	go func() {
   203  		for _ = range c {
   204  		}
   205  	}()
   206  	discardOperations = c
   207  	Reset()
   208  
   209  	// parse errors are ignored
   210  	providerDelay, _ = time.ParseDuration(os.Getenv("JUJU_DUMMY_DELAY"))
   211  }
   212  
   213  // Reset resets the entire dummy environment and forgets any registered
   214  // operation listener.  All opened environments after Reset will share
   215  // the same underlying state.
   216  func Reset() {
   217  	logger.Infof("reset environment")
   218  	p := &providerInstance
   219  	p.mu.Lock()
   220  	defer p.mu.Unlock()
   221  	providerInstance.ops = discardOperations
   222  	for _, s := range p.state {
   223  		s.httpListener.Close()
   224  		s.destroy()
   225  	}
   226  	providerInstance.state = make(map[int]*environState)
   227  	if testing.MgoServer.Addr() != "" {
   228  		testing.MgoServer.Reset()
   229  	}
   230  	providerInstance.statePolicy = environs.NewStatePolicy()
   231  }
   232  
   233  func (state *environState) destroy() {
   234  	state.storage.files = make(map[string][]byte)
   235  	if !state.bootstrapped {
   236  		return
   237  	}
   238  	if state.apiServer != nil {
   239  		if err := state.apiServer.Stop(); err != nil {
   240  			panic(err)
   241  		}
   242  		state.apiServer = nil
   243  		if err := state.apiState.Close(); err != nil {
   244  			panic(err)
   245  		}
   246  		state.apiState = nil
   247  	}
   248  	if testing.MgoServer.Addr() != "" {
   249  		testing.MgoServer.Reset()
   250  	}
   251  	state.bootstrapped = false
   252  }
   253  
   254  // GetStateInAPIServer returns the state connection used by the API server
   255  // This is so code in the test suite can trigger Syncs, etc that the API server
   256  // will see, which will then trigger API watchers, etc.
   257  func (e *environ) GetStateInAPIServer() *state.State {
   258  	st, err := e.state()
   259  	if err != nil {
   260  		panic(err)
   261  	}
   262  	return st.apiState
   263  }
   264  
   265  // newState creates the state for a new environment with the
   266  // given name and starts an http server listening for
   267  // storage requests.
   268  func newState(name string, ops chan<- Operation, policy state.Policy) *environState {
   269  	s := &environState{
   270  		name:        name,
   271  		ops:         ops,
   272  		statePolicy: policy,
   273  		insts:       make(map[instance.Id]*dummyInstance),
   274  		globalPorts: make(map[instance.Port]bool),
   275  	}
   276  	s.storage = newStorageServer(s, "/"+name+"/private")
   277  	s.listen()
   278  	return s
   279  }
   280  
   281  // listen starts a network listener listening for http
   282  // requests to retrieve files in the state's storage.
   283  func (s *environState) listen() {
   284  	l, err := net.Listen("tcp", "127.0.0.1:0")
   285  	if err != nil {
   286  		panic(fmt.Errorf("cannot start listener: %v", err))
   287  	}
   288  	s.httpListener = l
   289  	mux := http.NewServeMux()
   290  	mux.Handle(s.storage.path+"/", http.StripPrefix(s.storage.path+"/", s.storage))
   291  	go http.Serve(l, mux)
   292  }
   293  
   294  // SetStatePolicy sets the state.Policy to use when a
   295  // state server is initialised by dummy.
   296  func SetStatePolicy(policy state.Policy) {
   297  	p := &providerInstance
   298  	p.mu.Lock()
   299  	defer p.mu.Unlock()
   300  	p.statePolicy = policy
   301  }
   302  
   303  // Listen closes the previously registered listener (if any).
   304  // Subsequent operations on any dummy environment can be received on c
   305  // (if not nil).
   306  func Listen(c chan<- Operation) {
   307  	p := &providerInstance
   308  	p.mu.Lock()
   309  	defer p.mu.Unlock()
   310  	if c == nil {
   311  		c = discardOperations
   312  	}
   313  	if p.ops != discardOperations {
   314  		close(p.ops)
   315  	}
   316  	p.ops = c
   317  	for _, st := range p.state {
   318  		st.mu.Lock()
   319  		st.ops = c
   320  		st.mu.Unlock()
   321  	}
   322  }
   323  
   324  // SetStorageDelay causes any storage download operation in any current
   325  // environment to be delayed for the given duration.
   326  func SetStorageDelay(d time.Duration) {
   327  	p := &providerInstance
   328  	p.mu.Lock()
   329  	defer p.mu.Unlock()
   330  	for _, st := range p.state {
   331  		st.mu.Lock()
   332  		st.storageDelay = d
   333  		st.mu.Unlock()
   334  	}
   335  }
   336  
   337  var configFields = schema.Fields{
   338  	"state-server": schema.Bool(),
   339  	"broken":       schema.String(),
   340  	"secret":       schema.String(),
   341  	"state-id":     schema.String(),
   342  }
   343  var configDefaults = schema.Defaults{
   344  	"broken":   "",
   345  	"secret":   "pork",
   346  	"state-id": schema.Omit,
   347  }
   348  
   349  type environConfig struct {
   350  	*config.Config
   351  	attrs map[string]interface{}
   352  }
   353  
   354  func (c *environConfig) stateServer() bool {
   355  	return c.attrs["state-server"].(bool)
   356  }
   357  
   358  func (c *environConfig) broken() string {
   359  	return c.attrs["broken"].(string)
   360  }
   361  
   362  func (c *environConfig) secret() string {
   363  	return c.attrs["secret"].(string)
   364  }
   365  
   366  func (c *environConfig) stateId() int {
   367  	idStr, ok := c.attrs["state-id"].(string)
   368  	if !ok {
   369  		return noStateId
   370  	}
   371  	id, err := strconv.Atoi(idStr)
   372  	if err != nil {
   373  		panic(fmt.Errorf("unexpected state-id %q (should have pre-checked)", idStr))
   374  	}
   375  	return id
   376  }
   377  
   378  func (p *environProvider) newConfig(cfg *config.Config) (*environConfig, error) {
   379  	valid, err := p.Validate(cfg, nil)
   380  	if err != nil {
   381  		return nil, err
   382  	}
   383  	return &environConfig{valid, valid.UnknownAttrs()}, nil
   384  }
   385  
   386  func (p *environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) {
   387  	// Check for valid changes for the base config values.
   388  	if err := config.Validate(cfg, old); err != nil {
   389  		return nil, err
   390  	}
   391  	validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults)
   392  	if err != nil {
   393  		return nil, err
   394  	}
   395  	if idStr, ok := validated["state-id"].(string); ok {
   396  		if _, err := strconv.Atoi(idStr); err != nil {
   397  			return nil, fmt.Errorf("invalid state-id %q", idStr)
   398  		}
   399  	}
   400  	// Apply the coerced unknown values back into the config.
   401  	return cfg.Apply(validated)
   402  }
   403  
   404  func (e *environ) state() (*environState, error) {
   405  	stateId := e.ecfg().stateId()
   406  	if stateId == noStateId {
   407  		return nil, provider.ErrNotPrepared
   408  	}
   409  	p := &providerInstance
   410  	p.mu.Lock()
   411  	defer p.mu.Unlock()
   412  	if state := p.state[stateId]; state != nil {
   413  		return state, nil
   414  	}
   415  	return nil, provider.ErrDestroyed
   416  }
   417  
   418  func (p *environProvider) Open(cfg *config.Config) (environs.Environ, error) {
   419  	p.mu.Lock()
   420  	defer p.mu.Unlock()
   421  	ecfg, err := p.newConfig(cfg)
   422  	if err != nil {
   423  		return nil, err
   424  	}
   425  	if ecfg.stateId() == noStateId {
   426  		return nil, provider.ErrNotPrepared
   427  	}
   428  	env := &environ{
   429  		name:         ecfg.Name(),
   430  		ecfgUnlocked: ecfg,
   431  	}
   432  	if err := env.checkBroken("Open"); err != nil {
   433  		return nil, err
   434  	}
   435  	return env, nil
   436  }
   437  
   438  func (p *environProvider) Prepare(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) {
   439  	cfg, err := p.prepare(cfg)
   440  	if err != nil {
   441  		return nil, err
   442  	}
   443  	return p.Open(cfg)
   444  }
   445  
   446  // prepare is the internal version of Prepare - it prepares the
   447  // environment but does not open it.
   448  func (p *environProvider) prepare(cfg *config.Config) (*config.Config, error) {
   449  	ecfg, err := p.newConfig(cfg)
   450  	if err != nil {
   451  		return nil, err
   452  	}
   453  	p.mu.Lock()
   454  	defer p.mu.Unlock()
   455  	name := cfg.Name()
   456  	if ecfg.stateId() != noStateId {
   457  		return cfg, nil
   458  	}
   459  	// The environment has not been prepared,
   460  	// so create it and set its state identifier accordingly.
   461  	if ecfg.stateServer() && len(p.state) != 0 {
   462  		for _, old := range p.state {
   463  			panic(fmt.Errorf("cannot share a state between two dummy environs; old %q; new %q", old.name, name))
   464  		}
   465  	}
   466  	state := newState(name, p.ops, p.statePolicy)
   467  	p.maxStateId++
   468  	state.id = p.maxStateId
   469  	p.state[state.id] = state
   470  	// Add the state id to the configuration we use to
   471  	// in the returned environment.
   472  	return cfg.Apply(map[string]interface{}{
   473  		"state-id": fmt.Sprint(state.id),
   474  	})
   475  }
   476  
   477  func (*environProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) {
   478  	ecfg, err := providerInstance.newConfig(cfg)
   479  	if err != nil {
   480  		return nil, err
   481  	}
   482  	return map[string]string{
   483  		"secret": ecfg.secret(),
   484  	}, nil
   485  }
   486  
   487  func (*environProvider) PublicAddress() (string, error) {
   488  	return "public.dummy.address.example.com", nil
   489  }
   490  
   491  func (*environProvider) PrivateAddress() (string, error) {
   492  	return "private.dummy.address.example.com", nil
   493  }
   494  
   495  func (*environProvider) BoilerplateConfig() string {
   496  	return `
   497  # Fake configuration for dummy provider.
   498  dummy:
   499      type: dummy
   500  
   501  `[1:]
   502  }
   503  
   504  var errBroken = errors.New("broken environment")
   505  
   506  // Override for testing - the data directory with which the state api server is initialised.
   507  var DataDir = ""
   508  
   509  func (e *environ) ecfg() *environConfig {
   510  	e.ecfgMutex.Lock()
   511  	ecfg := e.ecfgUnlocked
   512  	e.ecfgMutex.Unlock()
   513  	return ecfg
   514  }
   515  
   516  func (e *environ) checkBroken(method string) error {
   517  	for _, m := range strings.Fields(e.ecfg().broken()) {
   518  		if m == method {
   519  			return fmt.Errorf("dummy.%s is broken", method)
   520  		}
   521  	}
   522  	return nil
   523  }
   524  
   525  func (e *environ) Name() string {
   526  	return e.name
   527  }
   528  
   529  // GetImageSources returns a list of sources which are used to search for simplestreams image metadata.
   530  func (e *environ) GetImageSources() ([]simplestreams.DataSource, error) {
   531  	return []simplestreams.DataSource{
   532  		storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Storage(), storage.BaseImagesPath)}, nil
   533  }
   534  
   535  // GetToolsSources returns a list of sources which are used to search for simplestreams tools metadata.
   536  func (e *environ) GetToolsSources() ([]simplestreams.DataSource, error) {
   537  	return []simplestreams.DataSource{
   538  		storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Storage(), storage.BaseToolsPath)}, nil
   539  }
   540  
   541  func (e *environ) Bootstrap(ctx environs.BootstrapContext, cons constraints.Value) error {
   542  	selectedTools, err := common.EnsureBootstrapTools(e, e.Config().DefaultSeries(), cons.Arch)
   543  	if err != nil {
   544  		return err
   545  	}
   546  
   547  	defer delay()
   548  	if err := e.checkBroken("Bootstrap"); err != nil {
   549  		return err
   550  	}
   551  	password := e.Config().AdminSecret()
   552  	if password == "" {
   553  		return fmt.Errorf("admin-secret is required for bootstrap")
   554  	}
   555  	if _, ok := e.Config().CACert(); !ok {
   556  		return fmt.Errorf("no CA certificate in environment configuration")
   557  	}
   558  
   559  	logger.Infof("would pick tools from %s", selectedTools)
   560  	cfg, err := environs.BootstrapConfig(e.Config())
   561  	if err != nil {
   562  		return fmt.Errorf("cannot make bootstrap config: %v", err)
   563  	}
   564  
   565  	estate, err := e.state()
   566  	if err != nil {
   567  		return err
   568  	}
   569  	estate.mu.Lock()
   570  	defer estate.mu.Unlock()
   571  	if estate.bootstrapped {
   572  		return fmt.Errorf("environment is already bootstrapped")
   573  	}
   574  	// Write the bootstrap file just like a normal provider. However
   575  	// we need to release the mutex for the save state to work, so regain
   576  	// it after the call.
   577  	estate.mu.Unlock()
   578  	if err := bootstrap.SaveState(e.Storage(), &bootstrap.BootstrapState{StateInstances: []instance.Id{"localhost"}}); err != nil {
   579  		logger.Errorf("failed to save state instances: %v", err)
   580  		estate.mu.Lock() // otherwise defered unlock will fail
   581  		return err
   582  	}
   583  	estate.mu.Lock() // back at it
   584  
   585  	if e.ecfg().stateServer() {
   586  		// TODO(rog) factor out relevant code from cmd/jujud/bootstrap.go
   587  		// so that we can call it here.
   588  
   589  		info := stateInfo()
   590  		st, err := state.Initialize(info, cfg, state.DefaultDialOpts(), estate.statePolicy)
   591  		if err != nil {
   592  			panic(err)
   593  		}
   594  		if err := st.SetEnvironConstraints(cons); err != nil {
   595  			panic(err)
   596  		}
   597  		if err := st.SetAdminMongoPassword(utils.UserPasswordHash(password, utils.CompatSalt)); err != nil {
   598  			panic(err)
   599  		}
   600  		_, err = st.AddUser("admin", password)
   601  		if err != nil {
   602  			panic(err)
   603  		}
   604  		estate.apiServer, err = apiserver.NewServer(st, "localhost:0", []byte(testing.ServerCert), []byte(testing.ServerKey), DataDir)
   605  		if err != nil {
   606  			panic(err)
   607  		}
   608  		estate.apiState = st
   609  	}
   610  	estate.bootstrapped = true
   611  	estate.ops <- OpBootstrap{Context: ctx, Env: e.name, Constraints: cons}
   612  	return nil
   613  }
   614  
   615  func (e *environ) StateInfo() (*state.Info, *api.Info, error) {
   616  	estate, err := e.state()
   617  	if err != nil {
   618  		return nil, nil, err
   619  	}
   620  	estate.mu.Lock()
   621  	defer estate.mu.Unlock()
   622  	if err := e.checkBroken("StateInfo"); err != nil {
   623  		return nil, nil, err
   624  	}
   625  	if !e.ecfg().stateServer() {
   626  		return nil, nil, errors.New("dummy environment has no state configured")
   627  	}
   628  	if !estate.bootstrapped {
   629  		return nil, nil, environs.ErrNotBootstrapped
   630  	}
   631  	return stateInfo(), &api.Info{
   632  		Addrs:  []string{estate.apiServer.Addr()},
   633  		CACert: []byte(testing.CACert),
   634  	}, nil
   635  }
   636  
   637  func (e *environ) Config() *config.Config {
   638  	return e.ecfg().Config
   639  }
   640  
   641  func (e *environ) SetConfig(cfg *config.Config) error {
   642  	if err := e.checkBroken("SetConfig"); err != nil {
   643  		return err
   644  	}
   645  	ecfg, err := providerInstance.newConfig(cfg)
   646  	if err != nil {
   647  		return err
   648  	}
   649  	e.ecfgMutex.Lock()
   650  	e.ecfgUnlocked = ecfg
   651  	e.ecfgMutex.Unlock()
   652  	return nil
   653  }
   654  
   655  func (e *environ) Destroy() (res error) {
   656  	defer delay()
   657  	estate, err := e.state()
   658  	if err != nil {
   659  		if err == provider.ErrDestroyed {
   660  			return nil
   661  		}
   662  		return err
   663  	}
   664  	defer func() { estate.ops <- OpDestroy{Env: estate.name, Error: res} }()
   665  	if err := e.checkBroken("Destroy"); err != nil {
   666  		return err
   667  	}
   668  	p := &providerInstance
   669  	p.mu.Lock()
   670  	delete(p.state, estate.id)
   671  	p.mu.Unlock()
   672  
   673  	estate.mu.Lock()
   674  	defer estate.mu.Unlock()
   675  	estate.destroy()
   676  	return nil
   677  }
   678  
   679  // StartInstance is specified in the InstanceBroker interface.
   680  func (e *environ) StartInstance(cons constraints.Value, possibleTools coretools.List,
   681  	machineConfig *cloudinit.MachineConfig) (instance.Instance, *instance.HardwareCharacteristics, error) {
   682  
   683  	defer delay()
   684  	machineId := machineConfig.MachineId
   685  	logger.Infof("dummy startinstance, machine %s", machineId)
   686  	if err := e.checkBroken("StartInstance"); err != nil {
   687  		return nil, nil, err
   688  	}
   689  	estate, err := e.state()
   690  	if err != nil {
   691  		return nil, nil, err
   692  	}
   693  	estate.mu.Lock()
   694  	defer estate.mu.Unlock()
   695  	if machineConfig.MachineNonce == "" {
   696  		return nil, nil, fmt.Errorf("cannot start instance: missing machine nonce")
   697  	}
   698  	if _, ok := e.Config().CACert(); !ok {
   699  		return nil, nil, fmt.Errorf("no CA certificate in environment configuration")
   700  	}
   701  	if machineConfig.StateInfo.Tag != names.MachineTag(machineId) {
   702  		return nil, nil, fmt.Errorf("entity tag must match started machine")
   703  	}
   704  	if machineConfig.APIInfo.Tag != names.MachineTag(machineId) {
   705  		return nil, nil, fmt.Errorf("entity tag must match started machine")
   706  	}
   707  	logger.Infof("would pick tools from %s", possibleTools)
   708  	series := possibleTools.OneSeries()
   709  	i := &dummyInstance{
   710  		id:           instance.Id(fmt.Sprintf("%s-%d", e.name, estate.maxId)),
   711  		ports:        make(map[instance.Port]bool),
   712  		machineId:    machineId,
   713  		series:       series,
   714  		firewallMode: e.Config().FirewallMode(),
   715  		state:        estate,
   716  	}
   717  	var hc *instance.HardwareCharacteristics
   718  	// To match current system capability, only provide hardware characteristics for
   719  	// environ machines, not containers.
   720  	if state.ParentId(machineId) == "" {
   721  		// We will just assume the instance hardware characteristics exactly matches
   722  		// the supplied constraints (if specified).
   723  		hc = &instance.HardwareCharacteristics{
   724  			Arch:     cons.Arch,
   725  			Mem:      cons.Mem,
   726  			RootDisk: cons.RootDisk,
   727  			CpuCores: cons.CpuCores,
   728  			CpuPower: cons.CpuPower,
   729  			Tags:     cons.Tags,
   730  		}
   731  		// Fill in some expected instance hardware characteristics if constraints not specified.
   732  		if hc.Arch == nil {
   733  			arch := "amd64"
   734  			hc.Arch = &arch
   735  		}
   736  		if hc.Mem == nil {
   737  			mem := uint64(1024)
   738  			hc.Mem = &mem
   739  		}
   740  		if hc.RootDisk == nil {
   741  			disk := uint64(8192)
   742  			hc.RootDisk = &disk
   743  		}
   744  		if hc.CpuCores == nil {
   745  			cores := uint64(1)
   746  			hc.CpuCores = &cores
   747  		}
   748  	}
   749  	estate.insts[i.id] = i
   750  	estate.maxId++
   751  	estate.ops <- OpStartInstance{
   752  		Env:          e.name,
   753  		MachineId:    machineId,
   754  		MachineNonce: machineConfig.MachineNonce,
   755  		Constraints:  cons,
   756  		Instance:     i,
   757  		Info:         machineConfig.StateInfo,
   758  		APIInfo:      machineConfig.APIInfo,
   759  		Secret:       e.ecfg().secret(),
   760  	}
   761  	return i, hc, nil
   762  }
   763  
   764  func (e *environ) StopInstances(is []instance.Instance) error {
   765  	defer delay()
   766  	if err := e.checkBroken("StopInstance"); err != nil {
   767  		return err
   768  	}
   769  	estate, err := e.state()
   770  	if err != nil {
   771  		return err
   772  	}
   773  	estate.mu.Lock()
   774  	defer estate.mu.Unlock()
   775  	for _, i := range is {
   776  		delete(estate.insts, i.(*dummyInstance).id)
   777  	}
   778  	estate.ops <- OpStopInstances{
   779  		Env:       e.name,
   780  		Instances: is,
   781  	}
   782  	return nil
   783  }
   784  
   785  func (e *environ) Instances(ids []instance.Id) (insts []instance.Instance, err error) {
   786  	defer delay()
   787  	if err := e.checkBroken("Instances"); err != nil {
   788  		return nil, err
   789  	}
   790  	if len(ids) == 0 {
   791  		return nil, nil
   792  	}
   793  	estate, err := e.state()
   794  	if err != nil {
   795  		return nil, err
   796  	}
   797  	estate.mu.Lock()
   798  	defer estate.mu.Unlock()
   799  	notFound := 0
   800  	for _, id := range ids {
   801  		inst := estate.insts[id]
   802  		if inst == nil {
   803  			err = environs.ErrPartialInstances
   804  			notFound++
   805  			insts = append(insts, nil)
   806  		} else {
   807  			insts = append(insts, inst)
   808  		}
   809  	}
   810  	if notFound == len(ids) {
   811  		return nil, environs.ErrNoInstances
   812  	}
   813  	return
   814  }
   815  
   816  func (e *environ) AllInstances() ([]instance.Instance, error) {
   817  	defer delay()
   818  	if err := e.checkBroken("AllInstances"); err != nil {
   819  		return nil, err
   820  	}
   821  	var insts []instance.Instance
   822  	estate, err := e.state()
   823  	if err != nil {
   824  		return nil, err
   825  	}
   826  	estate.mu.Lock()
   827  	defer estate.mu.Unlock()
   828  	for _, v := range estate.insts {
   829  		insts = append(insts, v)
   830  	}
   831  	return insts, nil
   832  }
   833  
   834  func (e *environ) OpenPorts(ports []instance.Port) error {
   835  	if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal {
   836  		return fmt.Errorf("invalid firewall mode %q for opening ports on environment", mode)
   837  	}
   838  	estate, err := e.state()
   839  	if err != nil {
   840  		return err
   841  	}
   842  	estate.mu.Lock()
   843  	defer estate.mu.Unlock()
   844  	for _, p := range ports {
   845  		estate.globalPorts[p] = true
   846  	}
   847  	return nil
   848  }
   849  
   850  func (e *environ) ClosePorts(ports []instance.Port) error {
   851  	if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal {
   852  		return fmt.Errorf("invalid firewall mode %q for closing ports on environment", mode)
   853  	}
   854  	estate, err := e.state()
   855  	if err != nil {
   856  		return err
   857  	}
   858  	estate.mu.Lock()
   859  	defer estate.mu.Unlock()
   860  	for _, p := range ports {
   861  		delete(estate.globalPorts, p)
   862  	}
   863  	return nil
   864  }
   865  
   866  func (e *environ) Ports() (ports []instance.Port, err error) {
   867  	if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal {
   868  		return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from environment", mode)
   869  	}
   870  	estate, err := e.state()
   871  	if err != nil {
   872  		return nil, err
   873  	}
   874  	estate.mu.Lock()
   875  	defer estate.mu.Unlock()
   876  	for p := range estate.globalPorts {
   877  		ports = append(ports, p)
   878  	}
   879  	instance.SortPorts(ports)
   880  	return
   881  }
   882  
   883  func (*environ) Provider() environs.EnvironProvider {
   884  	return &providerInstance
   885  }
   886  
   887  type dummyInstance struct {
   888  	state        *environState
   889  	ports        map[instance.Port]bool
   890  	id           instance.Id
   891  	status       string
   892  	machineId    string
   893  	series       string
   894  	firewallMode string
   895  
   896  	mu        sync.Mutex
   897  	addresses []instance.Address
   898  }
   899  
   900  func (inst *dummyInstance) Id() instance.Id {
   901  	return inst.id
   902  }
   903  
   904  func (inst *dummyInstance) Status() string {
   905  	return inst.status
   906  }
   907  
   908  // SetInstanceAddresses sets the addresses associated with the given
   909  // dummy instance.
   910  func SetInstanceAddresses(inst instance.Instance, addrs []instance.Address) {
   911  	inst0 := inst.(*dummyInstance)
   912  	inst0.mu.Lock()
   913  	inst0.addresses = append(inst0.addresses[:0], addrs...)
   914  	inst0.mu.Unlock()
   915  }
   916  
   917  // SetInstanceStatus sets the status associated with the given
   918  // dummy instance.
   919  func SetInstanceStatus(inst instance.Instance, status string) {
   920  	inst0 := inst.(*dummyInstance)
   921  	inst0.mu.Lock()
   922  	inst0.status = status
   923  	inst0.mu.Unlock()
   924  }
   925  
   926  func (inst *dummyInstance) DNSName() (string, error) {
   927  	defer delay()
   928  	return string(inst.id) + ".dns", nil
   929  }
   930  
   931  func (*dummyInstance) Refresh() error {
   932  	return nil
   933  }
   934  
   935  func (inst *dummyInstance) Addresses() ([]instance.Address, error) {
   936  	inst.mu.Lock()
   937  	defer inst.mu.Unlock()
   938  	return append([]instance.Address{}, inst.addresses...), nil
   939  }
   940  
   941  func (inst *dummyInstance) WaitDNSName() (string, error) {
   942  	return common.WaitDNSName(inst)
   943  }
   944  
   945  func (inst *dummyInstance) OpenPorts(machineId string, ports []instance.Port) error {
   946  	defer delay()
   947  	logger.Infof("openPorts %s, %#v", machineId, ports)
   948  	if inst.firewallMode != config.FwInstance {
   949  		return fmt.Errorf("invalid firewall mode %q for opening ports on instance",
   950  			inst.firewallMode)
   951  	}
   952  	if inst.machineId != machineId {
   953  		panic(fmt.Errorf("OpenPorts with mismatched machine id, expected %q got %q", inst.machineId, machineId))
   954  	}
   955  	inst.state.mu.Lock()
   956  	defer inst.state.mu.Unlock()
   957  	inst.state.ops <- OpOpenPorts{
   958  		Env:        inst.state.name,
   959  		MachineId:  machineId,
   960  		InstanceId: inst.Id(),
   961  		Ports:      ports,
   962  	}
   963  	for _, p := range ports {
   964  		inst.ports[p] = true
   965  	}
   966  	return nil
   967  }
   968  
   969  func (inst *dummyInstance) ClosePorts(machineId string, ports []instance.Port) error {
   970  	defer delay()
   971  	if inst.firewallMode != config.FwInstance {
   972  		return fmt.Errorf("invalid firewall mode %q for closing ports on instance",
   973  			inst.firewallMode)
   974  	}
   975  	if inst.machineId != machineId {
   976  		panic(fmt.Errorf("ClosePorts with mismatched machine id, expected %s got %s", inst.machineId, machineId))
   977  	}
   978  	inst.state.mu.Lock()
   979  	defer inst.state.mu.Unlock()
   980  	inst.state.ops <- OpClosePorts{
   981  		Env:        inst.state.name,
   982  		MachineId:  machineId,
   983  		InstanceId: inst.Id(),
   984  		Ports:      ports,
   985  	}
   986  	for _, p := range ports {
   987  		delete(inst.ports, p)
   988  	}
   989  	return nil
   990  }
   991  
   992  func (inst *dummyInstance) Ports(machineId string) (ports []instance.Port, err error) {
   993  	defer delay()
   994  	if inst.firewallMode != config.FwInstance {
   995  		return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from instance",
   996  			inst.firewallMode)
   997  	}
   998  	if inst.machineId != machineId {
   999  		panic(fmt.Errorf("Ports with mismatched machine id, expected %q got %q", inst.machineId, machineId))
  1000  	}
  1001  	inst.state.mu.Lock()
  1002  	defer inst.state.mu.Unlock()
  1003  	for p := range inst.ports {
  1004  		ports = append(ports, p)
  1005  	}
  1006  	instance.SortPorts(ports)
  1007  	return
  1008  }
  1009  
  1010  // providerDelay controls the delay before dummy responds.
  1011  // non empty values in JUJU_DUMMY_DELAY will be parsed as
  1012  // time.Durations into this value.
  1013  var providerDelay time.Duration
  1014  
  1015  // pause execution to simulate the latency of a real provider
  1016  func delay() {
  1017  	if providerDelay > 0 {
  1018  		logger.Infof("pausing for %v", providerDelay)
  1019  		<-time.After(providerDelay)
  1020  	}
  1021  }