github.com/vmware/govmomi@v0.37.2/task/wait.go (about)

     1  /*
     2  Copyright (c) 2015-2024 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 task
    18  
    19  import (
    20  	"context"
    21  
    22  	"github.com/vmware/govmomi/property"
    23  	"github.com/vmware/govmomi/vim25/progress"
    24  	"github.com/vmware/govmomi/vim25/types"
    25  )
    26  
    27  type taskProgress struct {
    28  	info *types.TaskInfo
    29  }
    30  
    31  func (t taskProgress) Percentage() float32 {
    32  	return float32(t.info.Progress)
    33  }
    34  
    35  func (t taskProgress) Detail() string {
    36  	return ""
    37  }
    38  
    39  func (t taskProgress) Error() error {
    40  	if t.info.Error != nil {
    41  		return Error{t.info.Error, t.info.Description}
    42  	}
    43  
    44  	return nil
    45  }
    46  
    47  type taskCallback struct {
    48  	ch   chan<- progress.Report
    49  	info *types.TaskInfo
    50  	err  error
    51  }
    52  
    53  func (t *taskCallback) fn(pc []types.PropertyChange) bool {
    54  	for _, c := range pc {
    55  		if c.Name != "info" {
    56  			continue
    57  		}
    58  
    59  		if c.Op != types.PropertyChangeOpAssign {
    60  			continue
    61  		}
    62  
    63  		if c.Val == nil {
    64  			continue
    65  		}
    66  
    67  		ti := c.Val.(types.TaskInfo)
    68  		t.info = &ti
    69  	}
    70  
    71  	// t.info could be nil if pc can't satisfy the rules above
    72  	if t.info == nil {
    73  		return false
    74  	}
    75  
    76  	pr := taskProgress{t.info}
    77  
    78  	// Store copy of error, so Wait() can return it as well.
    79  	t.err = pr.Error()
    80  
    81  	switch t.info.State {
    82  	case types.TaskInfoStateQueued, types.TaskInfoStateRunning:
    83  		if t.ch != nil {
    84  			// Don't care if this is dropped
    85  			select {
    86  			case t.ch <- pr:
    87  			default:
    88  			}
    89  		}
    90  		return false
    91  	case types.TaskInfoStateSuccess, types.TaskInfoStateError:
    92  		if t.ch != nil {
    93  			// Last one must always be delivered
    94  			t.ch <- pr
    95  		}
    96  		return true
    97  	default:
    98  		panic("unknown state: " + t.info.State)
    99  	}
   100  }
   101  
   102  // WaitEx waits for a task to finish with either success or failure. It does so
   103  // by waiting for the "info" property of task managed object to change. The
   104  // function returns when it finds the task in the "success" or "error" state.
   105  // In the former case, the return value is nil. In the latter case the return
   106  // value is an instance of this package's Error struct.
   107  //
   108  // Any error returned while waiting for property changes causes the function to
   109  // return immediately and propagate the error.
   110  //
   111  // If the progress.Sinker argument is specified, any progress updates for the
   112  // task are sent here. The completion percentage is passed through directly.
   113  // The detail for the progress update is set to an empty string. If the task
   114  // finishes in the error state, the error instance is passed through as well.
   115  // Note that this error is the same error that is returned by this function.
   116  func WaitEx(
   117  	ctx context.Context,
   118  	ref types.ManagedObjectReference,
   119  	pc *property.Collector,
   120  	s progress.Sinker) (*types.TaskInfo, error) {
   121  
   122  	cb := &taskCallback{}
   123  
   124  	// Include progress sink if specified
   125  	if s != nil {
   126  		cb.ch = s.Sink()
   127  		defer close(cb.ch)
   128  	}
   129  
   130  	filter := &property.WaitFilter{
   131  		WaitOptions: property.WaitOptions{
   132  			PropagateMissing: true,
   133  		},
   134  	}
   135  	filter.Add(ref, ref.Type, []string{"info"})
   136  
   137  	if err := property.WaitForUpdatesEx(
   138  		ctx,
   139  		pc,
   140  		filter,
   141  		func(updates []types.ObjectUpdate) bool {
   142  			for _, update := range updates {
   143  				// Only look at updates for the expected task object.
   144  				if update.Obj.Value == ref.Value && update.Obj.Type == ref.Type {
   145  					if cb.fn(update.ChangeSet) {
   146  						return true
   147  					}
   148  				}
   149  			}
   150  			return false
   151  		}); err != nil {
   152  
   153  		return nil, err
   154  	}
   155  
   156  	return cb.info, cb.err
   157  }