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 }