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