github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/payload/persistence/mongo.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package persistence 5 6 import ( 7 "fmt" 8 "strings" 9 10 "github.com/juju/errors" 11 "github.com/juju/names" 12 "gopkg.in/juju/charm.v6-unstable" 13 "gopkg.in/mgo.v2/bson" 14 "gopkg.in/mgo.v2/txn" 15 16 "github.com/juju/juju/payload" 17 ) 18 19 const ( 20 payloadsC = "payloads" 21 ) 22 23 // Collections is the list of names of the mongo collections where state 24 // is stored for payloads. 25 // TODO(ericsnow) Not needed anymore...modify for a new registration scheme? 26 var Collections = []string{ 27 payloadsC, 28 } 29 30 // TODO(ericsnow) Move the methods under their own type (payloadcollection?). 31 32 func (pp Persistence) extractPayload(id string, payloadDocs map[string]payloadDoc) (*payload.Payload, bool) { 33 doc, ok := payloadDocs[id] 34 if !ok { 35 return nil, false 36 } 37 p := doc.payload(pp.unit) 38 return &p, true 39 } 40 41 func (pp Persistence) all(query bson.D, docs interface{}) error { 42 return errors.Trace(pp.st.All(payloadsC, query, docs)) 43 } 44 45 func (pp Persistence) allID(query bson.D, docs interface{}) error { 46 if query != nil { 47 query = bson.D{{"_id", query}} 48 } 49 return errors.Trace(pp.all(query, docs)) 50 } 51 52 func (pp Persistence) payloadID(id string) string { 53 // TODO(ericsnow) Drop the unit part. 54 return fmt.Sprintf("payload#%s#%s", pp.unit, id) 55 } 56 57 func (pp Persistence) extractPayloadID(docID string) string { 58 parts := strings.Split(docID, "#") 59 return parts[len(parts)-1] 60 } 61 62 func (pp Persistence) newInsertPayloadOps(id string, p payload.Payload) []txn.Op { 63 var ops []txn.Op 64 65 doc := pp.newPayloadDoc(id, p) 66 ops = append(ops, txn.Op{ 67 C: payloadsC, 68 Id: doc.DocID, 69 Assert: txn.DocMissing, 70 Insert: doc, 71 }) 72 73 return ops 74 } 75 76 func (pp Persistence) newSetRawStatusOps(id, status string) []txn.Op { 77 id = pp.payloadID(id) 78 updates := bson.D{ 79 {"state", status}, 80 } 81 return []txn.Op{{ 82 C: payloadsC, 83 Id: id, 84 Assert: txn.DocExists, 85 Update: bson.D{{"$set", updates}}, 86 }} 87 } 88 89 func (pp Persistence) newRemovePayloadOps(id string) []txn.Op { 90 id = pp.payloadID(id) 91 return []txn.Op{{ 92 C: payloadsC, 93 Id: id, 94 Assert: txn.DocExists, 95 Remove: true, 96 }} 97 } 98 99 // payloadDoc is the top-level document for payloads. 100 type payloadDoc struct { 101 DocID string `bson:"_id"` 102 ModelUUID string `bson:"model-uuid"` 103 104 UnitID string `bson:"unitid"` 105 106 Name string `bson:"name"` 107 Type string `bson:"type"` 108 109 // TODO(ericsnow) Store status in the "statuses" collection? 110 111 State string `bson:"state"` 112 113 // TODO(ericsnow) Store labels in the "annotations" collection? 114 115 Labels []string `bson:"labels"` 116 117 RawID string `bson:"rawid"` 118 } 119 120 func (d payloadDoc) payload(unit string) payload.Payload { 121 labels := make([]string, len(d.Labels)) 122 copy(labels, d.Labels) 123 p := payload.Payload{ 124 PayloadClass: d.definition(), 125 ID: d.RawID, 126 Status: d.State, 127 Labels: labels, 128 Unit: unit, 129 } 130 return p 131 } 132 133 func (d payloadDoc) definition() charm.PayloadClass { 134 definition := charm.PayloadClass{ 135 Name: d.Name, 136 Type: d.Type, 137 } 138 return definition 139 } 140 141 func (d payloadDoc) match(name, rawID string) bool { 142 if d.Name != name { 143 return false 144 } 145 if d.RawID != rawID { 146 return false 147 } 148 return true 149 } 150 151 func (pp Persistence) newPayloadDoc(id string, p payload.Payload) *payloadDoc { 152 id = pp.payloadID(id) 153 154 definition := p.PayloadClass 155 156 labels := make([]string, len(p.Labels)) 157 copy(labels, p.Labels) 158 159 return &payloadDoc{ 160 DocID: id, 161 UnitID: pp.unit, 162 163 Name: definition.Name, 164 Type: definition.Type, 165 166 State: p.Status, 167 168 Labels: labels, 169 170 RawID: p.ID, 171 } 172 } 173 174 func (pp Persistence) allPayloads() (map[string]payloadDoc, error) { 175 var docs []payloadDoc 176 query := bson.D{{"unitid", pp.unit}} 177 if err := pp.all(query, &docs); err != nil { 178 return nil, errors.Trace(err) 179 } 180 181 results := make(map[string]payloadDoc) 182 for _, doc := range docs { 183 id := pp.extractPayloadID(doc.DocID) 184 results[id] = doc 185 } 186 return results, nil 187 } 188 189 func (pp Persistence) payloads(ids []string) (map[string]payloadDoc, error) { 190 fullIDs := make([]string, len(ids)) 191 idMap := make(map[string]string, len(ids)) 192 for i, id := range ids { 193 fullID := pp.payloadID(id) 194 fullIDs[i] = fullID 195 idMap[fullID] = id 196 } 197 198 var docs []payloadDoc 199 query := bson.D{{"$in", fullIDs}} 200 if err := pp.allID(query, &docs); err != nil { 201 return nil, errors.Trace(err) 202 } 203 204 results := make(map[string]payloadDoc) 205 for _, doc := range docs { 206 fullID := dropModelUUID(doc.DocID) 207 id := idMap[fullID] 208 results[id] = doc 209 } 210 return results, nil 211 } 212 213 func dropModelUUID(id string) string { 214 fullID := id 215 parts := strings.SplitN(fullID, ":", 2) 216 if len(parts) == 2 { 217 if names.IsValidModel(parts[0]) { 218 fullID = parts[1] 219 } 220 } 221 return fullID 222 }