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  }