github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/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  	"github.com/juju/errors"
     8  	"github.com/juju/mgo/v3"
     9  	"github.com/juju/names/v5"
    10  
    11  	"github.com/juju/juju/apiserver/authentication"
    12  	"github.com/juju/juju/apiserver/common"
    13  	apiservererrors "github.com/juju/juju/apiserver/errors"
    14  	"github.com/juju/juju/apiserver/facade"
    15  	"github.com/juju/juju/controller"
    16  	corebase "github.com/juju/juju/core/base"
    17  	"github.com/juju/juju/core/permission"
    18  	"github.com/juju/juju/environs/config"
    19  	"github.com/juju/juju/rpc/params"
    20  	"github.com/juju/juju/state"
    21  	"github.com/juju/juju/state/backups"
    22  )
    23  
    24  // Backend exposes state.State functionality needed by the backups Facade.
    25  type Backend interface {
    26  	IsController() bool
    27  	Machine(id string) (Machine, error)
    28  	MachineBase(id string) (corebase.Base, error)
    29  	MongoSession() *mgo.Session
    30  	ModelTag() names.ModelTag
    31  	ModelType() state.ModelType
    32  	ControllerTag() names.ControllerTag
    33  	ModelConfig() (*config.Config, error)
    34  	ControllerConfig() (controller.Config, error)
    35  	StateServingInfo() (controller.StateServingInfo, error)
    36  	ControllerNodes() ([]state.ControllerNode, error)
    37  }
    38  
    39  // API provides backup-specific API methods.
    40  type API struct {
    41  	backend Backend
    42  	paths   *backups.Paths
    43  
    44  	// machineID is the ID of the machine where the API server is running.
    45  	machineID string
    46  }
    47  
    48  // NewAPI creates a new instance of the Backups API facade.
    49  func NewAPI(backend Backend, resources facade.Resources, authorizer facade.Authorizer) (*API, error) {
    50  	err := authorizer.HasPermission(permission.SuperuserAccess, backend.ControllerTag())
    51  	if err != nil &&
    52  		!errors.Is(err, authentication.ErrorEntityMissingPermission) &&
    53  		!errors.Is(err, errors.NotFound) {
    54  		return nil, errors.Trace(err)
    55  	}
    56  	isControllerAdmin := err == nil
    57  
    58  	if !authorizer.AuthClient() || !isControllerAdmin {
    59  		return nil, apiservererrors.ErrPerm
    60  	}
    61  
    62  	// For now, backup operations are only permitted on the controller model.
    63  	if !backend.IsController() {
    64  		return nil, errors.New("backups are only supported from the controller model\nUse juju switch to select the controller model")
    65  	}
    66  
    67  	if backend.ModelType() == state.ModelTypeCAAS {
    68  		return nil, errors.NotSupportedf("backups on kubernetes controllers")
    69  	}
    70  
    71  	// Get the backup paths.
    72  	dataDir, err := extractResourceValue(resources, "dataDir")
    73  	if err != nil {
    74  		return nil, errors.Trace(err)
    75  	}
    76  	logsDir, err := extractResourceValue(resources, "logDir")
    77  	if err != nil {
    78  		return nil, errors.Trace(err)
    79  	}
    80  
    81  	modelConfig, err := backend.ModelConfig()
    82  	if err != nil {
    83  		return nil, errors.Trace(err)
    84  	}
    85  	backupDir := backups.BackupDirToUse(modelConfig.BackupDir())
    86  	paths := backups.Paths{
    87  		BackupDir: backupDir,
    88  		DataDir:   dataDir,
    89  		LogsDir:   logsDir,
    90  	}
    91  
    92  	// Build the API.
    93  	machineID, err := extractResourceValue(resources, "machineID")
    94  	if err != nil {
    95  		return nil, errors.Trace(err)
    96  	}
    97  	b := API{
    98  		backend:   backend,
    99  		paths:     &paths,
   100  		machineID: machineID,
   101  	}
   102  	return &b, nil
   103  }
   104  
   105  func extractResourceValue(resources facade.Resources, key string) (string, error) {
   106  	res := resources.Get(key)
   107  	strRes, ok := res.(common.StringResource)
   108  	if !ok {
   109  		if res == nil {
   110  			strRes = ""
   111  		} else {
   112  			return "", errors.Errorf("invalid %s resource: %v", key, res)
   113  		}
   114  	}
   115  	return strRes.String(), nil
   116  }
   117  
   118  var newBackups = backups.NewBackups
   119  
   120  // CreateResult updates the result with the information in the
   121  // metadata value.
   122  func CreateResult(meta *backups.Metadata, filename string) params.BackupsMetadataResult {
   123  	var result params.BackupsMetadataResult
   124  
   125  	result.ID = meta.ID()
   126  
   127  	result.Checksum = meta.Checksum()
   128  	result.ChecksumFormat = meta.ChecksumFormat()
   129  	result.Size = meta.Size()
   130  	if meta.Stored() != nil {
   131  		result.Stored = *(meta.Stored())
   132  	}
   133  
   134  	result.Started = meta.Started
   135  	if meta.Finished != nil {
   136  		result.Finished = *meta.Finished
   137  	}
   138  	result.Notes = meta.Notes
   139  
   140  	result.Model = meta.Origin.Model
   141  	result.Machine = meta.Origin.Machine
   142  	result.Hostname = meta.Origin.Hostname
   143  	result.Version = meta.Origin.Version
   144  	result.Base = meta.Origin.Base
   145  
   146  	result.ControllerUUID = meta.Controller.UUID
   147  	result.FormatVersion = meta.FormatVersion
   148  	result.HANodes = meta.Controller.HANodes
   149  	result.ControllerMachineID = meta.Controller.MachineID
   150  	result.ControllerMachineInstanceID = meta.Controller.MachineInstanceID
   151  	result.Filename = filename
   152  
   153  	return result
   154  }