vsphere/event_helper.go
func (w *virtualMachineCustomizationWaiter) wait(client *govmomi.Client, vm *object.VirtualMachine, timeout int) error {
	// A timeout of less than 1 minute (zero or negative value) skips the waiter,
	// so we return immediately.
	if timeout < 1 {
		return nil
	}

	// Our listener loop callback.
	cbErr := make(chan error, 1)
	cb := func(obj types.ManagedObjectReference, page []types.BaseEvent) error {
		for _, be := range page {
			switch e := be.(type) {
			case types.BaseCustomizationFailed:
				cbErr <- errors.New(e.GetCustomizationFailed().GetEvent().FullFormattedMessage)
			case *types.CustomizationSucceeded:
				close(cbErr)
			}
		}
		return nil
	}

	mgr := event.NewManager(client.Client)
	mgrErr := make(chan error, 1)
	// Make a proper background context so that we can gracefully cancel the
	// subscriber when we are done with it. This eventually gets passed down to
	// the property collector SOAP calls.
	pctx, pcancel := context.WithCancel(context.Background())
	defer pcancel()
	go func() {
		mgrErr <- mgr.Events(pctx, []types.ManagedObjectReference{vm.Reference()}, 10, true, false, cb)
	}()

	// Wait for any error condition (including nil from the closure of the
	// callback error channel on success). We also use a different context so
	// that we can give a better error message on timeout without interfering
	// with the subscriber's context.
	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Minute)
	defer cancel()
	var err error
	select {
	case <-ctx.Done():
		if ctx.Err() == context.DeadlineExceeded {
			err = fmt.Errorf("timeout waiting for customization to complete")
		}
	case err = <-mgrErr:
	case err = <-cbErr:
	}
	return err
}
func (w *virtualMachineCustomizationWaiter) wait(client *govmomi.Client, vm *object.VirtualMachine, timeout int) error {
	// A timeout of less than 1 minute (zero or negative value) skips the waiter,
	// so we return immediately.
	if timeout < 1 {
		return nil
	}

	// Our listener loop callback.
	cbErr := make(chan error, 1)
	cb := func(obj types.ManagedObjectReference, page []types.BaseEvent) error {
		for _, be := range page {
			switch e := be.(type) {
			case types.BaseCustomizationFailed:
				cbErr <- errors.New(e.GetCustomizationFailed().GetEvent().FullFormattedMessage)
			case *types.CustomizationSucceeded:
				close(cbErr)
			}
		}
		return nil
	}

	mgr := event.NewManager(client.Client)
	mgrErr := make(chan error, 1)
	// Make a proper background context so that we can gracefully cancel the
	// subscriber when we are done with it. This eventually gets passed down to
	// the property collector SOAP calls.
	pctx, pcancel := context.WithCancel(context.Background())
	defer pcancel()
	go func() {
		mgrErr <- mgr.Events(pctx, []types.ManagedObjectReference{vm.Reference()}, 10, true, false, cb)
	}()

	// Wait for any error condition (including nil from the closure of the
	// callback error channel on success). We also use a different context so
	// that we can give a better error message on timeout without interfering
	// with the subscriber's context.
	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Minute)
	defer cancel()
	var err error
	select {
	case <-ctx.Done():
		if ctx.Err() == context.DeadlineExceeded {
			err = fmt.Errorf("timeout waiting for customization to complete")
		}
	case err = <-mgrErr:
	case err = <-cbErr:
	}
	return err
}
// wait waits for the customization of a supplied VirtualMachine to complete,
// either due to success or error. It does this by watching specifically for
// CustomizationSucceeded and CustomizationFailed events. If the customization
// failed due to some sort of error, the full formatted message is returned as
// an error.