github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/client/backups/backups.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package backups
     5  
     6  import (
     7  	"io"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/loggo"
    11  	"gopkg.in/juju/names.v2"
    12  	"gopkg.in/mgo.v2"
    13  
    14  	"github.com/juju/juju/apiserver/common"
    15  	"github.com/juju/juju/apiserver/facade"
    16  	"github.com/juju/juju/apiserver/params"
    17  	"github.com/juju/juju/controller"
    18  	"github.com/juju/juju/environs/config"
    19  	"github.com/juju/juju/permission"
    20  	"github.com/juju/juju/state"
    21  	"github.com/juju/juju/state/backups"
    22  )
    23  
    24  var logger = loggo.GetLogger("juju.apiserver.backups")
    25  
    26  // Backend exposes state.State functionality needed by the backups Facade.
    27  type Backend interface {
    28  	IsController() bool
    29  	Machine(id string) (*state.Machine, error)
    30  	MachineSeries(id string) (string, error)
    31  	MongoSession() *mgo.Session
    32  	MongoVersion() (string, error)
    33  	ModelTag() names.ModelTag
    34  	ControllerTag() names.ControllerTag
    35  	ModelConfig() (*config.Config, error)
    36  	ControllerConfig() (controller.Config, error)
    37  	StateServingInfo() (state.StateServingInfo, error)
    38  	RestoreInfo() *state.RestoreInfo
    39  }
    40  
    41  // API provides backup-specific API methods.
    42  type API struct {
    43  	backend Backend
    44  	paths   *backups.Paths
    45  
    46  	// machineID is the ID of the machine where the API server is running.
    47  	machineID string
    48  }
    49  
    50  // APIv2 serves backup-specific API methods for version 2.
    51  type APIv2 struct {
    52  	*API
    53  }
    54  
    55  func NewAPIv2(backend Backend, resources facade.Resources, authorizer facade.Authorizer) (*APIv2, error) {
    56  	api, err := NewAPI(backend, resources, authorizer)
    57  	if err != nil {
    58  		return nil, errors.Trace(err)
    59  	}
    60  	return &APIv2{api}, nil
    61  }
    62  
    63  // NewAPI creates a new instance of the Backups API facade.
    64  func NewAPI(backend Backend, resources facade.Resources, authorizer facade.Authorizer) (*API, error) {
    65  	isControllerAdmin, err := authorizer.HasPermission(permission.SuperuserAccess, backend.ControllerTag())
    66  	if err != nil && !errors.IsNotFound(err) {
    67  		return nil, errors.Trace(err)
    68  	}
    69  
    70  	if !authorizer.AuthClient() || !isControllerAdmin {
    71  		return nil, common.ErrPerm
    72  	}
    73  
    74  	// For now, backup operations are only permitted on the controller model.
    75  	if !backend.IsController() {
    76  		return nil, errors.New("backups are only supported from the controller model\nUse juju switch to select the controller model")
    77  	}
    78  
    79  	// Get the backup paths.
    80  	dataDir, err := extractResourceValue(resources, "dataDir")
    81  	if err != nil {
    82  		return nil, errors.Trace(err)
    83  	}
    84  	logsDir, err := extractResourceValue(resources, "logDir")
    85  	if err != nil {
    86  		return nil, errors.Trace(err)
    87  	}
    88  
    89  	config, err := backend.ModelConfig()
    90  	if err != nil {
    91  		return nil, errors.Trace(err)
    92  	}
    93  	backupDir := config.BackupDir()
    94  
    95  	paths := backups.Paths{
    96  		BackupDir: backupDir,
    97  		DataDir:   dataDir,
    98  		LogsDir:   logsDir,
    99  	}
   100  
   101  	// Build the API.
   102  	machineID, err := extractResourceValue(resources, "machineID")
   103  	if err != nil {
   104  		return nil, errors.Trace(err)
   105  	}
   106  	b := API{
   107  		backend:   backend,
   108  		paths:     &paths,
   109  		machineID: machineID,
   110  	}
   111  	return &b, nil
   112  }
   113  
   114  func extractResourceValue(resources facade.Resources, key string) (string, error) {
   115  	res := resources.Get(key)
   116  	strRes, ok := res.(common.StringResource)
   117  	if !ok {
   118  		if res == nil {
   119  			strRes = ""
   120  		} else {
   121  			return "", errors.Errorf("invalid %s resource: %v", key, res)
   122  		}
   123  	}
   124  	return strRes.String(), nil
   125  }
   126  
   127  var newBackups = func(backend Backend) (backups.Backups, io.Closer) {
   128  	stor := backups.NewStorage(backend)
   129  	return backups.NewBackups(stor), stor
   130  }
   131  
   132  // CreateResult updates the result with the information in the
   133  // metadata value.
   134  func CreateResult(meta *backups.Metadata, filename string) params.BackupsMetadataResult {
   135  	var result params.BackupsMetadataResult
   136  
   137  	result.ID = meta.ID()
   138  
   139  	result.Checksum = meta.Checksum()
   140  	result.ChecksumFormat = meta.ChecksumFormat()
   141  	result.Size = meta.Size()
   142  	if meta.Stored() != nil {
   143  		result.Stored = *(meta.Stored())
   144  	}
   145  
   146  	result.Started = meta.Started
   147  	if meta.Finished != nil {
   148  		result.Finished = *meta.Finished
   149  	}
   150  	result.Notes = meta.Notes
   151  
   152  	result.Model = meta.Origin.Model
   153  	result.Machine = meta.Origin.Machine
   154  	result.Hostname = meta.Origin.Hostname
   155  	result.Version = meta.Origin.Version
   156  	result.Series = meta.Origin.Series
   157  
   158  	// TODO(wallyworld) - remove these ASAP
   159  	// These are only used by the restore CLI when re-bootstrapping.
   160  	// We will use a better solution but the way restore currently
   161  	// works, we need them and they are no longer available via
   162  	// bootstrap config. We will need to fix how re-bootstrap deals
   163  	// with these keys to address the issue.
   164  	result.CACert = meta.CACert
   165  	result.CAPrivateKey = meta.CAPrivateKey
   166  	result.Filename = filename
   167  
   168  	return result
   169  }
   170  
   171  // MetadataFromResult returns a new Metadata based on the result. The ID
   172  // of the metadata is not set. Call meta.SetID() if that is desired.
   173  // Likewise with Stored and meta.SetStored().
   174  func MetadataFromResult(result params.BackupsMetadataResult) *backups.Metadata {
   175  	meta := backups.NewMetadata()
   176  	meta.Started = result.Started
   177  	if !result.Finished.IsZero() {
   178  		meta.Finished = &result.Finished
   179  	}
   180  	meta.Origin.Model = result.Model
   181  	meta.Origin.Machine = result.Machine
   182  	meta.Origin.Hostname = result.Hostname
   183  	meta.Origin.Version = result.Version
   184  	meta.Origin.Series = result.Series
   185  	meta.Notes = result.Notes
   186  	meta.SetFileInfo(result.Size, result.Checksum, result.ChecksumFormat)
   187  	return meta
   188  }