43
44
"launchpad.net/juju-core/instance"
44
45
"launchpad.net/juju-core/log"
45
46
"launchpad.net/juju-core/names"
47
"launchpad.net/juju-core/provider"
46
48
"launchpad.net/juju-core/provider/common"
47
49
"launchpad.net/juju-core/schema"
48
50
"launchpad.net/juju-core/state"
143
145
ops chan<- Operation
144
146
// We have one state for each environment name
145
state map[string]*environState
147
state map[int]*environState
148
151
var providerInstance environProvider
150
155
// environState represents the state of an environment.
151
156
// It can be shared between several environ values,
152
157
// so that a given environment can be opened several times.
153
158
type environState struct {
155
161
ops chan<- Operation
159
165
globalPorts map[instance.Port]bool
160
166
bootstrapped bool
161
167
storageDelay time.Duration
162
storage *dummystorage
163
publicStorage *dummystorage
168
storage *storageServer
169
publicStorage *storageServer
164
170
httpListener net.Listener
165
171
apiServer *apiserver.Server
166
172
apiState *state.State
178
184
var _ tools.SupportsCustomSources = (*environ)(nil)
179
185
var _ environs.Environ = (*environ)(nil)
181
// dummystorage holds the storage for an environState.
182
// There are two instances for each environState
183
// instance, one for public files and one for private.
184
type dummystorage struct {
185
path string // path prefix in http space.
187
files map[string][]byte
188
poisoned map[string]error
191
187
// discardOperations discards all Operations written to it.
192
188
var discardOperations chan<- Operation
252
248
// This is so code in the test suite can trigger Syncs, etc that the API server
253
249
// will see, which will then trigger API watchers, etc.
254
250
func (e *environ) GetStateInAPIServer() *state.State {
255
return e.state().apiState
258
258
// newState creates the state for a new environment with the
265
265
insts: make(map[instance.Id]*dummyInstance),
266
266
globalPorts: make(map[instance.Port]bool),
268
s.storage = newStorage(s, "/"+name+"/private")
269
s.publicStorage = newStorage(s, "/"+name+"/public")
268
s.storage = newStorageServer(s, "/"+name+"/private")
269
s.publicStorage = newStorageServer(s, "/"+name+"/public")
323
323
"state-server": schema.Bool(),
324
324
"broken": schema.String(),
325
325
"secret": schema.String(),
326
"state-id": schema.String(),
327
328
var configDefaults = schema.Defaults{
331
"state-id": schema.Omit,
332
334
type environConfig struct {
346
348
return c.attrs["secret"].(string)
351
func (c *environConfig) stateId() int {
352
idStr, ok := c.attrs["state-id"].(string)
356
id, err := strconv.Atoi(idStr)
358
panic(fmt.Errorf("unexpected state-id %q (should have pre-checked)", idStr))
349
363
func (p *environProvider) newConfig(cfg *config.Config) (*environConfig, error) {
350
364
valid, err := p.Validate(cfg, nil)
380
if idStr, ok := validated["state-id"].(string); ok {
381
if _, err := strconv.Atoi(idStr); err != nil {
382
return nil, fmt.Errorf("invalid state-id %q", idStr)
366
385
// Apply the coerced unknown values back into the config.
367
386
return cfg.Apply(validated)
370
func (e *environ) maybeState() (*environState, bool) {
389
func (e *environ) state() (*environState, error) {
390
stateId := e.ecfg().stateId()
391
if stateId == noStateId {
392
return nil, provider.ErrNotPrepared
371
394
p := &providerInstance
373
396
defer p.mu.Unlock()
374
if state := p.state[e.name]; state != nil {
380
func (e *environ) state() *environState {
381
if state, ok := e.maybeState(); !ok {
382
panic(fmt.Errorf("environment %q is not prepared", e.name))
397
if state := p.state[stateId]; state != nil {
400
return nil, provider.ErrDestroyed
388
403
func (p *environProvider) Open(cfg *config.Config) (environs.Environ, error) {
405
423
func (p *environProvider) Prepare(cfg *config.Config) (environs.Environ, error) {
424
cfg, err := p.prepare(cfg)
431
// prepare is the internal version of Prepare - it prepares the
432
// environment but does not open it.
433
func (p *environProvider) prepare(cfg *config.Config) (*config.Config, error) {
406
434
ecfg, err := p.newConfig(cfg)
411
440
name := cfg.Name()
412
state := p.state[name]
414
if ecfg.stateServer() && len(p.state) != 0 {
416
for oldName := range p.state {
420
panic(fmt.Errorf("cannot share a state between two dummy environs; old %q; new %q", old, name))
441
if ecfg.stateId() != noStateId {
444
// The environment has not been prepared,
445
// so create it and set its state identifier accordingly.
446
if ecfg.stateServer() && len(p.state) != 0 {
447
for _, old := range p.state {
448
panic(fmt.Errorf("cannot share a state between two dummy environs; old %q; new %q", old.name, name))
422
state = newState(name, p.ops)
423
p.state[name] = state
425
// TODO(rog) add an attribute to the configuration which is required for Open?
451
state := newState(name, p.ops)
453
state.id = p.maxStateId
454
p.state[state.id] = state
455
// Add the state id to the configuration we use to
456
// in the returned environment.
457
return cfg.Apply(map[string]interface{}{
458
"state-id": fmt.Sprint(state.id),
430
462
func (*environProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) {
559
597
return nil, nil, errors.New("dummy environment has no state configured")
561
599
if !estate.bootstrapped {
562
return nil, nil, errors.New("dummy environment not bootstrapped")
600
return nil, nil, environs.ErrNotBootstrapped
564
602
return stateInfo(), &api.Info{
565
603
Addrs: []string{estate.apiServer.Addr()},
590
628
if err := e.checkBroken("Destroy"); err != nil {
593
estate, ok := e.maybeState()
631
estate, err := e.state()
633
if err == provider.ErrDestroyed {
597
638
p := &providerInstance
599
delete(p.state, e.name)
640
delete(p.state, estate.id)