2 * Copyright 2018 Intel Corporation, Inc
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
20 "github.com/mongodb/mongo-go-driver/bson"
21 "github.com/mongodb/mongo-go-driver/bson/primitive"
22 "github.com/mongodb/mongo-go-driver/mongo"
23 "github.com/mongodb/mongo-go-driver/mongo/options"
24 pkgerrors "github.com/pkg/errors"
25 "golang.org/x/net/context"
30 // MongoCollection defines the a subset of MongoDB operations
31 // Note: This interface is defined mainly for mock testing
32 type MongoCollection interface {
33 InsertOne(ctx context.Context, document interface{},
34 opts ...*options.InsertOneOptions) (*mongo.InsertOneResult, error)
35 FindOne(ctx context.Context, filter interface{},
36 opts ...*options.FindOneOptions) *mongo.SingleResult
37 FindOneAndUpdate(ctx context.Context, filter interface{},
38 update interface{}, opts ...*options.FindOneAndUpdateOptions) *mongo.SingleResult
39 DeleteOne(ctx context.Context, filter interface{},
40 opts ...*options.DeleteOptions) (*mongo.DeleteResult, error)
41 Find(ctx context.Context, filter interface{},
42 opts ...*options.FindOptions) (mongo.Cursor, error)
45 // MongoStore is an implementation of the db.Store interface
46 type MongoStore struct {
50 // This exists only for allowing us to mock the collection object
51 // for testing purposes
52 var getCollection = func(coll string, m *MongoStore) MongoCollection {
53 return m.db.Collection(coll)
56 // This exists only for allowing us to mock the DecodeBytes function
57 // Mainly because we cannot construct a SingleResult struct from our
58 // tests. All fields in that struct are private.
59 var decodeBytes = func(sr *mongo.SingleResult) (bson.Raw, error) {
60 return sr.DecodeBytes()
63 // NewMongoStore initializes a Mongo Database with the name provided
64 // If a database with that name exists, it will be returned
65 func NewMongoStore(name string, store *mongo.Database) (Store, error) {
67 ip := "mongodb://" + os.Getenv("DATABASE_IP") + ":27017"
68 mongoClient, err := mongo.NewClient(ip)
73 err = mongoClient.Connect(context.Background())
77 store = mongoClient.Database(name)
85 // HealthCheck verifies if the database is up and running
86 func (m *MongoStore) HealthCheck() error {
88 _, err := decodeBytes(m.db.RunCommand(context.Background(), bson.D{{"serverStatus", 1}}))
90 return pkgerrors.Wrap(err, "Error getting server status")
96 // validateParams checks to see if any parameters are empty
97 func (m *MongoStore) validateParams(args ...string) bool {
98 for _, v := range args {
107 // Create is used to create a DB entry
108 func (m *MongoStore) Create(coll, key, tag string, data interface{}) error {
109 if data == nil || !m.validateParams(coll, key, tag) {
110 return pkgerrors.New("No Data to store")
113 c := getCollection(coll, m)
114 ctx := context.Background()
116 //Insert the data and then add the objectID to the masterTable
117 res, err := c.InsertOne(ctx, bson.D{
121 return pkgerrors.Errorf("Error inserting into database: %s", err.Error())
124 //Add objectID of created data to masterKey document
125 //Create masterkey document if it does not exist
126 filter := bson.D{{"key", key}}
128 _, err = decodeBytes(
134 {tag, res.InsertedID},
137 options.FindOneAndUpdate().SetUpsert(true).SetReturnDocument(options.After)))
140 return pkgerrors.Errorf("Error updating master table: %s", err.Error())
146 // Unmarshal implements an unmarshaler for bson data that
147 // is produced from the mongo database
148 func (m *MongoStore) Unmarshal(inp []byte, out interface{}) error {
149 err := bson.Unmarshal(inp, out)
151 return pkgerrors.Wrap(err, "Unmarshaling bson")
156 // Read method returns the data stored for this key and for this particular tag
157 func (m *MongoStore) Read(coll, key, tag string) ([]byte, error) {
158 if !m.validateParams(coll, key, tag) {
159 return nil, pkgerrors.New("Mandatory fields are missing")
162 c := getCollection(coll, m)
163 ctx := context.Background()
165 //Get the masterkey document based on given key
166 filter := bson.D{{"key", key}}
167 keydata, err := decodeBytes(c.FindOne(context.Background(), filter))
169 return nil, pkgerrors.Errorf("Error finding master table: %s", err.Error())
172 //Read the tag objectID from document
173 tagoid, ok := keydata.Lookup(tag).ObjectIDOK()
175 return nil, pkgerrors.Errorf("Error finding objectID for tag %s", tag)
178 //Use tag objectID to read the data from store
179 filter = bson.D{{"_id", tagoid}}
180 tagdata, err := decodeBytes(c.FindOne(ctx, filter))
182 return nil, pkgerrors.Errorf("Error reading found object: %s", err.Error())
185 //Return the data as a byte array
186 //Convert string data to byte array using the built-in functions
187 switch tagdata.Lookup(tag).Type {
188 case bson.TypeString:
189 return []byte(tagdata.Lookup(tag).StringValue()), nil
191 return tagdata.Lookup(tag).Value, nil
195 // Helper function that deletes an object by its ID
196 func (m *MongoStore) deleteObjectByID(coll string, objID primitive.ObjectID) error {
198 c := getCollection(coll, m)
199 ctx := context.Background()
201 _, err := c.DeleteOne(ctx, bson.D{{"_id", objID}})
203 return pkgerrors.Errorf("Error Deleting from database: %s", err.Error())
206 log.Printf("Deleted Obj with ID %s", objID.String())
210 // Delete method removes a document from the Database that matches key
211 // TODO: delete all referenced docs if tag is empty string
212 func (m *MongoStore) Delete(coll, key, tag string) error {
213 if !m.validateParams(coll, key, tag) {
214 return pkgerrors.New("Mandatory fields are missing")
217 c := getCollection(coll, m)
218 ctx := context.Background()
220 //Get the masterkey document based on given key
221 filter := bson.D{{"key", key}}
222 //Remove the tag ID entry from masterkey table
230 keydata, err := decodeBytes(c.FindOneAndUpdate(ctx, filter, update,
231 options.FindOneAndUpdate().SetReturnDocument(options.Before)))
233 //No document was found. Return nil.
234 if err == mongo.ErrNoDocuments {
237 //Return any other error that was found.
238 return pkgerrors.Errorf("Error decoding master table after update: %s",
242 //Read the tag objectID from document
243 elems, err := keydata.Elements()
245 return pkgerrors.Errorf("Error reading elements from database: %s", err.Error())
248 tagoid, ok := keydata.Lookup(tag).ObjectIDOK()
250 return pkgerrors.Errorf("Error finding objectID for tag %s", tag)
253 //Use tag objectID to read the data from store
254 err = m.deleteObjectByID(coll, tagoid)
256 return pkgerrors.Errorf("Error deleting from database: %s", err.Error())
259 //Delete master table if no more tags left
260 //_id, key and tag should be elements in before doc
261 //if master table needs to be removed too
263 keyid, ok := keydata.Lookup("_id").ObjectIDOK()
265 return pkgerrors.Errorf("Error finding objectID for key %s", key)
267 err = m.deleteObjectByID(coll, keyid)
269 return pkgerrors.Errorf("Error deleting master table from database: %s", err.Error())
276 // ReadAll is used to get all documents in db of a particular tag
277 func (m *MongoStore) ReadAll(coll, tag string) (map[string][]byte, error) {
278 if !m.validateParams(coll, tag) {
279 return nil, pkgerrors.New("Missing collection or tag name")
282 c := getCollection(coll, m)
283 ctx := context.Background()
285 //Get all master tables in this collection
291 cursor, err := c.Find(ctx, filter)
293 return nil, pkgerrors.Errorf("Error reading from database: %s", err.Error())
295 defer cursor.Close(ctx)
297 //Iterate over all the master tables
298 result := make(map[string][]byte)
299 for cursor.Next(ctx) {
300 d, err := cursor.DecodeBytes()
302 log.Printf("Unable to decode data in Readall: %s", err.Error())
306 //Read key of each master table
307 key, ok := d.Lookup("key").StringValueOK()
309 log.Printf("Unable to read key string from mastertable %s", err.Error())
313 //Get objectID of tag document
314 tid, ok := d.Lookup(tag).ObjectIDOK()
316 log.Printf("Did not find tag: %s", tag)
320 //Find tag document and unmarshal it into []byte
321 tagData, err := decodeBytes(c.FindOne(ctx, bson.D{{"_id", tid}}))
323 log.Printf("Unable to decode tag data %s", err.Error())
326 result[key] = tagData.Lookup(tag).Value
329 if len(result) == 0 {
330 return result, pkgerrors.Errorf("Did not find any objects with tag: %s", tag)