github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/blobserver/mongo/mongo.go (about)

     1  /*
     2  Copyright 2014 The Camlistore Authors
     3  
     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
     7  
     8       http://www.apache.org/licenses/LICENSE-2.0
     9  
    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.
    15  */
    16  
    17  /*
    18  Package mongo registers the "mongo" blobserver storage type, storing
    19  blobs using MongoDB.
    20  
    21  Sample (low-level) config:
    22  "/bs/": {
    23      "handler": "storage-mongo",
    24      "handlerArgs": {
    25         "host": "172.17.0.2",
    26         "database": "camlitest"
    27       }
    28  },
    29  
    30  Possible parameters:
    31  host (optional, defaults to localhost)
    32  database (required)
    33  collection (optional, defaults to blobs)
    34  user (optional)
    35  password (optional)
    36  */
    37  package mongo
    38  
    39  import (
    40  	"camlistore.org/pkg/blobserver"
    41  	"camlistore.org/pkg/jsonconfig"
    42  	"camlistore.org/third_party/labix.org/v2/mgo"
    43  )
    44  
    45  type mongoStorage struct {
    46  	c *mgo.Collection
    47  }
    48  
    49  // blobDoc is the document that gets inserted in the MongoDB database
    50  // Its fields are exported because they need to be for the mgo driver to pick them up
    51  type blobDoc struct {
    52  	// Key contains the string representation of a blob reference (e.g. sha1-200d278aa6dd347f494407385ceab316440d5fba).
    53  	Key string
    54  	// Size contains the total size of a blob.
    55  	Size uint32
    56  	// Blob contains the raw blob data of the blob the above Key refers to.
    57  	Blob []byte
    58  }
    59  
    60  func init() {
    61  	blobserver.RegisterStorageConstructor("mongo", blobserver.StorageConstructor(newFromConfig))
    62  }
    63  
    64  func newFromConfig(_ blobserver.Loader, config jsonconfig.Obj) (blobserver.Storage, error) {
    65  	cfg, err := configFromJSON(config)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  	return newMongoStorage(cfg)
    70  }
    71  
    72  func newMongoStorage(cfg config) (blobserver.Storage, error) {
    73  	session, err := getConnection(cfg.url())
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  	c := session.DB(cfg.database).C(cfg.collection)
    78  
    79  	return blobserver.Storage(&mongoStorage{c: c}), nil
    80  
    81  }
    82  
    83  // Config holds the parameters used to connect to MongoDB.
    84  type config struct {
    85  	server     string // Required. Defaults to "localhost" in ConfigFromJSON.
    86  	database   string // Required.
    87  	collection string // Required. Defaults to "blobs" in ConfigFromJSON.
    88  	user       string // Optional, unless the server was configured with auth on.
    89  	password   string // Optional, unless the server was configured with auth on.
    90  }
    91  
    92  func (cfg *config) url() string {
    93  	if cfg.user == "" || cfg.password == "" {
    94  		return cfg.server
    95  	}
    96  	return cfg.user + ":" + cfg.password + "@" + cfg.server + "/" + cfg.database
    97  }
    98  
    99  // ConfigFromJSON populates Config from cfg, and validates
   100  // cfg. It returns an error if cfg fails to validate.
   101  func configFromJSON(cfg jsonconfig.Obj) (config, error) {
   102  	conf := config{
   103  		server:     cfg.OptionalString("host", "localhost"),
   104  		database:   cfg.RequiredString("database"),
   105  		collection: cfg.OptionalString("collection", "blobs"),
   106  		user:       cfg.OptionalString("user", ""),
   107  		password:   cfg.OptionalString("password", ""),
   108  	}
   109  	if err := cfg.Validate(); err != nil {
   110  		return config{}, err
   111  	}
   112  	return conf, nil
   113  }
   114  
   115  func getConnection(url string) (*mgo.Session, error) {
   116  	session, err := mgo.Dial(url)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  	session.SetMode(mgo.Monotonic, true)
   121  	session.SetSafe(&mgo.Safe{}) // so we get an ErrNotFound error when deleting an absent key
   122  	return session, nil
   123  }