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  }