~themue/juju-core/049-prepare-ec2

« back to all changes in this revision

Viewing changes to provider/dummy/environs.go

  • Committer: Tarmac
  • Author(s): Roger Peppe, John Arbash Meinel
  • Date: 2013-10-02 12:38:13 UTC
  • mfrom: (1717.1.42 375-dummy-prepare-state-id)
  • Revision ID: tarmac-20131002123813-3tidl1lj00d3a29c
[r=rogpeppe] environs/dummy: add state-id

This makes the dummy provider more like the other
providers in that the Environ does not refer
directly to the bootstrapped state, but is looked
up when operations are performed on it.

This is also a test case for adding attributes at Prepare
time - many tests needed changing to correctly
deal with this.

https://codereview.appspot.com/14207046/

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
        "net"
29
29
        "net/http"
30
30
        "os"
 
31
        "strconv"
31
32
        "strings"
32
33
        "sync"
33
34
        "time"
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"
142
144
        mu  sync.Mutex
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
        maxStateId int
146
149
}
147
150
 
148
151
var providerInstance environProvider
149
152
 
 
153
const noStateId = 0
 
154
 
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 {
 
159
        id            int
154
160
        name          string
155
161
        ops           chan<- Operation
156
162
        mu            sync.Mutex
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)
180
186
 
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.
186
 
        state    *environState
187
 
        files    map[string][]byte
188
 
        poisoned map[string]error
189
 
}
190
 
 
191
187
// discardOperations discards all Operations written to it.
192
188
var discardOperations chan<- Operation
193
189
 
221
217
                s.httpListener.Close()
222
218
                s.destroy()
223
219
        }
224
 
        providerInstance.state = make(map[string]*environState)
 
220
        providerInstance.state = make(map[int]*environState)
225
221
        if testing.MgoAddr != "" {
226
222
                testing.MgoReset()
227
223
        }
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
 
251
        st, err := e.state()
 
252
        if err != nil {
 
253
                panic(err)
 
254
        }
 
255
        return st.apiState
256
256
}
257
257
 
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),
267
267
        }
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")
270
270
        s.listen()
271
271
        return s
272
272
}
323
323
        "state-server": schema.Bool(),
324
324
        "broken":       schema.String(),
325
325
        "secret":       schema.String(),
 
326
        "state-id":     schema.String(),
326
327
}
327
328
var configDefaults = schema.Defaults{
328
 
        "broken": "",
329
 
        "secret": "pork",
 
329
        "broken":   "",
 
330
        "secret":   "pork",
 
331
        "state-id": schema.Omit,
330
332
}
331
333
 
332
334
type environConfig struct {
346
348
        return c.attrs["secret"].(string)
347
349
}
348
350
 
 
351
func (c *environConfig) stateId() int {
 
352
        idStr, ok := c.attrs["state-id"].(string)
 
353
        if !ok {
 
354
                return noStateId
 
355
        }
 
356
        id, err := strconv.Atoi(idStr)
 
357
        if err != nil {
 
358
                panic(fmt.Errorf("unexpected state-id %q (should have pre-checked)", idStr))
 
359
        }
 
360
        return id
 
361
}
 
362
 
349
363
func (p *environProvider) newConfig(cfg *config.Config) (*environConfig, error) {
350
364
        valid, err := p.Validate(cfg, nil)
351
365
        if err != nil {
363
377
        if err != nil {
364
378
                return nil, err
365
379
        }
 
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)
 
383
                }
 
384
        }
366
385
        // Apply the coerced unknown values back into the config.
367
386
        return cfg.Apply(validated)
368
387
}
369
388
 
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
 
393
        }
371
394
        p := &providerInstance
372
395
        p.mu.Lock()
373
396
        defer p.mu.Unlock()
374
 
        if state := p.state[e.name]; state != nil {
375
 
                return state, true
376
 
        }
377
 
        return nil, false
378
 
}
379
 
 
380
 
func (e *environ) state() *environState {
381
 
        if state, ok := e.maybeState(); !ok {
382
 
                panic(fmt.Errorf("environment %q is not prepared", e.name))
383
 
        } else {
384
 
                return state
385
 
        }
 
397
        if state := p.state[stateId]; state != nil {
 
398
                return state, nil
 
399
        }
 
400
        return nil, provider.ErrDestroyed
386
401
}
387
402
 
388
403
func (p *environProvider) Open(cfg *config.Config) (environs.Environ, error) {
392
407
        if err != nil {
393
408
                return nil, err
394
409
        }
 
410
        if ecfg.stateId() == noStateId {
 
411
                return nil, provider.ErrNotPrepared
 
412
        }
395
413
        env := &environ{
396
414
                name:         ecfg.Name(),
397
415
                ecfgUnlocked: ecfg,
403
421
}
404
422
 
405
423
func (p *environProvider) Prepare(cfg *config.Config) (environs.Environ, error) {
 
424
        cfg, err := p.prepare(cfg)
 
425
        if err != nil {
 
426
                return nil, err
 
427
        }
 
428
        return p.Open(cfg)
 
429
}
 
430
 
 
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)
407
435
        if err != nil {
408
436
                return nil, err
409
437
        }
410
438
        p.mu.Lock()
 
439
        defer p.mu.Unlock()
411
440
        name := cfg.Name()
412
 
        state := p.state[name]
