github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/api/agent/facade.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package agent 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 ) 13 14 // Life is a local representation of entity life. Should probably be 15 // in a core/life or core/entity package; and quite probably, so 16 // should the ConnFacade interface. 17 type Life string 18 19 const ( 20 Alive Life = "alive" 21 Dying Life = "dying" 22 Dead Life = "dead" 23 ) 24 25 // ConnFacade exposes the parts of the Agent facade needed by the bits 26 // that currently live in apicaller. This criterion is every bit as 27 // terrible as it sounds -- surely there should be a new facade at the 28 // apiserver level somewhere? -- but: 29 // 1) this feels like a convenient/transitional method grouping, not a 30 // fundamental *role*; and 31 // 2) at least it's a narrowed interface, and eschews the object-style 32 // sins of *State/*Entity. 33 // Progress not perfection. 34 type ConnFacade interface { 35 36 // Life returns Alive, Dying, Dead, ErrDenied, or some other error. 37 Life(names.Tag) (Life, error) 38 39 // SetPassword returns nil, ErrDenied, or some other error. 40 SetPassword(names.Tag, string) error 41 } 42 43 // ErrDenied is returned by Life and SetPassword to indicate that the 44 // requested operation is impossible (and hence that the entity is 45 // either dead or gone, and in either case that no further meaningful 46 // interaction is possible). 47 var ErrDenied = errors.New("entity operation impossible") 48 49 // NewConnFacade returns a ConnFacade backed by the supplied APICaller. 50 func NewConnFacade(caller base.APICaller) (ConnFacade, error) { 51 facadeCaller := base.NewFacadeCaller(caller, "Agent") 52 return &connFacade{ 53 caller: facadeCaller, 54 }, nil 55 } 56 57 // connFacade implements ConnFacade. 58 type connFacade struct { 59 caller base.FacadeCaller 60 } 61 62 // Life is part of the ConnFacade interface. 63 func (facade *connFacade) Life(entity names.Tag) (Life, error) { 64 var results params.AgentGetEntitiesResults 65 args := params.Entities{ 66 Entities: []params.Entity{{Tag: entity.String()}}, 67 } 68 err := facade.caller.FacadeCall("GetEntities", args, &results) 69 if err != nil { 70 return "", errors.Trace(err) 71 } 72 if len(results.Entities) != 1 { 73 return "", errors.Errorf("expected 1 result, got %d", len(results.Entities)) 74 } 75 if err := results.Entities[0].Error; err != nil { 76 if params.IsCodeNotFoundOrCodeUnauthorized(err) { 77 return "", ErrDenied 78 } 79 return "", errors.Trace(err) 80 } 81 life := Life(results.Entities[0].Life) 82 switch life { 83 case Alive, Dying, Dead: 84 return life, nil 85 } 86 return "", errors.Errorf("unknown life value %q", life) 87 } 88 89 // SetPassword is part of the ConnFacade interface. 90 func (facade *connFacade) SetPassword(entity names.Tag, password string) error { 91 var results params.ErrorResults 92 args := params.EntityPasswords{ 93 Changes: []params.EntityPassword{{ 94 Tag: entity.String(), 95 Password: password, 96 }}, 97 } 98 err := facade.caller.FacadeCall("SetPasswords", args, &results) 99 if err != nil { 100 return errors.Trace(err) 101 } 102 if len(results.Results) != 1 { 103 return errors.Errorf("expected 1 result, got %d", len(results.Results)) 104 } 105 if err := results.Results[0].Error; err != nil { 106 if params.IsCodeDead(err) { 107 return ErrDenied 108 } else if params.IsCodeNotFoundOrCodeUnauthorized(err) { 109 return ErrDenied 110 } 111 return errors.Trace(err) 112 } 113 return nil 114 }