github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/state/restore.go (about)

     1  // Copyright 2014 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  	"gopkg.in/mgo.v2"
     9  	"gopkg.in/mgo.v2/bson"
    10  	"gopkg.in/mgo.v2/txn"
    11  )
    12  
    13  // RestoreStatus is the type of the statuses
    14  type RestoreStatus string
    15  
    16  const (
    17  	currentRestoreId = "current"
    18  
    19  	// UnknownRestoreStatus is the initial status for restoreInfoDoc.
    20  	UnknownRestoreStatus RestoreStatus = "UNKNOWN"
    21  	// RestorePending is a status to signal that a restore is about to start
    22  	// any change done in this status will be lost.
    23  	RestorePending RestoreStatus = "PENDING"
    24  	// RestoreInProgress indicates that a Restore is in progress.
    25  	RestoreInProgress RestoreStatus = "RESTORING"
    26  	// RestoreFinished it is set by restore upon a succesful run.
    27  	RestoreFinished RestoreStatus = "RESTORED"
    28  	// RestoreChecked is set when the server comes up after a succesful restore.
    29  	RestoreChecked RestoreStatus = "CHECKED"
    30  	// RestoreFailed indicates that the process failed in a recoverable step.
    31  	RestoreFailed RestoreStatus = "FAILED"
    32  )
    33  
    34  type restoreInfoDoc struct {
    35  	Id     string        `bson:"_id"`
    36  	Status RestoreStatus `bson:"status"`
    37  }
    38  
    39  // RestoreInfo its used to syncronize Restore and machine agent
    40  type RestoreInfo struct {
    41  	st  *State
    42  	doc restoreInfoDoc
    43  }
    44  
    45  // Status returns the current Restore doc status
    46  func (info *RestoreInfo) Status() RestoreStatus {
    47  	return info.doc.Status
    48  }
    49  
    50  // SetStatus sets the status of the current restore. Checks are made
    51  // to ensure that status changes are performed in the correct order.
    52  func (info *RestoreInfo) SetStatus(status RestoreStatus) error {
    53  	var assertSane bson.D
    54  
    55  	if status == RestoreInProgress {
    56  		assertSane = bson.D{{"status", RestorePending}}
    57  	}
    58  	if status == RestoreChecked {
    59  		assertSane = bson.D{{"status", RestoreFinished}}
    60  	}
    61  
    62  	ops := []txn.Op{{
    63  		C:      restoreInfoC,
    64  		Id:     currentRestoreId,
    65  		Assert: assertSane,
    66  		Update: bson.D{{"$set", bson.D{{"status", status}}}},
    67  	}}
    68  	err := info.st.runTransaction(ops)
    69  	if err == txn.ErrAborted {
    70  		return errors.Errorf("cannot set restore status to %q: Another "+
    71  			"status change occurred concurrently", status)
    72  	}
    73  	return errors.Annotatef(err, "cannot set restore status to %q", status)
    74  }
    75  
    76  // RestoreInfoSetter returns the current info doc, if it does not exists
    77  // it creates it with UnknownRestoreStatus status
    78  func (st *State) RestoreInfoSetter() (*RestoreInfo, error) {
    79  	doc := restoreInfoDoc{}
    80  	restoreInfo, closer := st.getCollection(restoreInfoC)
    81  	defer closer()
    82  	err := restoreInfo.Find(bson.M{"_id": currentRestoreId}).One(&doc)
    83  	if err == nil {
    84  		return &RestoreInfo{st: st, doc: doc}, nil
    85  	}
    86  
    87  	if err != mgo.ErrNotFound {
    88  		return nil, errors.Annotate(err, "cannot read restore info")
    89  	}
    90  	doc = restoreInfoDoc{
    91  		Id:     currentRestoreId,
    92  		Status: UnknownRestoreStatus,
    93  	}
    94  	ops := []txn.Op{{
    95  		C:      restoreInfoC,
    96  		Id:     currentRestoreId,
    97  		Assert: txn.DocMissing,
    98  		Insert: doc,
    99  	}}
   100  
   101  	if err := st.runTransaction(ops); err != nil {
   102  		return nil, errors.Annotate(err, "cannot create restore info")
   103  	}
   104  
   105  	return &RestoreInfo{st: st, doc: doc}, nil
   106  }