github.com/vmware/govmomi@v0.37.2/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  }