github.com/vmware/govmomi@v0.43.0/simulator/race_test.go (about)

     1  /*
     2  Copyright (c) 2018 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  	"fmt"
    22  	"sync"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/vmware/govmomi"
    27  	"github.com/vmware/govmomi/event"
    28  	"github.com/vmware/govmomi/find"
    29  	"github.com/vmware/govmomi/property"
    30  	"github.com/vmware/govmomi/task"
    31  	"github.com/vmware/govmomi/view"
    32  	"github.com/vmware/govmomi/vim25/types"
    33  )
    34  
    35  func TestRace(t *testing.T) {
    36  	ctx := context.Background()
    37  
    38  	m := VPX()
    39  
    40  	defer m.Remove()
    41  
    42  	err := m.Create()
    43  	if err != nil {
    44  		t.Fatal(err)
    45  	}
    46  
    47  	s := m.Service.NewServer()
    48  	defer s.Close()
    49  
    50  	c, err := govmomi.NewClient(ctx, s.URL, true)
    51  	if err != nil {
    52  		t.Fatal(err)
    53  	}
    54  
    55  	content := c.Client.ServiceContent
    56  
    57  	wctx, cancel := context.WithCancel(ctx)
    58  	var wg, collectors sync.WaitGroup
    59  
    60  	nevents := -1
    61  	em := event.NewManager(c.Client)
    62  
    63  	wg.Add(1)
    64  	collectors.Add(1)
    65  	go func() {
    66  		defer collectors.Done()
    67  
    68  		werr := em.Events(wctx, []types.ManagedObjectReference{content.RootFolder}, 50, true, false,
    69  			func(_ types.ManagedObjectReference, e []types.BaseEvent) error {
    70  				if nevents == -1 {
    71  					wg.Done() // make sure we are called at least once before cancel() below
    72  					nevents = 0
    73  				}
    74  
    75  				nevents += len(e)
    76  				return nil
    77  			})
    78  		if werr != nil {
    79  			t.Error(werr)
    80  		}
    81  	}()
    82  
    83  	collectors.Add(1)
    84  	go func() {
    85  		defer collectors.Done()
    86  
    87  		ec, werr := em.CreateCollectorForEvents(ctx, types.EventFilterSpec{})
    88  		if werr != nil {
    89  			t.Error(werr)
    90  		}
    91  
    92  		n := 0
    93  		for {
    94  			events, werr := ec.ReadNextEvents(ctx, 10)
    95  			if werr != nil {
    96  				t.Error(werr)
    97  			}
    98  
    99  			n += len(events)
   100  			if len(events) != 0 {
   101  				continue
   102  			}
   103  
   104  			select {
   105  			case <-wctx.Done():
   106  				logf := t.Logf
   107  				if n == 0 {
   108  					logf = t.Errorf
   109  				}
   110  				logf("ReadNextEvents=%d", n)
   111  				return
   112  			case <-time.After(time.Millisecond * 100):
   113  			}
   114  		}
   115  	}()
   116  
   117  	ntasks := -1
   118  	tv, err := view.NewManager(c.Client).CreateTaskView(ctx, content.TaskManager)
   119  	if err != nil {
   120  		t.Fatal(err)
   121  	}
   122  
   123  	lv, err := view.NewManager(c.Client).CreateListView(ctx, nil)
   124  	if err != nil {
   125  		t.Fatal(err)
   126  	}
   127  
   128  	wg.Add(1)
   129  	collectors.Add(1)
   130  	go func() {
   131  		defer collectors.Done()
   132  
   133  		werr := tv.Collect(ctx, func(tasks []types.TaskInfo) {
   134  			if ntasks == -1 {
   135  				wg.Done() // make sure we are called at least once before cancel() below
   136  				ntasks = 0
   137  			}
   138  			ntasks += len(tasks)
   139  		})
   140  		if werr != nil {
   141  			t.Error(werr)
   142  		}
   143  	}()
   144  
   145  	for i := 0; i < 2; i++ {
   146  		spec := types.VirtualMachineConfigSpec{
   147  			Name:    fmt.Sprintf("race-test-%d", i),
   148  			GuestId: string(types.VirtualMachineGuestOsIdentifierOtherGuest),
   149  			Files: &types.VirtualMachineFileInfo{
   150  				VmPathName: "[LocalDS_0]",
   151  			},
   152  		}
   153  
   154  		wg.Add(1)
   155  		go func() {
   156  			defer wg.Done()
   157  
   158  			finder := find.NewFinder(c.Client, false)
   159  			pc := property.DefaultCollector(c.Client)
   160  			dc, err := finder.DefaultDatacenter(ctx)
   161  			if err != nil {
   162  				t.Error(err)
   163  			}
   164  
   165  			finder.SetDatacenter(dc)
   166  
   167  			f, err := dc.Folders(ctx)
   168  			if err != nil {
   169  				t.Error(err)
   170  			}
   171  
   172  			pool, err := finder.ResourcePool(ctx, "DC0_C0/Resources")
   173  			if err != nil {
   174  				t.Error(err)
   175  			}
   176  
   177  			for j := 0; j < 2; j++ {
   178  				cspec := spec // copy spec and give it a unique name
   179  				cspec.Name += fmt.Sprintf("-%d", j)
   180  
   181  				wg.Add(1)
   182  				go func() {
   183  					defer wg.Done()
   184  
   185  					task, _ := f.VmFolder.CreateVM(ctx, cspec, pool, nil)
   186  					r, terr := task.WaitForResult(ctx, nil)
   187  					if terr != nil {
   188  						t.Error(terr)
   189  					}
   190  					_, terr = lv.Add(ctx, []types.ManagedObjectReference{r.Result.(types.ManagedObjectReference)})
   191  					if terr != nil {
   192  						t.Error(terr)
   193  					}
   194  				}()
   195  			}
   196  
   197  			vms, err := finder.VirtualMachineList(ctx, "*")
   198  			if err != nil {
   199  				t.Error(err)
   200  			}
   201  
   202  			for i := range vms {
   203  				props := []string{"runtime.powerState"}
   204  				vm := vms[i]
   205  
   206  				wg.Add(1)
   207  				go func() {
   208  					defer wg.Done()
   209  
   210  					werr := property.Wait(ctx, pc, vm.Reference(), props, func(changes []types.PropertyChange) bool {
   211  						for _, change := range changes {
   212  							if change.Name != props[0] {
   213  								t.Errorf("unexpected property: %s", change.Name)
   214  							}
   215  							if change.Val == types.VirtualMachinePowerStatePoweredOff {
   216  								return true
   217  							}
   218  						}
   219  
   220  						wg.Add(1)
   221  						time.AfterFunc(100*time.Millisecond, func() {
   222  							defer wg.Done()
   223  
   224  							_, _ = lv.Remove(ctx, []types.ManagedObjectReference{vm.Reference()})
   225  							task, _ := vm.PowerOff(ctx)
   226  							_ = task.Wait(ctx)
   227  						})
   228  
   229  						return false
   230  
   231  					})
   232  					if werr != nil {
   233  						if werr != context.Canceled {
   234  							t.Error(werr)
   235  						}
   236  					}
   237  				}()
   238  			}
   239  		}()
   240  	}
   241  
   242  	wg.Wait()
   243  
   244  	// cancel event and tasks collectors, waiting for them to complete
   245  	cancel()
   246  	collectors.Wait()
   247  
   248  	t.Logf("collected %d events, %d tasks", nevents, ntasks)
   249  	if nevents == 0 {
   250  		t.Error("no events collected")
   251  	}
   252  	if ntasks == 0 {
   253  		t.Error("no tasks collected")
   254  	}
   255  }
   256  
   257  func isManagedObjectNotFound(err error) bool {
   258  	_, ok := err.(task.Error).Fault().(*types.ManagedObjectNotFound)
   259  	return ok
   260  }
   261  
   262  // Race VirtualMachine.Destroy vs Folder.Destroy; the latter removes the VM parent and the former should not panic in that case
   263  func TestRaceDestroy(t *testing.T) {
   264  	ctx := context.Background()
   265  
   266  	m := VPX()
   267  	m.Folder = 1
   268  	m.Autostart = false
   269  	defer m.Remove()
   270  
   271  	err := m.Create()
   272  	if err != nil {
   273  		t.Fatal(err)
   274  	}
   275  
   276  	s := m.Service.NewServer()
   277  	defer s.Close()
   278  
   279  	c, err := govmomi.NewClient(ctx, s.URL, true)
   280  	if err != nil {
   281  		t.Fatal(err)
   282  	}
   283  
   284  	finder := find.NewFinder(c.Client)
   285  	vms, err := finder.VirtualMachineList(ctx, "*")
   286  	if err != nil {
   287  		t.Fatal(err)
   288  	}
   289  
   290  	folder, err := finder.Folder(ctx, "vm/F0")
   291  	if err != nil {
   292  		t.Fatal(err)
   293  	}
   294  
   295  	notFound := false
   296  	var wg sync.WaitGroup
   297  
   298  	wg.Add(1)
   299  	go func() {
   300  		defer wg.Done()
   301  		for _, vm := range vms {
   302  			task, err := vm.Destroy(ctx)
   303  			if err != nil {
   304  				t.Error(err)
   305  			}
   306  			err = task.Wait(ctx)
   307  			if err != nil {
   308  				if isManagedObjectNotFound(err) {
   309  					notFound = true
   310  				} else {
   311  					t.Error(err)
   312  				}
   313  			}
   314  		}
   315  	}()
   316  
   317  	wg.Add(1)
   318  	go func() {
   319  		defer wg.Done()
   320  		task, err := folder.Destroy(ctx)
   321  		if err != nil {
   322  			t.Error(err)
   323  		}
   324  		err = task.Wait(ctx)
   325  		if err != nil {
   326  			t.Error(err)
   327  		}
   328  	}()
   329  
   330  	wg.Wait()
   331  
   332  	if !notFound {
   333  		t.Error("expected ManagedObjectNotFound")
   334  	}
   335  }