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