github.com/vmware/govmomi@v0.51.0/simulator/datastore_test.go (about) 1 // © Broadcom. All Rights Reserved. 2 // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. 3 // SPDX-License-Identifier: Apache-2.0 4 5 package simulator 6 7 import ( 8 "context" 9 "net/http" 10 "os" 11 "path" 12 "strings" 13 "testing" 14 15 "github.com/vmware/govmomi" 16 "github.com/vmware/govmomi/find" 17 "github.com/vmware/govmomi/object" 18 "github.com/vmware/govmomi/vim25/methods" 19 "github.com/vmware/govmomi/vim25/soap" 20 "github.com/vmware/govmomi/vim25/types" 21 ) 22 23 func TestParseDatastorePath(t *testing.T) { 24 tests := []struct { 25 dsPath string 26 dsFile string 27 fail bool 28 }{ 29 {"", "", true}, 30 {"x", "", true}, 31 {"[", "", true}, 32 {"[nope", "", true}, 33 {"[test]", "", false}, 34 {"[test] foo", "foo", false}, 35 {"[test] foo/foo.vmx", "foo/foo.vmx", false}, 36 {"[test]foo bar/foo bar.vmx", "foo bar/foo bar.vmx", false}, 37 } 38 39 for _, test := range tests { 40 p, err := parseDatastorePath(test.dsPath) 41 if test.fail { 42 if err == nil { 43 t.Errorf("expected error for: %s", test.dsPath) 44 } 45 } else { 46 if err != nil { 47 t.Errorf("unexpected error '%#v' for: %s", err, test.dsPath) 48 } else { 49 if test.dsFile != p.Path { 50 t.Errorf("dsFile=%s", p.Path) 51 } 52 if p.Datastore != "test" { 53 t.Errorf("ds=%s", p.Datastore) 54 } 55 } 56 } 57 } 58 } 59 60 func TestRefreshDatastore(t *testing.T) { 61 tests := []struct { 62 dir string 63 fail bool 64 }{ 65 {".", false}, 66 {"-", true}, 67 } 68 69 for _, test := range tests { 70 ds := &Datastore{} 71 ds.Info = &types.LocalDatastoreInfo{ 72 DatastoreInfo: types.DatastoreInfo{ 73 Url: test.dir, 74 }, 75 } 76 77 r := ds.RefreshDatastore(NewContext(), nil) 78 res, ok := r.(*methods.RefreshDatastoreBody) 79 if !ok { 80 t.Fatalf("Unexpected response type: %T", r) 81 } 82 83 err := res.Fault() 84 85 if test.fail { 86 if err == nil { 87 t.Error("expected error") 88 } 89 } else { 90 if err != nil { 91 t.Error(err) 92 } 93 if res.Res == nil { 94 t.Errorf("Invalid response: %v", res) 95 } 96 } 97 } 98 } 99 100 func TestDatastoreHTTP(t *testing.T) { 101 ctx := context.Background() 102 src := "datastore_test.go" 103 dst := "tmp.go" 104 105 for i, model := range []*Model{ESX(), VPX()} { 106 defer model.Remove() 107 err := model.Create() 108 if err != nil { 109 t.Fatal(err) 110 } 111 112 s := model.Service.NewServer() 113 defer s.Close() 114 115 if i == 0 { 116 // Enable use of SessionManagerGenericServiceTicket.HostName in govmomi, disabled by default. 117 opts := s.URL.Query() 118 opts.Set("GOVMOMI_USE_SERVICE_TICKET_HOSTNAME", "true") 119 s.URL.RawQuery = opts.Encode() 120 } 121 122 c, err := govmomi.NewClient(ctx, s.URL, true) 123 if err != nil { 124 t.Fatal(err) 125 } 126 127 finder := find.NewFinder(c.Client, false) 128 129 dc, err := finder.DefaultDatacenter(ctx) 130 if err != nil { 131 t.Fatal(err) 132 } 133 134 finder.SetDatacenter(dc) 135 136 ds, err := finder.DefaultDatastore(ctx) 137 if err != nil { 138 t.Fatal(err) 139 } 140 141 //if i == 0 { 142 // // Cover the service ticket path 143 // // TODO: govmomi requires HostSystem.Config.VirtualNicManagerInfo 144 // // ctx = ds.HostContext(ctx, object.NewHostSystem(c.Client, esx.HostSystem.Reference())) 145 //} 146 147 dsPath := ds.Path 148 149 if !c.IsVC() { 150 dc = nil // test using the default 151 } 152 153 fm := object.NewFileManager(c.Client) 154 browser, err := ds.Browser(ctx) 155 if err != nil { 156 t.Fatal(err) 157 } 158 159 download := func(name string, fail bool) { 160 st, serr := ds.Stat(ctx, name) 161 162 _, _, err = ds.Download(ctx, name, nil) 163 if fail { 164 if err == nil { 165 t.Fatal("expected Download error") 166 } 167 if serr == nil { 168 t.Fatal("expected Stat error") 169 } 170 } else { 171 if err != nil { 172 t.Errorf("Download error: %s", err) 173 } 174 if serr != nil { 175 t.Errorf("Stat error: %s", serr) 176 } 177 178 p := st.GetFileInfo().Path 179 if p != name { 180 t.Errorf("path=%s", p) 181 } 182 } 183 } 184 185 upload := func(name string, fail bool, method string) { 186 f, err := os.Open(src) 187 if err != nil { 188 t.Fatal(err) 189 } 190 defer f.Close() 191 192 p := soap.DefaultUpload 193 p.Method = method 194 195 err = ds.Upload(ctx, f, name, &p) 196 if fail { 197 if err == nil { 198 t.Fatalf("%s %s: expected error", method, name) 199 } 200 } else { 201 if err != nil { 202 t.Fatal(err) 203 } 204 } 205 } 206 207 rm := func(name string, fail bool) { 208 task, err := fm.DeleteDatastoreFile(ctx, dsPath(name), dc) 209 if err != nil { 210 t.Fatal(err) 211 } 212 213 err = task.Wait(ctx) 214 if fail { 215 if err == nil { 216 t.Fatalf("rm %s: expected error", name) 217 } 218 } else { 219 if err != nil { 220 t.Fatal(err) 221 } 222 } 223 } 224 225 mv := func(src string, dst string, fail bool, force bool) { 226 task, err := fm.MoveDatastoreFile(ctx, dsPath(src), dc, dsPath(dst), dc, force) 227 if err != nil { 228 t.Fatal(err) 229 } 230 231 err = task.Wait(ctx) 232 if fail { 233 if err == nil { 234 t.Fatalf("mv %s %s: expected error", src, dst) 235 } 236 } else { 237 if err != nil { 238 t.Fatal(err) 239 } 240 } 241 } 242 243 mkdir := func(name string, fail bool, p bool) { 244 err := fm.MakeDirectory(ctx, dsPath(name), dc, p) 245 if fail { 246 if err == nil { 247 t.Fatalf("mkdir %s: expected error", name) 248 } 249 } else { 250 if err != nil { 251 t.Fatal(err) 252 } 253 } 254 } 255 256 stat := func(name string, fail bool) { 257 _, err := ds.Stat(ctx, name) 258 if fail { 259 if err == nil { 260 t.Fatalf("stat %s: expected error", name) 261 } 262 } else { 263 if err != nil { 264 t.Fatal(err) 265 } 266 } 267 } 268 269 ls := func(name string, fail bool) []types.BaseFileInfo { 270 spec := types.HostDatastoreBrowserSearchSpec{ 271 MatchPattern: []string{"*"}, 272 } 273 274 task, err := browser.SearchDatastore(ctx, dsPath(name), &spec) 275 if err != nil { 276 t.Fatal(err) 277 } 278 279 info, err := task.WaitForResult(ctx, nil) 280 if err != nil { 281 if fail { 282 if err == nil { 283 t.Fatalf("ls %s: expected error", name) 284 } 285 } else { 286 if err != nil { 287 t.Fatal(err) 288 } 289 } 290 return nil 291 } 292 if info.Entity.Type != "Datastore" { 293 t.Fatal(info.Entity.Type) 294 } 295 296 return info.Result.(types.HostDatastoreBrowserSearchResults).File 297 } 298 299 lsr := func(name string, fail bool, query ...types.BaseFileQuery) []types.HostDatastoreBrowserSearchResults { 300 spec := types.HostDatastoreBrowserSearchSpec{ 301 MatchPattern: []string{"*"}, 302 Query: query, 303 } 304 305 task, err := browser.SearchDatastoreSubFolders(ctx, dsPath(name), &spec) 306 if err != nil { 307 t.Fatal(err) 308 } 309 310 info, err := task.WaitForResult(ctx, nil) 311 if err != nil { 312 if fail { 313 if err == nil { 314 t.Fatalf("find %s: expected error", name) 315 } 316 } else { 317 if err != nil { 318 t.Fatal(err) 319 } 320 } 321 return nil 322 } 323 if info.Entity.Type != "Datastore" { 324 t.Fatal(info.Entity.Type) 325 } 326 327 return info.Result.(types.ArrayOfHostDatastoreBrowserSearchResults).HostDatastoreBrowserSearchResults 328 } 329 330 // GET file does not exist = fail 331 download(dst, true) 332 stat(dst, true) 333 ls(dst, true) 334 lsr(dst, true) 335 336 // delete file does not exist = fail 337 rm(dst, true) 338 339 // PUT file = ok 340 upload(dst, false, "PUT") 341 stat(dst, false) 342 ls("", false) 343 lsr("", false) 344 345 // GET file exists = ok 346 download(dst, false) 347 348 // POST file exists = fail 349 upload(dst, true, "POST") 350 351 // delete existing file = ok 352 rm(dst, false) 353 stat(dst, true) 354 355 // GET file does not exist = fail 356 download(dst, true) 357 358 // POST file does not exist = ok 359 upload(dst, false, "POST") 360 361 // PATCH method not supported = fail 362 upload(dst+".patch", true, "PATCH") 363 364 // PUT path is directory = fail 365 upload("", true, "PUT") 366 367 // POST parent does not exist = ok 368 upload("foobar/"+dst, false, "POST") 369 370 // PUT parent does not exist = ok 371 upload("barfoo/"+dst, false, "PUT") 372 373 // mkdir parent does not exist = fail 374 mkdir("foo/bar", true, false) 375 376 // mkdir -p parent does not exist = ok 377 mkdir("foo/bar", false, true) 378 379 // mkdir = ok 380 mkdir("foo/bar/baz", false, false) 381 382 target := path.Join("foo", dst) 383 384 // mv dst not exist = ok 385 mv(dst, target, false, false) 386 387 // POST file does not exist = ok 388 upload(dst, false, "POST") 389 390 // mv dst exists = fail 391 mv(dst, target, true, false) 392 393 // mv dst exists, force=true = ok 394 mv(dst, target, false, true) 395 396 // mv src does not exist = fail 397 mv(dst, target, true, true) 398 399 // ls -R = ok 400 res := lsr("foo", false) 401 402 count := func(s string) int { 403 n := 0 404 for _, dir := range res { 405 for _, f := range dir.File { 406 if strings.HasSuffix(f.GetFileInfo().Path, s) { 407 n++ 408 } 409 } 410 } 411 return n 412 } 413 414 n := len(res) + count("") 415 416 if n != 6 { 417 t.Errorf("ls -R foo==%d", n) 418 } 419 420 // test FileQuery 421 res = lsr("", false) 422 all := count(".vmdk") // foo-flat.vmdk + foo.vmdk 423 424 res = lsr("", false, new(types.VmDiskFileQuery)) 425 allq := count(".vmdk") // foo.vmdk only 426 if allq*2 != all { 427 t.Errorf("ls -R *.vmdk: %d vs %d", all, allq) 428 } 429 430 res = lsr("", false, new(types.VmLogFileQuery), new(types.VmConfigFileQuery)) 431 all = count("") 432 if all != model.Count().Machine*2 { 433 t.Errorf("ls -R vmware.log+.vmx: %d", all) 434 } 435 436 invalid := []string{ 437 "", //InvalidDatastorePath 438 "[nope]", // InvalidDatastore 439 } 440 441 // test FileType details 442 mkdir("exts", false, false) 443 stat("exts", false) 444 exts := []string{"img", "iso", "log", "nvram", "vmdk", "vmx"} 445 for _, ext := range exts { 446 name := dst + "." + ext 447 upload(name, false, "POST") 448 stat(name, false) 449 } 450 451 for _, p := range invalid { 452 dsPath = func(name string) string { 453 return p 454 } 455 mv(target, dst, true, false) 456 mkdir("sorry", true, false) 457 rm(target, true) 458 ls(target, true) 459 } 460 461 // cover the dst failure path 462 for _, p := range invalid { 463 dsPath = func(name string) string { 464 if name == dst { 465 return p 466 } 467 return ds.Path(name) 468 } 469 mv(target, dst, true, false) 470 } 471 472 dsPath = func(name string) string { 473 return ds.Path("enoent") 474 } 475 ls(target, true) 476 477 // cover the case where datacenter or datastore lookup fails 478 for _, q := range []string{"dcName=nope", "dsName=nope"} { 479 u := *s.URL 480 u.RawQuery = q 481 u.Path = path.Join(folderPrefix, dst) 482 483 r, err := http.Get(u.String()) 484 if err != nil { 485 t.Fatal(err) 486 } 487 488 if r.StatusCode == http.StatusOK { 489 t.Error("expected failure") 490 } 491 } 492 } 493 }