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