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