vitess.io/vitess@v0.16.2/go/vt/topo/k8stopo/watch.go (about) 1 /* 2 Copyright 2020 The Vitess 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 agreedto 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 k8stopo 18 19 import ( 20 "context" 21 22 "k8s.io/apimachinery/pkg/fields" 23 "k8s.io/client-go/tools/cache" 24 25 "vitess.io/vitess/go/vt/log" 26 "vitess.io/vitess/go/vt/topo" 27 vtv1beta1 "vitess.io/vitess/go/vt/topo/k8stopo/apis/topo/v1beta1" 28 ) 29 30 // Watch is part of the topo.Conn interface. 31 func (s *Server) Watch(ctx context.Context, filePath string) (*topo.WatchData, <-chan *topo.WatchData, error) { 32 log.Info("Starting Kubernetes topo Watch on ", filePath) 33 34 current := &topo.WatchData{} 35 36 // get current 37 initialCtx, initialCancel := context.WithTimeout(ctx, topo.RemoteOperationTimeout) 38 defer initialCancel() 39 40 contents, ver, err := s.Get(initialCtx, filePath) 41 if err != nil { 42 return nil, nil, err 43 } 44 current.Contents = contents 45 current.Version = ver 46 47 // Create the changes channel 48 changes := make(chan *topo.WatchData, 10) 49 50 // Create a signal channel for non-interrupt shutdowns 51 gracefulShutdown := make(chan struct{}) 52 53 resource, err := s.buildFileResource(filePath, []byte{}) 54 if err != nil { 55 return nil, nil, err 56 } 57 58 // Create the informer / indexer to watch the single resource 59 restClient := s.vtKubeClient.TopoV1beta1().RESTClient() 60 listwatch := cache.NewListWatchFromClient(restClient, "vitesstoponodes", s.namespace, fields.OneTermEqualSelector("metadata.name", resource.Name)) 61 62 // set up index funcs 63 indexers := cache.Indexers{} 64 indexers["by_parent"] = indexByParent 65 66 _, memberInformer := cache.NewIndexerInformer(listwatch, &vtv1beta1.VitessTopoNode{}, 0, 67 cache.ResourceEventHandlerFuncs{ 68 AddFunc: func(obj any) { 69 vtn := obj.(*vtv1beta1.VitessTopoNode) 70 out, err := unpackValue([]byte(vtn.Data.Value)) 71 if err != nil { 72 changes <- &topo.WatchData{Err: err} 73 close(gracefulShutdown) 74 } else { 75 changes <- &topo.WatchData{ 76 Contents: out, 77 Version: KubernetesVersion(vtn.GetResourceVersion()), 78 } 79 } 80 }, 81 UpdateFunc: func(oldObj, newObj any) { 82 vtn := newObj.(*vtv1beta1.VitessTopoNode) 83 out, err := unpackValue([]byte(vtn.Data.Value)) 84 if err != nil { 85 changes <- &topo.WatchData{Err: err} 86 close(gracefulShutdown) 87 } else { 88 changes <- &topo.WatchData{ 89 Contents: out, 90 Version: KubernetesVersion(vtn.GetResourceVersion()), 91 } 92 } 93 }, 94 DeleteFunc: func(obj any) { 95 vtn := obj.(*vtv1beta1.VitessTopoNode) 96 changes <- &topo.WatchData{Err: topo.NewError(topo.NoNode, vtn.Name)} 97 close(gracefulShutdown) 98 }, 99 }, indexers) 100 101 // create control chan for informer and start it 102 informerChan := make(chan struct{}) 103 go memberInformer.Run(informerChan) 104 105 // Handle interrupts 106 go closeOnDone(ctx, filePath, informerChan, gracefulShutdown, changes) 107 108 return current, changes, nil 109 } 110 111 func closeOnDone(ctx context.Context, filePath string, informerChan chan struct{}, gracefulShutdown chan struct{}, changes chan *topo.WatchData) { 112 select { 113 case <-ctx.Done(): 114 if err := ctx.Err(); err != nil && err == context.Canceled { 115 changes <- &topo.WatchData{Err: topo.NewError(topo.Interrupted, filePath)} 116 } 117 case <-gracefulShutdown: 118 } 119 close(informerChan) 120 close(changes) 121 } 122 123 // WatchRecursive is part of the topo.Conn interface. 124 func (s *Server) WatchRecursive(_ context.Context, path string) ([]*topo.WatchDataRecursive, <-chan *topo.WatchDataRecursive, error) { 125 // Kubernetes doesn't seem to provide a primitive that watches a prefix 126 // or directory, so this likely can never be implemented. 127 return nil, nil, topo.NewError(topo.NoImplementation, path) 128 }