github.com/drud/ddev@v1.21.5-alpha1.0.20230226034409-94fcc4b94453/pkg/ddevapp/snapshot_test.go (about) 1 package ddevapp_test 2 3 import ( 4 "fmt" 5 "github.com/drud/ddev/pkg/archive" 6 "github.com/drud/ddev/pkg/ddevapp" 7 "github.com/drud/ddev/pkg/nodeps" 8 "github.com/drud/ddev/pkg/testcommon" 9 "github.com/drud/ddev/pkg/util" 10 assert2 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 "os" 13 "path/filepath" 14 "runtime" 15 "strings" 16 "testing" 17 "time" 18 ) 19 20 // TestDdevSnapshotCleanup tests creating a snapshot and deleting it. 21 func TestDdevSnapshotCleanup(t *testing.T) { 22 assert := assert2.New(t) 23 app := &ddevapp.DdevApp{} 24 site := TestSites[0] 25 switchDir := site.Chdir() 26 defer switchDir() 27 28 runTime := util.TimeTrack(time.Now(), fmt.Sprintf("TestDdevSnapshotCleanup")) 29 30 testcommon.ClearDockerEnv() 31 err := app.Init(site.Dir) 32 assert.NoError(err) 33 34 t.Cleanup(func() { 35 err = app.Stop(true, false) 36 assert.NoError(err) 37 err = os.RemoveAll(app.GetConfigPath("db_snapshots")) 38 assert.NoError(err) 39 }) 40 41 err = app.Start() 42 assert.NoError(err) 43 44 // Make a snapshot of d7 tester test 1 45 snapshotName, err := app.Snapshot(t.Name() + "_1") 46 assert.NoError(err) 47 48 err = app.Init(site.Dir) 49 require.NoError(t, err) 50 51 err = app.Start() 52 require.NoError(t, err) 53 54 err = app.DeleteSnapshot(snapshotName) 55 assert.NoError(err) 56 57 // Snapshot data should be deleted 58 err = app.DeleteSnapshot(snapshotName) 59 assert.Error(err) 60 61 runTime() 62 } 63 64 // TestGetLatestSnapshot tests if the latest snapshot of a project is returned correctly. 65 func TestGetLatestSnapshot(t *testing.T) { 66 assert := assert2.New(t) 67 app := &ddevapp.DdevApp{} 68 site := TestSites[0] 69 origDir, _ := os.Getwd() 70 err := os.Chdir(site.Dir) 71 assert.NoError(err) 72 73 runTime := util.TimeTrack(time.Now(), t.Name()) 74 75 testcommon.ClearDockerEnv() 76 err = app.Init(site.Dir) 77 assert.NoError(err) 78 79 t.Cleanup(func() { 80 err = app.Stop(true, false) 81 assert.NoError(err) 82 err = os.RemoveAll(app.GetConfigPath("db_snapshots")) 83 assert.NoError(err) 84 err = os.Chdir(origDir) 85 assert.NoError(err) 86 }) 87 88 err = app.Start() 89 assert.NoError(err) 90 91 snapshots := []string{t.Name() + "_1", t.Name() + "_2", t.Name() + "_3"} 92 // Make three snapshots and compare the last 93 s1Name, err := app.Snapshot(snapshots[0]) 94 assert.NoError(err) 95 s2Name, err := app.Snapshot(snapshots[1]) 96 assert.NoError(err) 97 s3Name, err := app.Snapshot(snapshots[2]) // last = latest 98 assert.NoError(err) 99 100 latestSnapshot, err := app.GetLatestSnapshot() 101 assert.NoError(err) 102 assert.Equal(s3Name, latestSnapshot) 103 104 // delete snapshot 3 105 err = app.DeleteSnapshot(s3Name) 106 assert.NoError(err) 107 latestSnapshot, err = app.GetLatestSnapshot() 108 assert.NoError(err) 109 assert.Equal(s2Name, latestSnapshot, "%s should be latest snapshot", snapshots[1]) 110 111 // delete snapshot 2 112 err = app.DeleteSnapshot(s2Name) 113 assert.NoError(err) 114 latestSnapshot, err = app.GetLatestSnapshot() 115 assert.NoError(err) 116 assert.Equal(s1Name, latestSnapshot, "%s should now be latest snapshot", s1Name) 117 118 // delete snapshot 1 (should be last) 119 err = app.DeleteSnapshot(s1Name) 120 assert.NoError(err) 121 latestSnapshot, _ = app.GetLatestSnapshot() 122 assert.Equal("", latestSnapshot) 123 124 runTime() 125 } 126 127 // TestDdevRestoreSnapshot tests creating a snapshot and reverting to it. 128 func TestDdevRestoreSnapshot(t *testing.T) { 129 assert := assert2.New(t) 130 131 runTime := util.TimeTrack(time.Now(), t.Name()) 132 origDir, _ := os.Getwd() 133 site := TestSites[0] 134 135 d7testerTest1Dump, err := filepath.Abs(filepath.Join("testdata", t.Name(), "restore_snapshot", "d7tester_test_1.sql.gz")) 136 assert.NoError(err) 137 d7testerTest2Dump, err := filepath.Abs(filepath.Join("testdata", t.Name(), "restore_snapshot", "d7tester_test_2.sql.gz")) 138 assert.NoError(err) 139 140 testcommon.ClearDockerEnv() 141 142 app, err := ddevapp.NewApp(site.Dir, false) 143 require.NoError(t, err) 144 145 t.Cleanup(func() { 146 err = app.Stop(true, false) 147 assert.NoError(err) 148 149 app.Hooks = nil 150 app.Database.Type = nodeps.MariaDB 151 app.Database.Version = nodeps.MariaDBDefaultVersion 152 err = app.WriteConfig() 153 assert.NoError(err) 154 err = os.Chdir(origDir) 155 assert.NoError(err) 156 err = os.RemoveAll(app.GetConfigPath("db_snapshots")) 157 assert.NoError(err) 158 testcommon.ClearDockerEnv() 159 }) 160 161 err = os.Chdir(app.AppRoot) 162 require.NoError(t, err) 163 app.Hooks = map[string][]ddevapp.YAMLTask{"post-snapshot": {{"exec-host": "touch hello-post-snapshot-" + app.Name}}, "pre-snapshot": {{"exec-host": "touch hello-pre-snapshot-" + app.Name}}} 164 165 err = app.Stop(true, false) 166 assert.NoError(err) 167 err = app.Start() 168 require.NoError(t, err) 169 170 err = app.ImportDB(d7testerTest1Dump, "", false, false, "db") 171 require.NoError(t, err, "Failed to app.ImportDB path: %s err: %v", d7testerTest1Dump, err) 172 173 stdout, _, err := app.Exec(&ddevapp.ExecOpts{ 174 Service: "db", 175 Cmd: `echo "SELECT title FROM node WHERE nid=1;" | mysql -N`, 176 }) 177 assert.NoError(err) 178 assert.Contains(stdout, "d7 tester test 1 has 1 node") 179 180 // Make a snapshot of d7 tester test 1 181 tester1Snapshot, err := app.Snapshot("d7testerTest1") 182 assert.NoError(err) 183 184 assert.Contains(tester1Snapshot, "d7testerTest1") 185 latest, err := app.GetLatestSnapshot() 186 assert.NoError(err) 187 assert.Equal(tester1Snapshot, latest) 188 189 assert.FileExists("hello-pre-snapshot-" + app.Name) 190 assert.FileExists("hello-post-snapshot-" + app.Name) 191 err = os.Remove("hello-pre-snapshot-" + app.Name) 192 assert.NoError(err) 193 err = os.Remove("hello-post-snapshot-" + app.Name) 194 assert.NoError(err) 195 196 // Make sure duplicate snapshot name gives an error 197 _, err = app.Snapshot("d7testerTest1") 198 assert.Error(err) 199 200 err = app.ImportDB(d7testerTest2Dump, "", false, false, "db") 201 assert.NoError(err, "Failed to app.ImportDB path: %s err: %v", d7testerTest2Dump, err) 202 203 stdout, _, err = app.Exec(&ddevapp.ExecOpts{ 204 Service: "db", 205 Cmd: `echo "SELECT title FROM node WHERE nid=1;" | mysql -N`, 206 }) 207 assert.NoError(err) 208 assert.Contains(stdout, "d7 tester test 2 has 2 nodes") 209 210 tester2Snapshot, err := app.Snapshot("d7testerTest2") 211 assert.NoError(err) 212 assert.Contains(tester2Snapshot, "d7testerTest2") 213 latest, err = app.GetLatestSnapshot() 214 assert.Equal(tester2Snapshot, latest) 215 216 app.Hooks = map[string][]ddevapp.YAMLTask{"post-restore-snapshot": {{"exec-host": "touch hello-post-restore-snapshot-" + app.Name}}, "pre-restore-snapshot": {{"exec-host": "touch hello-pre-restore-snapshot-" + app.Name}}} 217 218 err = app.MutagenSyncFlush() 219 require.NoError(t, err) 220 // Sleep to let sync happen if needed (M1 failure) 221 time.Sleep(2 * time.Second) 222 223 err = app.RestoreSnapshot(tester1Snapshot) 224 assert.NoError(err) 225 226 assert.FileExists("hello-pre-restore-snapshot-" + app.Name) 227 assert.FileExists("hello-post-restore-snapshot-" + app.Name) 228 err = os.Remove("hello-pre-restore-snapshot-" + app.Name) 229 assert.NoError(err) 230 err = os.Remove("hello-post-restore-snapshot-" + app.Name) 231 assert.NoError(err) 232 233 stdout, _, err = app.Exec(&ddevapp.ExecOpts{ 234 Service: "db", 235 Cmd: `echo "SELECT title FROM node WHERE nid=1;" | mysql -N`, 236 }) 237 assert.NoError(err) 238 assert.Contains(stdout, "d7 tester test 1 has 1 node") 239 240 err = app.RestoreSnapshot(tester2Snapshot) 241 assert.NoError(err) 242 243 stdout, _, err = app.Exec(&ddevapp.ExecOpts{ 244 Service: "db", 245 Cmd: `echo "SELECT title FROM node WHERE nid=1;" | mysql -N`, 246 }) 247 assert.NoError(err) 248 assert.Contains(stdout, "d7 tester test 2 has 2 nodes") 249 250 // Attempt a restore with a pre-mariadb_10.2 snapshot. It should fail and give a link. 251 oldSnapshotTarball, err := filepath.Abs(filepath.Join(origDir, "testdata", t.Name(), "restore_snapshot", "d7tester_test_1.snapshot_mariadb_10_1.tgz")) 252 assert.NoError(err) 253 254 err = archive.Untar(oldSnapshotTarball, app.GetConfigPath("db_snapshots"), "") 255 assert.NoError(err) 256 257 err = app.RestoreSnapshot("d7tester_test_1.snapshot_mariadb_10.1") 258 assert.Error(err) 259 assert.Contains(err.Error(), "is not compatible") 260 261 // Make sure that we can use old-style directory-based snapshots 262 dirSnapshots := map[string]string{ 263 "mariadb:10.3": "mariadb10.3-users", 264 "mysql:5.7": "mysql5.7-users", 265 } 266 267 // Despite much effort, and successful manual restore of the mysql5.7-users.tgz to another project, 268 // I can't get it to restore in this test. The logs show 269 // " [ERROR] InnoDB: Log block 24712 at lsn 12652032 has valid header, but checksum field contains 1825156513, should be 1116246688" 270 // Since this works everywhere else and this is legacy snapshot support, I'm going to punt 271 // and skip the mysql snapshot. rfay 2022-02-25 272 if runtime.GOOS == "windows" { 273 delete(dirSnapshots, "mysql:5.7") 274 } 275 276 for dbDesc, dirSnapshot := range dirSnapshots { 277 oldSnapshotTarball, err = filepath.Abs(filepath.Join(origDir, "testdata", t.Name(), dirSnapshot+".tgz")) 278 assert.NoError(err) 279 fullsnapshotDir := filepath.Join(app.GetConfigPath("db_snapshots"), dirSnapshot) 280 err = os.MkdirAll(fullsnapshotDir, 0755) 281 require.NoError(t, err) 282 err = archive.Untar(oldSnapshotTarball, fullsnapshotDir, "") 283 assert.NoError(err) 284 285 err = app.Stop(true, false) 286 assert.NoError(err) 287 288 parts := strings.Split(dbDesc, ":") 289 require.Equal(t, 2, len(parts)) 290 dbType := parts[0] 291 dbVersion := parts[1] 292 app.Database.Type = dbType 293 app.Database.Version = dbVersion 294 err = app.WriteConfig() 295 assert.NoError(err) 296 297 err = app.Start() 298 assert.NoError(err) 299 300 _, _, err = app.Exec(&ddevapp.ExecOpts{ 301 Service: "db", 302 Cmd: `echo "DROP TABLE IF EXISTS users;" | mysql`, 303 }) 304 assert.NoError(err) 305 306 err = app.RestoreSnapshot(dirSnapshot) 307 if err != nil { 308 assert.NoError(err, "Failed to restore dirSnapshot '%s': %v, continuing", dirSnapshot, err) 309 continue 310 } 311 312 stdout, _, err = app.Exec(&ddevapp.ExecOpts{ 313 Service: "db", 314 Cmd: `echo "SELECT COUNT(*) FROM users;" | mysql -N`, 315 }) 316 assert.NoError(err) 317 assert.Equal(stdout, "2\n") 318 } 319 runTime() 320 }