github.com/cilium/cilium@v1.16.2/pkg/node/local_node_store.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package node 5 6 import ( 7 "context" 8 "io" 9 "sync" 10 11 "github.com/cilium/hive/cell" 12 "github.com/cilium/stream" 13 k8stypes "k8s.io/apimachinery/pkg/types" 14 15 "github.com/cilium/cilium/pkg/cidr" 16 "github.com/cilium/cilium/pkg/lock" 17 "github.com/cilium/cilium/pkg/node/types" 18 ) 19 20 type LocalNode struct { 21 types.Node 22 // OptOutNodeEncryption will make the local node opt-out of node-to-node 23 // encryption 24 OptOutNodeEncryption bool 25 // Unique identifier of the Kubernetes node, used to construct the 26 // corresponding owner reference. 27 UID k8stypes.UID 28 // ID of the node assigned by the cloud provider. 29 ProviderID string 30 // v4 CIDR in which pod IPs are routable 31 IPv4NativeRoutingCIDR *cidr.CIDR 32 // v6 CIDR in which pod IPs are routable 33 IPv6NativeRoutingCIDR *cidr.CIDR 34 } 35 36 // LocalNodeSynchronizer specifies how to build, and keep synchronized the local 37 // node object. 38 type LocalNodeSynchronizer interface { 39 InitLocalNode(context.Context, *LocalNode) error 40 SyncLocalNode(context.Context, *LocalNodeStore) 41 } 42 43 // LocalNodeStoreCell provides the LocalNodeStore instance. 44 // The LocalNodeStore is the canonical owner of `types.Node` for the local node and 45 // provides a reactive API for observing and updating it. 46 var LocalNodeStoreCell = cell.Module( 47 "local-node-store", 48 "Provides LocalNodeStore for observing and updating local node info", 49 50 cell.Provide(NewLocalNodeStore), 51 ) 52 53 // LocalNodeStoreParams are the inputs needed for constructing LocalNodeStore. 54 type LocalNodeStoreParams struct { 55 cell.In 56 57 Lifecycle cell.Lifecycle 58 Sync LocalNodeSynchronizer `optional:"true"` 59 } 60 61 // LocalNodeStore is the canonical owner for the local node object and provides 62 // a reactive API for observing and updating the state. 63 type LocalNodeStore struct { 64 // Changes to the local node are observable. 65 stream.Observable[LocalNode] 66 67 // mu is the main LocalNodeStore mutex, which protects the access to the 68 // different fields during all operations. getMu, instead, is a separate 69 // mutex which is used to guard updates of the value field, as well as its 70 // access by the Get() method. The reason for using two separate mutexes 71 // being that we don't want Get() to be blocked while calling emit, as 72 // that synchronously calls into all subscribers, which is a potentially 73 // expensive operation, and a possible source of deadlocks (e.g., one of 74 // the subscribers needs to acquire another mutex, which is held by a 75 // separate goroutine trying to call LocalNodeStore.Get()). In addition, 76 // getMu also guards the complete field, as it is used by Get() to 77 // determine that the LocalNodeStore was stopped. When both mu and getMu 78 // are to be acquired together, mu shall be always acquired first. 79 mu lock.Mutex 80 getMu lock.RWMutex 81 82 value LocalNode 83 hasValue <-chan struct{} 84 emit func(LocalNode) 85 complete func(error) 86 } 87 88 func NewTestLocalNodeStore(mockNode LocalNode) *LocalNodeStore { 89 src, emit, complete := stream.Multicast[LocalNode](stream.EmitLatest) 90 emit(mockNode) 91 return &LocalNodeStore{ 92 Observable: src, 93 emit: emit, 94 complete: complete, 95 value: mockNode, 96 hasValue: func() <-chan struct{} { 97 ch := make(chan struct{}) 98 close(ch) 99 return ch 100 }(), 101 } 102 } 103 104 func NewLocalNodeStore(params LocalNodeStoreParams) (*LocalNodeStore, error) { 105 src, emit, complete := stream.Multicast[LocalNode](stream.EmitLatest) 106 hasValue := make(chan struct{}) 107 108 s := &LocalNodeStore{ 109 Observable: src, 110 value: LocalNode{Node: types.Node{ 111 // Explicitly initialize the labels and annotations maps, so that 112 // we don't need to always check for nil values. 113 Labels: make(map[string]string), 114 Annotations: make(map[string]string), 115 }}, 116 hasValue: hasValue, 117 } 118 119 bctx, cancel := context.WithCancel(context.Background()) 120 var wg sync.WaitGroup 121 122 params.Lifecycle.Append(cell.Hook{ 123 OnStart: func(ctx cell.HookContext) error { 124 s.mu.Lock() 125 defer s.mu.Unlock() 126 if params.Sync != nil { 127 if err := params.Sync.InitLocalNode(ctx, &s.value); err != nil { 128 return err 129 } 130 131 // Start the synchronization process in background 132 wg.Add(1) 133 go func() { 134 params.Sync.SyncLocalNode(bctx, s) 135 wg.Done() 136 }() 137 } 138 139 // Set the global variable still used by getters 140 // and setters in address.go. We're setting it in Start 141 // to catch uses of it before it's initialized. 142 localNode = s 143 144 s.emit = emit 145 s.complete = complete 146 emit(s.value) 147 close(hasValue) 148 return nil 149 }, 150 OnStop: func(cell.HookContext) error { 151 // Stop the synchronization process (no-op if it had not been started) 152 cancel() 153 wg.Wait() 154 155 s.mu.Lock() 156 s.complete(nil) 157 s.getMu.Lock() 158 s.complete = nil 159 s.emit = nil 160 s.getMu.Unlock() 161 s.mu.Unlock() 162 163 localNode = nil 164 return nil 165 }, 166 }) 167 168 return s, nil 169 } 170 171 // Get retrieves the current local node. Use Get() only for inspecting the state, 172 // e.g. in API handlers. Do not assume the value does not change over time. 173 // Blocks until the store has been initialized. 174 func (s *LocalNodeStore) Get(ctx context.Context) (LocalNode, error) { 175 select { 176 case <-s.hasValue: 177 s.getMu.RLock() 178 defer s.getMu.RUnlock() 179 180 if s.complete == nil { 181 // Return EOF when the LocalNodeStore is stopped, to preserve the 182 // same behavior of stream.First[LocalNode]. 183 return LocalNode{}, io.EOF 184 } 185 186 return s.value, nil 187 188 case <-ctx.Done(): 189 return LocalNode{}, ctx.Err() 190 } 191 } 192 193 // Update modifies the local node with a mutator. The updated value 194 // is passed to observers. Calling LocalNodeStore.Get() from the 195 // mutation function is forbidden, and would result in a deadlock. 196 func (s *LocalNodeStore) Update(update func(*LocalNode)) { 197 s.mu.Lock() 198 defer s.mu.Unlock() 199 200 s.getMu.Lock() 201 update(&s.value) 202 s.getMu.Unlock() 203 204 if s.emit != nil { 205 s.emit(s.value) 206 } 207 }