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