413
 
        if state == nil {
414
 
                if ecfg.stateServer() && len(p.state) != 0 {
415
 
                        var old string
416
 
                        for oldName := range p.state {
417
 
                                old = oldName
418
 
                                break
419
 
                        }
420
 
                        panic(fmt.Errorf("cannot share a state between two dummy environs; old %q; new %q", old, name))
 
441
        if ecfg.stateId() != noStateId {
 
442
                return cfg, nil
 
443
        }
 
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))
421
449
                }
422
 
                state = newState(name, p.ops)
423
 
                p.state[name] = state
424
450
        }
425
 
        // TODO(rog) add an attribute to the configuration which is required for Open?
426
 
        p.mu.Unlock()
427
 
        return p.Open(cfg)
 
451
        state := newState(name, p.ops)
 
452
        p.maxStateId++
 
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),
 
459
        })
428
460
}
429
461
 
430
462
func (*environProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) {
512
544
                return fmt.Errorf("cannot make bootstrap config: %v", err)
513
545
        }
514
546
 
515
 
        estate := e.state()
 
547
        estate, err := e.state()
 
548
        if err != nil {
 
549
                return err
 
550
        }
516
551
        estate.mu.Lock()
517
552
        defer estate.mu.Unlock()
518
553
        if estate.bootstrapped {
549
584
}
550
585
 
551
586
func (e *environ) StateInfo() (*state.Info, *api.Info, error) {
552
 
        estate := e.state()
 
587
        estate, err := e.state()
 
588
        if err != nil {
 
589
                return nil, nil, err
 
590
        }
553
591
        estate.mu.Lock()
554
592
        defer estate.mu.Unlock()
555
593
        if err := e.checkBroken("StateInfo"); err != nil {
559
597
                return nil, nil, errors.New("dummy environment has no state configured")
560
598
        }
561
599
        if !estate.bootstrapped {
562
 
                return nil, nil, errors.New("dummy environment not bootstrapped")
 
600
                return nil, nil, environs.ErrNotBootstrapped
563
601
        }
564
602
        return stateInfo(), &api.Info{
565
603
                Addrs:  []string{estate.apiServer.Addr()},
590
628
        if err := e.checkBroken("Destroy"); err != nil {
591
629
                return err
592
630
        }
593
 
        estate, ok := e.maybeState()
594
 
        if !ok {
595
 
                return nil
 
631
        estate, err := e.state()
 
632
        if err != nil {
 
633
                if err == provider.ErrDestroyed {
 
634
                        return nil
 
635
                }
 
636
                return err
596
637
        }
597
638
        p := &providerInstance
598
639
        p.mu.Lock()
599
 
        delete(p.state, e.name)
 
640
        delete(p.state, estate.id)
600
641
        p.mu.Unlock()
601
642
 
602
643
        estate.mu.Lock()
616
657
        if err := e.checkBroken("StartInstance"); err != nil {
617
658
                return nil, nil, err
618
659
        }
619
 
        estate := e.state()
 
660
        estate, err := e.state()
 
661
        if err != nil {
 
662
                return nil, nil, err
 
663
        }
620
664
        estate.mu.Lock()
621
665
        defer estate.mu.Unlock()
622
666
        if machineConfig.MachineNonce == "" {
693
737
        if err := e.checkBroken("StopInstance"); err != nil {
694
738
                return err
695
739
        }
696
 
        estate := e.state()
 
740
        estate, err := e.state()
 
741
        if err != nil {
 
742
                return err
 
743
        }
697
744
        estate.mu.Lock()
698
745
        defer estate.mu.Unlock()
699
746
        for _, i := range is {
714
761
        if len(ids) == 0 {
715
762
                return nil, nil
716
763
        }
717
 
        estate := e.state()
 
764
        estate, err := e.state()
 
765
        if err != nil {
 
766
                return nil, err
 
767
        }
718
768
        estate.mu.Lock()
719
769
        defer estate.mu.Unlock()
720
770
        notFound := 0
738
788
                return nil, err
739
789
        }
740
790
        var insts []instance.Instance
741
 
        estate := e.state()
 
791
        estate, err := e.state()
 
792
        if err != nil {
 
793
                return nil, err
 
794
        }
742
795
        estate.mu.Lock()
743
796
        defer estate.mu.Unlock()
744
797
        for _, v := range estate.insts {
751
804
        if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal {
752
805
                return fmt.Errorf("invalid firewall mode %q for opening ports on environment", mode)
753
806
        }
754
 
        estate := e.state()
 
807
        estate, err := e.state()
 
808
        if err != nil {
 
809
                return err
 
810
        }
755
811
        estate.mu.Lock()
756
812
        defer estate.mu.Unlock()
757
813
        for _, p := range ports {
764
820
        if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal {
765
821
                return fmt.Errorf("invalid firewall mode %q for closing ports on environment", mode)
766
822
        }
767
 
        estate := e.state()
 
823
        estate, err := e.state()
 
824
        if err != nil {
 
825
                return err
 
826
        }
768
827
        estate.mu.Lock()
769
828
        defer estate.mu.Unlock()
770
829
        for _, p := range ports {
777
836
        if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal {
778
837
                return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from environment", mode)
779
838
        }
780
 
        estate := e.state()
 
839
        estate, err := e.state()
 
840
        if err != nil {
 
841
                return nil, err
 
842
        }
781
843
        estate.mu.Lock()
782
844
        defer estate.mu.Unlock()
783
845
        for p := range estate.globalPorts {