istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/status/manager.go (about) 1 /* 2 Copyright Istio 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 status 18 19 import ( 20 "istio.io/api/meta/v1alpha1" 21 "istio.io/istio/pilot/pkg/features" 22 "istio.io/istio/pilot/pkg/model" 23 "istio.io/istio/pkg/config" 24 "istio.io/istio/pkg/config/schema/gvk" 25 ) 26 27 // Manager allows multiple controllers to provide input into configuration 28 // status without needlessly doubling the number of writes, or overwriting 29 // one another. Each status controller calls newController, passing in 30 // an arbitrary status modification function, and then calls EnqueueStatusUpdate 31 // when an individual resource is ready to be updated with the relevant data. 32 type Manager struct { 33 // TODO: is Resource the right abstraction? 34 store model.ConfigStore 35 workers WorkerQueue 36 } 37 38 func NewManager(store model.ConfigStore) *Manager { 39 writeFunc := func(m *config.Config, istatus any) { 40 scope.Debugf("writing status for resource %s/%s", m.Namespace, m.Name) 41 status := istatus.(GenerationProvider) 42 m.Status = status.Unwrap() 43 _, err := store.UpdateStatus(*m) 44 if err != nil { 45 // TODO: need better error handling 46 scope.Errorf("Encountered unexpected error updating status for %v, will try again later: %s", m, err) 47 return 48 } 49 } 50 retrieveFunc := func(resource Resource) *config.Config { 51 scope.Debugf("retrieving config for status update: %s/%s", resource.Namespace, resource.Name) 52 k, ok := gvk.FromGVR(resource.GroupVersionResource) 53 if !ok { 54 scope.Warnf("GVR %v could not be identified", resource.GroupVersionResource) 55 return nil 56 } 57 58 current := store.Get(k, resource.Name, resource.Namespace) 59 return current 60 } 61 return &Manager{ 62 store: store, 63 workers: NewWorkerPool(writeFunc, retrieveFunc, uint(features.StatusMaxWorkers)), 64 } 65 } 66 67 func (m *Manager) Start(stop <-chan struct{}) { 68 scope.Info("Starting status manager") 69 70 ctx := NewIstioContext(stop) 71 m.workers.Run(ctx) 72 } 73 74 // CreateGenericController provides an interface for a status update function to be 75 // called in series with other controllers, minimizing the number of actual 76 // api server writes sent from various status controllers. The UpdateFunc 77 // must take the target resource status and arbitrary context information as 78 // parameters, and return the updated status value. Multiple controllers 79 // will be called in series, so the input status may not have been written 80 // to the API server yet, and the output status may be modified by other 81 // controllers before it is written to the server. 82 func (m *Manager) CreateGenericController(fn UpdateFunc) *Controller { 83 result := &Controller{ 84 fn: fn, 85 workers: m.workers, 86 } 87 return result 88 } 89 90 func (m *Manager) CreateIstioStatusController(fn func(status *v1alpha1.IstioStatus, context any) *v1alpha1.IstioStatus) *Controller { 91 wrapper := func(status any, context any) GenerationProvider { 92 var input *v1alpha1.IstioStatus 93 if status != nil { 94 converted := status.(*IstioGenerationProvider) 95 input = converted.IstioStatus 96 } 97 result := fn(input, context) 98 return &IstioGenerationProvider{result} 99 } 100 result := &Controller{ 101 fn: wrapper, 102 workers: m.workers, 103 } 104 return result 105 } 106 107 type UpdateFunc func(status any, context any) GenerationProvider 108 109 type Controller struct { 110 fn UpdateFunc 111 workers WorkerQueue 112 } 113 114 // EnqueueStatusUpdateResource informs the manager that this controller would like to 115 // update the status of target, using the information in context. Once the status 116 // workers are ready to perform this update, the controller's UpdateFunc 117 // will be called with target and context as input. 118 func (c *Controller) EnqueueStatusUpdateResource(context any, target Resource) { 119 // TODO: buffer this with channel 120 c.workers.Push(target, c, context) 121 } 122 123 func (c *Controller) Delete(r Resource) { 124 c.workers.Delete(r) 125 }