github.com/kubevela/workflow@v0.6.0/pkg/monitor/watcher/workflow.go (about) 1 /* 2 Copyright 2022 The KubeVela Authors. 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 watcher 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "strings" 23 "sync" 24 "time" 25 26 "k8s.io/client-go/tools/cache" 27 "k8s.io/klog/v2" 28 ctrlcache "sigs.k8s.io/controller-runtime/pkg/cache" 29 30 "github.com/kubevela/workflow/api/v1alpha1" 31 "github.com/kubevela/workflow/pkg/monitor/metrics" 32 ) 33 34 type workflowRunMetricsWatcher struct { 35 mu sync.Mutex 36 phaseCounter map[string]int 37 stepPhaseCounter map[string]int 38 phaseDirty map[string]struct{} 39 stepPhaseDirty map[string]struct{} 40 informer ctrlcache.Informer 41 stopCh chan struct{} 42 } 43 44 func (watcher *workflowRunMetricsWatcher) getRun(obj interface{}) *v1alpha1.WorkflowRun { 45 wr := &v1alpha1.WorkflowRun{} 46 bs, _ := json.Marshal(obj) 47 _ = json.Unmarshal(bs, wr) 48 return wr 49 } 50 51 func (watcher *workflowRunMetricsWatcher) getPhase(phase string) string { 52 if phase == "" { 53 return "-" 54 } 55 return phase 56 } 57 58 func (watcher *workflowRunMetricsWatcher) inc(wr *v1alpha1.WorkflowRun, delta int) { 59 watcher.mu.Lock() 60 defer watcher.mu.Unlock() 61 phase := watcher.getPhase(string(wr.Status.Phase)) 62 watcher.phaseCounter[phase] += delta 63 watcher.phaseDirty[phase] = struct{}{} 64 for _, step := range wr.Status.Steps { 65 stepPhase := watcher.getPhase(string(step.Phase)) 66 key := fmt.Sprintf("%s/%s#%s", step.Type, stepPhase, step.Reason) 67 watcher.stepPhaseCounter[key] += delta 68 watcher.stepPhaseDirty[key] = struct{}{} 69 } 70 } 71 72 func (watcher *workflowRunMetricsWatcher) report() { 73 watcher.mu.Lock() 74 defer watcher.mu.Unlock() 75 for phase := range watcher.phaseDirty { 76 metrics.WorkflowRunPhaseCounter.WithLabelValues(phase).Set(float64(watcher.phaseCounter[phase])) 77 } 78 for stepPhase := range watcher.stepPhaseDirty { 79 metrics.WorkflowRunStepPhaseGauge.WithLabelValues(strings.Split(stepPhase, "/")[:2]...).Set(float64(watcher.stepPhaseCounter[stepPhase])) 80 } 81 watcher.phaseDirty = map[string]struct{}{} 82 watcher.stepPhaseDirty = map[string]struct{}{} 83 } 84 85 func (watcher *workflowRunMetricsWatcher) run() { 86 go func() { 87 for { 88 select { 89 case <-watcher.stopCh: 90 return 91 default: 92 time.Sleep(time.Second) 93 watcher.report() 94 } 95 } 96 }() 97 } 98 99 // StartWorkflowRunMetricsWatcher start metrics watcher for reporting workflow run stats 100 func StartWorkflowRunMetricsWatcher(informer ctrlcache.Informer) { 101 watcher := &workflowRunMetricsWatcher{ 102 phaseCounter: map[string]int{}, 103 stepPhaseCounter: map[string]int{}, 104 phaseDirty: map[string]struct{}{}, 105 stepPhaseDirty: map[string]struct{}{}, 106 informer: informer, 107 stopCh: make(chan struct{}, 1), 108 } 109 _, err := watcher.informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ 110 AddFunc: func(obj interface{}) { 111 wr := watcher.getRun(obj) 112 watcher.inc(wr, 1) 113 }, 114 UpdateFunc: func(oldObj, obj interface{}) { 115 oldWr := watcher.getRun(oldObj) 116 wr := watcher.getRun(obj) 117 watcher.inc(oldWr, -1) 118 watcher.inc(wr, 1) 119 }, 120 DeleteFunc: func(obj interface{}) { 121 wr := watcher.getRun(obj) 122 watcher.inc(wr, -1) 123 }, 124 }) 125 if err != nil { 126 klog.ErrorS(err, "failed to add event handler to workflow metrics watcher") 127 } 128 watcher.run() 129 }