github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/payload/state/unit.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/loggo" 9 "github.com/juju/utils" 10 11 "github.com/juju/juju/payload" 12 "github.com/juju/juju/payload/persistence" 13 ) 14 15 var logger = loggo.GetLogger("juju.payload.state") 16 17 // TODO(ericsnow) We need a worker to clean up dying payloads. 18 19 // The persistence methods needed for payloads in state. 20 type payloadsPersistence interface { 21 Track(id string, info payload.Payload) (bool, error) 22 // SetStatus updates the status for a payload. 23 SetStatus(id, status string) (bool, error) 24 List(ids ...string) ([]payload.Payload, []string, error) 25 ListAll() ([]payload.Payload, error) 26 LookUp(name, rawID string) (string, error) 27 Untrack(id string) (bool, error) 28 } 29 30 // UnitPayloads provides the functionality related to a unit's 31 // payloads, as needed by state. 32 type UnitPayloads struct { 33 // Persist is the persistence layer that will be used. 34 Persist payloadsPersistence 35 36 // Unit identifies the unit associated with the payloads. This 37 // is the "unit ID" of the targeted unit. 38 Unit string 39 40 // Machine identifies the unit's machine. This is the "machine ID" 41 // of the machine on which the unit is running. 42 Machine string 43 44 newID func() (string, error) 45 } 46 47 // NewUnitPayloads builds a UnitPayloads for a unit. 48 func NewUnitPayloads(st persistence.PersistenceBase, unit, machine string) *UnitPayloads { 49 persist := persistence.NewPersistence(st, unit) 50 return &UnitPayloads{ 51 Persist: persist, 52 Unit: unit, 53 Machine: machine, 54 newID: newID, 55 } 56 } 57 58 func newID() (string, error) { 59 uuid, err := utils.NewUUID() 60 if err != nil { 61 return "", errors.Annotate(err, "could not create new payload ID") 62 } 63 return uuid.String(), nil 64 } 65 66 // TODO(ericsnow) Return the new ID from Track()? 67 68 // Track inserts the provided payload info in state. The new Juju ID 69 // for the payload is returned. 70 func (uw UnitPayloads) Track(pl payload.Payload) error { 71 logger.Tracef("tracking %#v", pl) 72 73 if err := pl.Validate(); err != nil { 74 return errors.NewNotValid(err, "bad payload") 75 } 76 77 id, err := uw.newID() 78 if err != nil { 79 return errors.Trace(err) 80 } 81 82 ok, err := uw.Persist.Track(id, pl) 83 if err != nil { 84 return errors.Trace(err) 85 } 86 if !ok { 87 return errors.NotValidf("payload %s (already in state)", id) 88 } 89 90 return nil 91 } 92 93 // SetStatus updates the raw status for the identified payload to the 94 // provided value. 95 func (uw UnitPayloads) SetStatus(id, status string) error { 96 logger.Tracef("setting payload status for %q to %q", id, status) 97 98 if err := payload.ValidateState(status); err != nil { 99 return errors.Trace(err) 100 } 101 102 found, err := uw.Persist.SetStatus(id, status) 103 if err != nil { 104 return errors.Trace(err) 105 } 106 if !found { 107 return errors.NotFoundf(id) 108 } 109 return nil 110 } 111 112 // List builds the list of payload information for the provided payload 113 // IDs. If none are provided then the list contains the info for all 114 // payloads associated with the unit. Missing payloads 115 // are ignored. 116 func (uw UnitPayloads) List(ids ...string) ([]payload.Result, error) { 117 logger.Tracef("listing %v", ids) 118 var err error 119 var payloads []payload.Payload 120 missingIDs := make(map[string]bool) 121 if len(ids) == 0 { 122 payloads, err = uw.Persist.ListAll() 123 if err != nil { 124 return nil, errors.Trace(err) 125 } 126 for _ = range payloads { 127 ids = append(ids, "") 128 } 129 } else { 130 var missing []string 131 payloads, missing, err = uw.Persist.List(ids...) 132 if err != nil { 133 return nil, errors.Trace(err) 134 } 135 for _, id := range missing { 136 missingIDs[id] = true 137 } 138 } 139 140 var results []payload.Result 141 i := 0 142 for _, id := range ids { 143 if missingIDs[id] { 144 results = append(results, payload.Result{ 145 ID: id, 146 NotFound: true, 147 Error: errors.NotFoundf(id), 148 }) 149 continue 150 } 151 pl := payloads[i] 152 i += 1 153 154 // TODO(ericsnow) Ensure that pl.Unit == uw.Unit? 155 156 result := payload.Result{ 157 ID: id, 158 Payload: &payload.FullPayloadInfo{ 159 Payload: pl, 160 Machine: uw.Machine, 161 }, 162 } 163 if id == "" { 164 // TODO(ericsnow) Do this more efficiently. 165 id, err := uw.LookUp(pl.Name, pl.ID) 166 if err != nil { 167 id = "" 168 result.Error = errors.Trace(err) 169 } 170 result.ID = id 171 } 172 results = append(results, result) 173 } 174 return results, nil 175 } 176 177 // LookUp returns the payload ID for the given name/rawID pair. 178 func (uw UnitPayloads) LookUp(name, rawID string) (string, error) { 179 logger.Tracef("looking up payload id for %s/%s", name, rawID) 180 181 id, err := uw.Persist.LookUp(name, rawID) 182 if err != nil { 183 return "", errors.Trace(err) 184 } 185 return id, nil 186 } 187 188 // Untrack removes the identified payload from state. It does not 189 // trigger the actual destruction of the payload. 190 func (uw UnitPayloads) Untrack(id string) error { 191 logger.Tracef("untracking %q", id) 192 // If the record wasn't found then we're already done. 193 _, err := uw.Persist.Untrack(id) 194 if err != nil { 195 return errors.Trace(err) 196 } 197 return nil 198 }