github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/state/backups/backups.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  /*
     5  Package backups contains all the stand-alone backup-related
     6  functionality for juju state. That functionality is encapsulated by
     7  the backups.Backups type. The package also exposes a few key helpers
     8  and components.
     9  
    10  Backups are not a part of juju state nor of normal state operations.
    11  However, they certainly are tightly coupled with state (the very
    12  subject of backups). This puts backups in an odd position, particularly
    13  with regard to the storage of backup metadata and archives.
    14  
    15  As noted above backups are about state but not a part of state. So
    16  exposing backup-related methods on State would imply the wrong thing.
    17  Thus most of the functionality here is defined at a high level without
    18  relation to state. A few low-level parts or helpers are exposed as
    19  functions to which you pass a state value. Those are kept to a minimum.
    20  
    21  Note that state (and juju as a whole) currently does not have a
    22  persistence layer abstraction to facilitate separating different
    23  persistence needs and implementations. As a consequence, state's
    24  data, whether about how an environment should look or about existing
    25  resources within an environment, is dumped essentially straight into
    26  State's mongo connection. The code in the state package does not
    27  make any distinction between the two (nor does the package clearly
    28  distinguish between state-related abstractions and state-related
    29  data).
    30  
    31  Backups add yet another category, merely taking advantage of State's
    32  mongo for storage. In the interest of making the distinction clear,
    33  among other reasons, backups uses its own database under state's mongo
    34  connection.
    35  */
    36  package backups
    37  
    38  import (
    39  	"io"
    40  	"time"
    41  
    42  	"github.com/juju/errors"
    43  	"github.com/juju/loggo"
    44  	"github.com/juju/utils/filestorage"
    45  
    46  	"github.com/juju/juju/apiserver/params"
    47  )
    48  
    49  const (
    50  	// FilenamePrefix is the prefix used for backup archive files.
    51  	FilenamePrefix = "juju-backup-"
    52  
    53  	// FilenameTemplate is used with time.Time.Format to generate a filename.
    54  	FilenameTemplate = FilenamePrefix + "20060102-150405.tar.gz"
    55  )
    56  
    57  var logger = loggo.GetLogger("juju.state.backups")
    58  
    59  var (
    60  	getFilesToBackUp = GetFilesToBackUp
    61  	getDBDumper      = NewDBDumper
    62  	runCreate        = create
    63  	finishMeta       = func(meta *Metadata, result *createResult) error {
    64  		return meta.MarkComplete(result.size, result.checksum)
    65  	}
    66  	storeArchive = StoreArchive
    67  )
    68  
    69  // StoreArchive sends the backup archive and its metadata to storage.
    70  // It also sets the metadata's ID and Stored values.
    71  func StoreArchive(stor filestorage.FileStorage, meta *Metadata, file io.Reader) error {
    72  	id, err := stor.Add(meta, file)
    73  	if err != nil {
    74  		return errors.Trace(err)
    75  	}
    76  	meta.SetID(id)
    77  	stored, err := stor.Metadata(id)
    78  	if err != nil {
    79  		return errors.Trace(err)
    80  	}
    81  	meta.SetStored(stored.Stored())
    82  	return nil
    83  }
    84  
    85  // Backups is an abstraction around all juju backup-related functionality.
    86  type Backups interface {
    87  	// Create creates and stores a new juju backup archive. It updates
    88  	// the provided metadata.
    89  	Create(meta *Metadata, paths *Paths, dbInfo *DBInfo) error
    90  
    91  	// Add stores the backup archive and returns its new ID.
    92  	Add(archive io.Reader, meta *Metadata) (string, error)
    93  
    94  	// Get returns the metadata and archive file associated with the ID.
    95  	Get(id string) (*Metadata, io.ReadCloser, error)
    96  
    97  	// List returns the metadata for all stored backups.
    98  	List() ([]*Metadata, error)
    99  
   100  	// Remove deletes the backup from storage.
   101  	Remove(id string) error
   102  
   103  	// Restore updates juju's state to the contents of the backup archive.
   104  	Restore(backupId string, args params.RestoreArgs) error
   105  }
   106  
   107  type backups struct {
   108  	storage filestorage.FileStorage
   109  }
   110  
   111  // NewBackups creates a new Backups value using the FileStorage provided.
   112  func NewBackups(stor filestorage.FileStorage) Backups {
   113  	b := backups{
   114  		storage: stor,
   115  	}
   116  	return &b
   117  }
   118  
   119  // Create creates and stores a new juju backup archive and updates the
   120  // provided metadata.
   121  func (b *backups) Create(meta *Metadata, paths *Paths, dbInfo *DBInfo) error {
   122  	meta.Started = time.Now().UTC()
   123  
   124  	// The metadata file will not contain the ID or the "finished" data.
   125  	// However, that information is not as critical. The alternatives
   126  	// are either adding the metadata file to the archive after the fact
   127  	// or adding placeholders here for the finished data and filling
   128  	// them in afterward.  Neither is particularly trivial.
   129  	metadataFile, err := meta.AsJSONBuffer()
   130  	if err != nil {
   131  		return errors.Annotate(err, "while preparing the metadata")
   132  	}
   133  
   134  	// Create the archive.
   135  	filesToBackUp, err := getFilesToBackUp("", paths, meta.Origin.Machine)
   136  	if err != nil {
   137  		return errors.Annotate(err, "while listing files to back up")
   138  	}
   139  	dumper, err := getDBDumper(dbInfo)
   140  	if err != nil {
   141  		return errors.Annotate(err, "while preparing for DB dump")
   142  	}
   143  	args := createArgs{filesToBackUp, dumper, metadataFile}
   144  	result, err := runCreate(&args)
   145  	if err != nil {
   146  		return errors.Annotate(err, "while creating backup archive")
   147  	}
   148  	defer result.archiveFile.Close()
   149  
   150  	// Finalize the metadata.
   151  	err = finishMeta(meta, result)
   152  	if err != nil {
   153  		return errors.Annotate(err, "while updating metadata")
   154  	}
   155  
   156  	// Store the archive.
   157  	err = storeArchive(b.storage, meta, result.archiveFile)
   158  	if err != nil {
   159  		return errors.Annotate(err, "while storing backup archive")
   160  	}
   161  
   162  	return nil
   163  }
   164  
   165  // Add stores the backup archive and returns its new ID.
   166  func (b *backups) Add(archive io.Reader, meta *Metadata) (string, error) {
   167  	// Store the archive.
   168  	err := storeArchive(b.storage, meta, archive)
   169  	if err != nil {
   170  		return "", errors.Annotate(err, "while storing backup archive")
   171  	}
   172  
   173  	return meta.ID(), nil
   174  }
   175  
   176  // Get retrieves the associated metadata and archive file from environment storage.
   177  func (b *backups) Get(id string) (*Metadata, io.ReadCloser, error) {
   178  	rawmeta, archiveFile, err := b.storage.Get(id)
   179  	if err != nil {
   180  		return nil, nil, errors.Trace(err)
   181  	}
   182  
   183  	meta, ok := rawmeta.(*Metadata)
   184  	if !ok {
   185  		return nil, nil, errors.New("did not get a backups.Metadata value from storage")
   186  	}
   187  
   188  	return meta, archiveFile, nil
   189  }
   190  
   191  // List returns the metadata for all stored backups.
   192  func (b *backups) List() ([]*Metadata, error) {
   193  	metaList, err := b.storage.List()
   194  	if err != nil {
   195  		return nil, errors.Trace(err)
   196  	}
   197  	result := make([]*Metadata, len(metaList))
   198  	for i, meta := range metaList {
   199  		m, ok := meta.(*Metadata)
   200  		if !ok {
   201  			msg := "expected backups.Metadata value from storage for %q, got %T"
   202  			return nil, errors.Errorf(msg, meta.ID(), meta)
   203  		}
   204  		result[i] = m
   205  	}
   206  	return result, nil
   207  }
   208  
   209  // Remove deletes the backup from storage.
   210  func (b *backups) Remove(id string) error {
   211  	return errors.Trace(b.storage.Remove(id))
   212  }