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