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