github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/api/lifeflag/facade.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package lifeflag 5 6 import ( 7 "github.com/juju/errors" 8 "gopkg.in/juju/names.v2" 9 10 "github.com/juju/juju/api/base" 11 "github.com/juju/juju/apiserver/params" 12 "github.com/juju/juju/core/life" 13 "github.com/juju/juju/core/watcher" 14 ) 15 16 // NewWatcherFunc exists to let us test Watch properly. 17 type NewWatcherFunc func(base.APICaller, params.NotifyWatchResult) watcher.NotifyWatcher 18 19 // Facade makes calls to the LifeFlag facade. 20 type Facade struct { 21 caller base.FacadeCaller 22 newWatcher NewWatcherFunc 23 } 24 25 // NewFacade returns a new Facade using the supplied caller. 26 func NewFacade(caller base.APICaller, newWatcher NewWatcherFunc) *Facade { 27 return &Facade{ 28 caller: base.NewFacadeCaller(caller, "LifeFlag"), 29 newWatcher: newWatcher, 30 } 31 } 32 33 // ErrNotFound indicates that the requested entity no longer exists. 34 // 35 // We avoid errors.NotFound, because errors.NotFound is non-specific, and 36 // it's our job to communicate *this specific condition*. There are many 37 // possible sources of errors.NotFound in the world, and it's not safe or 38 // sane for a client to treat a generic NotFound as specific to the entity 39 // in question. 40 // 41 // We're still vulnerable to apiservers returning unjustified CodeNotFound 42 // but at least we're safe from accidental errors.NotFound injection in 43 // the api client mechanism. 44 var ErrNotFound = errors.New("entity not found") 45 46 // Watch returns a NotifyWatcher that sends a value whenever the 47 // entity's life value may have changed; or ErrNotFound; or some 48 // other error. 49 func (facade *Facade) Watch(entity names.Tag) (watcher.NotifyWatcher, error) { 50 args := params.Entities{ 51 Entities: []params.Entity{{Tag: entity.String()}}, 52 } 53 var results params.NotifyWatchResults 54 err := facade.caller.FacadeCall("Watch", args, &results) 55 if err != nil { 56 return nil, errors.Trace(err) 57 } 58 if count := len(results.Results); count != 1 { 59 return nil, errors.Errorf("expected 1 Watch result, got %d", count) 60 } 61 result := results.Results[0] 62 if err := result.Error; err != nil { 63 if params.IsCodeNotFound(err) { 64 return nil, ErrNotFound 65 } 66 return nil, errors.Trace(result.Error) 67 } 68 w := facade.newWatcher(facade.caller.RawAPICaller(), result) 69 return w, nil 70 } 71 72 // Life returns the entity's life value; or ErrNotFound; or some 73 // other error. 74 func (facade *Facade) Life(entity names.Tag) (life.Value, error) { 75 args := params.Entities{ 76 Entities: []params.Entity{{Tag: entity.String()}}, 77 } 78 var results params.LifeResults 79 err := facade.caller.FacadeCall("Life", args, &results) 80 if err != nil { 81 return "", errors.Trace(err) 82 } 83 if count := len(results.Results); count != 1 { 84 return "", errors.Errorf("expected 1 Life result, got %d", count) 85 } 86 result := results.Results[0] 87 if err := result.Error; err != nil { 88 if params.IsCodeNotFound(err) { 89 return "", ErrNotFound 90 } 91 return "", errors.Trace(result.Error) 92 } 93 life := life.Value(result.Life) 94 if err := life.Validate(); err != nil { 95 return "", errors.Trace(err) 96 } 97 return life, nil 98 }