github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/backups/backups_test.go (about)

     1  // Copyright 2013,2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package backups_test
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  	"path"
    12  	"path/filepath"
    13  
    14  	"github.com/dustin/go-humanize"
    15  	"github.com/juju/collections/set"
    16  	"github.com/juju/errors"
    17  	jc "github.com/juju/testing/checkers"
    18  	gc "gopkg.in/check.v1"
    19  
    20  	"github.com/juju/juju/state/backups"
    21  	backupstesting "github.com/juju/juju/state/backups/testing"
    22  )
    23  
    24  type backupsSuite struct {
    25  	backupstesting.BaseSuite
    26  
    27  	paths *backups.Paths
    28  	api   backups.Backups
    29  
    30  	totalDiskMiB     uint64
    31  	availableDiskMiB uint64
    32  	dirSizeBytes     int64
    33  	dbSizeMiB        int
    34  }
    35  
    36  var _ = gc.Suite(&backupsSuite{}) // Register the suite.
    37  
    38  func (s *backupsSuite) SetUpTest(c *gc.C) {
    39  	s.BaseSuite.SetUpTest(c)
    40  
    41  	s.paths = &backups.Paths{
    42  		BackupDir: c.MkDir(),
    43  		DataDir:   c.MkDir(),
    44  	}
    45  	s.api = backups.NewBackups(s.paths)
    46  	s.PatchValue(backups.AvailableDisk, func(string) uint64 {
    47  		return s.availableDiskMiB
    48  	})
    49  	s.PatchValue(backups.TotalDisk, func(string) uint64 {
    50  		return s.totalDiskMiB
    51  	})
    52  	s.PatchValue(backups.DirSize, func(path string) (int64, error) {
    53  		return s.dirSizeBytes, nil
    54  	})
    55  }
    56  
    57  type fakeDumper struct{}
    58  
    59  func (*fakeDumper) Dump(dumpDir string) error {
    60  	return nil
    61  }
    62  
    63  func (*fakeDumper) IsSnap() bool {
    64  	return false
    65  }
    66  
    67  func (s *backupsSuite) checkFailure(c *gc.C, expected string) {
    68  	s.PatchValue(backups.GetDBDumper, func(*backups.DBInfo) (backups.DBDumper, error) {
    69  		return &fakeDumper{}, nil
    70  	})
    71  
    72  	targets := set.NewStrings("juju", "admin")
    73  	dbInfo := backups.DBInfo{
    74  		Address: "a", Username: "b", Password: "c",
    75  		Targets:      targets,
    76  		ApproxSizeMB: s.dbSizeMiB}
    77  	meta := backupstesting.NewMetadataStarted()
    78  	meta.Notes = "some notes"
    79  
    80  	_, err := s.api.Create(meta, &dbInfo)
    81  	c.Check(err, gc.ErrorMatches, expected)
    82  }
    83  
    84  func (s *backupsSuite) TestCreateOkay(c *gc.C) {
    85  	// Patch the internals.
    86  	archiveFile := io.NopCloser(bytes.NewBufferString("<compressed tarball>"))
    87  	result := backups.NewTestCreateResult(
    88  		archiveFile,
    89  		10,
    90  		"<checksum>",
    91  		path.Join(s.paths.BackupDir, "test-backup.tar.gz"))
    92  	received, testCreate := backups.NewTestCreate(result)
    93  	s.PatchValue(backups.RunCreate, testCreate)
    94  
    95  	rootDir := "<was never set>"
    96  	s.PatchValue(backups.TestGetFilesToBackUp, func(root string, paths *backups.Paths) ([]string, error) {
    97  		rootDir = root
    98  		return []string{"<some file>"}, nil
    99  	})
   100  
   101  	var receivedDBInfo *backups.DBInfo
   102  	s.PatchValue(backups.GetDBDumper, func(info *backups.DBInfo) (backups.DBDumper, error) {
   103  		receivedDBInfo = info
   104  		return nil, nil
   105  	})
   106  
   107  	// Run the backup.
   108  	targets := set.NewStrings("juju", "admin")
   109  	dbInfo := backups.DBInfo{
   110  		Address: "a", Username: "b", Password: "c",
   111  		Targets:      targets,
   112  		ApproxSizeMB: s.dbSizeMiB}
   113  	meta := backupstesting.NewMetadataStarted()
   114  	backupstesting.SetOrigin(meta, "<model ID>", "<machine ID>", "<hostname>")
   115  	meta.Notes = "some notes"
   116  	resultFilename, err := s.api.Create(meta, &dbInfo)
   117  	c.Assert(err, jc.ErrorIsNil)
   118  	c.Assert(resultFilename, gc.Equals, path.Join(s.paths.BackupDir, "test-backup.tar.gz"))
   119  
   120  	// Test the call values.
   121  	resultBackupDir, filesToBackUp, _ := backups.ExposeCreateArgs(received)
   122  	c.Check(resultBackupDir, gc.Equals, s.paths.BackupDir)
   123  	c.Check(filesToBackUp, jc.SameContents, []string{"<some file>"})
   124  
   125  	c.Check(receivedDBInfo.Address, gc.Equals, "a")
   126  	c.Check(receivedDBInfo.Username, gc.Equals, "b")
   127  	c.Check(receivedDBInfo.Password, gc.Equals, "c")
   128  	c.Check(receivedDBInfo.Targets, gc.DeepEquals, targets)
   129  
   130  	c.Check(rootDir, gc.Equals, "")
   131  
   132  	// Check the resulting metadata.
   133  	c.Check(meta.Size(), gc.Equals, int64(10))
   134  	c.Check(meta.Checksum(), gc.Equals, "<checksum>")
   135  	c.Check(meta.Origin.Model, gc.Equals, "<model ID>")
   136  	c.Check(meta.Origin.Machine, gc.Equals, "<machine ID>")
   137  	c.Check(meta.Origin.Hostname, gc.Equals, "<hostname>")
   138  	c.Check(meta.Notes, gc.Equals, "some notes")
   139  }
   140  
   141  func (s *backupsSuite) TestCreateFailToListFiles(c *gc.C) {
   142  	s.PatchValue(backups.TestGetFilesToBackUp, func(root string, paths *backups.Paths) ([]string, error) {
   143  		return nil, errors.New("failed!")
   144  	})
   145  
   146  	s.checkFailure(c, "while listing files to back up: failed!")
   147  }
   148  
   149  func (s *backupsSuite) TestCreateFailToCreate(c *gc.C) {
   150  	s.PatchValue(backups.TestGetFilesToBackUp, func(root string, paths *backups.Paths) ([]string, error) {
   151  		return []string{}, nil
   152  	})
   153  	s.PatchValue(backups.RunCreate, backups.NewTestCreateFailure("failed!"))
   154  
   155  	s.checkFailure(c, "while creating backup archive: failed!")
   156  }
   157  
   158  func (s *backupsSuite) TestCreateFailToFinishMeta(c *gc.C) {
   159  	s.PatchValue(backups.TestGetFilesToBackUp, func(root string, paths *backups.Paths) ([]string, error) {
   160  		return []string{}, nil
   161  	})
   162  	_, testCreate := backups.NewTestCreate(nil)
   163  	s.PatchValue(backups.RunCreate, testCreate)
   164  	s.PatchValue(backups.FinishMeta, backups.NewTestMetaFinisher("failed!"))
   165  
   166  	s.checkFailure(c, "while updating metadata: failed!")
   167  }
   168  
   169  func (s *backupsSuite) TestNotEnoughDiskSpaceSmallBackup(c *gc.C) {
   170  	s.PatchValue(backups.TestGetFilesToBackUp, func(root string, paths *backups.Paths) ([]string, error) {
   171  		return []string{"file1"}, nil
   172  	})
   173  	s.dbSizeMiB = 6
   174  	s.dirSizeBytes = 3 * humanize.MiByte
   175  	s.availableDiskMiB = 10 * humanize.MiByte
   176  	s.totalDiskMiB = 200 * humanize.GiByte
   177  
   178  	s.checkFailure(c, "not enough free space in .*; want 5129MiB, have 10MiB")
   179  }
   180  
   181  func (s *backupsSuite) TestNotEnoughDiskSpaceLargeBackup(c *gc.C) {
   182  	s.PatchValue(backups.TestGetFilesToBackUp, func(root string, paths *backups.Paths) ([]string, error) {
   183  		return []string{"file1"}, nil
   184  	})
   185  	s.dbSizeMiB = 100
   186  	s.dirSizeBytes = 50 * humanize.GiByte
   187  	s.availableDiskMiB = 10 * humanize.MiByte
   188  	s.totalDiskMiB = 200 * humanize.GiByte
   189  
   190  	s.checkFailure(c, "not enough free space in .*; want 61560MiB, have 10MiB")
   191  }
   192  
   193  func (s *backupsSuite) TestNotEnoughDiskSpaceSmallDisk(c *gc.C) {
   194  	s.PatchValue(backups.TestGetFilesToBackUp, func(root string, paths *backups.Paths) ([]string, error) {
   195  		return []string{"file1"}, nil
   196  	})
   197  	s.dbSizeMiB = 6
   198  	s.dirSizeBytes = 3 * humanize.MiByte
   199  	s.availableDiskMiB = 10 * humanize.MiByte
   200  	s.totalDiskMiB = 20 * humanize.GiByte
   201  
   202  	s.checkFailure(c, "not enough free space in .*; want 2057MiB, have 10MiB")
   203  }
   204  
   205  func (s *backupsSuite) TestGetFileName(c *gc.C) {
   206  	backupSubDir := filepath.Join(s.paths.BackupDir, "a", "b")
   207  	err := os.MkdirAll(backupSubDir, 0755)
   208  	c.Assert(err, jc.ErrorIsNil)
   209  	backupFilename := path.Join(backupSubDir, "juju-backup-123.tar.gz")
   210  	backupFile, err := os.Create(backupFilename)
   211  	c.Assert(err, jc.ErrorIsNil)
   212  	_, err = backupFile.Write([]byte("archive file testing"))
   213  	c.Assert(err, jc.ErrorIsNil)
   214  
   215  	_, _, err = s.api.Get("/etc/hostname")
   216  	c.Assert(err, gc.ErrorMatches, `backup file "/etc/hostname" not valid`)
   217  
   218  	resultMeta, resultArchive, err := s.api.Get(backupFilename)
   219  	c.Assert(err, jc.ErrorIsNil)
   220  	defer resultArchive.Close()
   221  	resultMeta.FileMetadata.Checksum()
   222  
   223  	// Purpose for metadata here is for the checksum to be used by the
   224  	// caller, so check it here.
   225  	c.Assert(resultMeta.FileMetadata.Checksum(), gc.NotNil)
   226  	b, err := io.ReadAll(resultArchive)
   227  	c.Assert(err, jc.ErrorIsNil)
   228  	c.Assert(string(b), gc.Equals, "archive file testing")
   229  
   230  	_, err = os.Stat(backupFilename)
   231  	c.Assert(err, gc.ErrorMatches, fmt.Sprintf("stat %s: no such file or directory", backupFilename))
   232  }