github.com/hernad/nomad@v1.6.112/nomad/state/state_store_service_registration.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package state 5 6 import ( 7 "errors" 8 "fmt" 9 10 "github.com/hashicorp/go-memdb" 11 "github.com/hernad/nomad/nomad/structs" 12 ) 13 14 // UpsertServiceRegistrations is used to insert a number of service 15 // registrations into the state store. It uses a single write transaction for 16 // efficiency, however, any error means no entries will be committed. 17 func (s *StateStore) UpsertServiceRegistrations( 18 msgType structs.MessageType, index uint64, services []*structs.ServiceRegistration) error { 19 20 // Grab a write transaction, so we can use this across all service inserts. 21 txn := s.db.WriteTxnMsgT(msgType, index) 22 defer txn.Abort() 23 24 // updated tracks whether any inserts have been made. This allows us to 25 // skip updating the index table if we do not need to. 26 var updated bool 27 28 // Iterate the array of services. In the event of a single error, all 29 // inserts fail via the txn.Abort() defer. 30 for _, service := range services { 31 serviceUpdated, err := s.upsertServiceRegistrationTxn(index, txn, service) 32 if err != nil { 33 return err 34 } 35 // Ensure we track whether any inserts have been made. 36 updated = updated || serviceUpdated 37 } 38 39 // If we did not perform any inserts, exit early. 40 if !updated { 41 return nil 42 } 43 44 // Perform the index table update to mark the new inserts. 45 if err := txn.Insert(tableIndex, &IndexEntry{TableServiceRegistrations, index}); err != nil { 46 return fmt.Errorf("index update failed: %v", err) 47 } 48 49 return txn.Commit() 50 } 51 52 // upsertServiceRegistrationTxn inserts a single service registration into the 53 // state store using the provided write transaction. It is the responsibility 54 // of the caller to update the index table. 55 func (s *StateStore) upsertServiceRegistrationTxn( 56 index uint64, txn *txn, service *structs.ServiceRegistration) (bool, error) { 57 58 existing, err := txn.First(TableServiceRegistrations, indexID, service.Namespace, service.ID) 59 if err != nil { 60 return false, fmt.Errorf("service registration lookup failed: %v", err) 61 } 62 63 // Set up the indexes correctly to ensure existing indexes are maintained. 64 if existing != nil { 65 exist := existing.(*structs.ServiceRegistration) 66 if exist.Equal(service) { 67 return false, nil 68 } 69 service.CreateIndex = exist.CreateIndex 70 service.ModifyIndex = index 71 } else { 72 service.CreateIndex = index 73 service.ModifyIndex = index 74 } 75 76 // Insert the service registration into the table. 77 if err := txn.Insert(TableServiceRegistrations, service); err != nil { 78 return false, fmt.Errorf("service registration insert failed: %v", err) 79 } 80 return true, nil 81 } 82 83 // DeleteServiceRegistrationByID is responsible for deleting a single service 84 // registration based on it's ID and namespace. If the service registration is 85 // not found within state, an error will be returned. 86 func (s *StateStore) DeleteServiceRegistrationByID( 87 msgType structs.MessageType, index uint64, namespace, id string) error { 88 89 txn := s.db.WriteTxnMsgT(msgType, index) 90 defer txn.Abort() 91 92 if err := s.deleteServiceRegistrationByIDTxn(index, txn, namespace, id); err != nil { 93 return err 94 } 95 return txn.Commit() 96 } 97 98 func (s *StateStore) deleteServiceRegistrationByIDTxn( 99 index uint64, txn *txn, namespace, id string) error { 100 101 // Lookup the service registration by its ID and namespace. This is a 102 // unique index and therefore there will be a maximum of one entry. 103 existing, err := txn.First(TableServiceRegistrations, indexID, namespace, id) 104 if err != nil { 105 return fmt.Errorf("service registration lookup failed: %v", err) 106 } 107 if existing == nil { 108 return errors.New("service registration not found") 109 } 110 111 // Delete the existing entry from the table. 112 if err := txn.Delete(TableServiceRegistrations, existing); err != nil { 113 return fmt.Errorf("service registration deletion failed: %v", err) 114 } 115 116 // Update the index table to indicate an update has occurred. 117 if err := txn.Insert(tableIndex, &IndexEntry{TableServiceRegistrations, index}); err != nil { 118 return fmt.Errorf("index update failed: %v", err) 119 } 120 return nil 121 } 122 123 // DeleteServiceRegistrationByNodeID deletes all service registrations that 124 // belong on a single node. If there are no registrations tied to the nodeID, 125 // the call will noop without an error. 126 func (s *StateStore) DeleteServiceRegistrationByNodeID( 127 msgType structs.MessageType, index uint64, nodeID string) error { 128 129 txn := s.db.WriteTxnMsgT(msgType, index) 130 defer txn.Abort() 131 132 num, err := txn.DeleteAll(TableServiceRegistrations, indexNodeID, nodeID) 133 if err != nil { 134 return fmt.Errorf("deleting service registrations failed: %v", err) 135 } 136 137 // If we did not delete any entries, do not update the index table. 138 // Otherwise, update the table with the latest index. 139 switch num { 140 case 0: 141 return nil 142 default: 143 if err := txn.Insert(tableIndex, &IndexEntry{TableServiceRegistrations, index}); err != nil { 144 return fmt.Errorf("index update failed: %v", err) 145 } 146 } 147 148 return txn.Commit() 149 } 150 151 // GetServiceRegistrations returns an iterator that contains all service 152 // registrations stored within state. This is primarily useful when performing 153 // listings which use the namespace wildcard operator. The caller is 154 // responsible for ensuring ACL access is confirmed, or filtering is performed 155 // before responding. 156 func (s *StateStore) GetServiceRegistrations(ws memdb.WatchSet) (memdb.ResultIterator, error) { 157 txn := s.db.ReadTxn() 158 159 // Walk the entire table. 160 iter, err := txn.Get(TableServiceRegistrations, indexID) 161 if err != nil { 162 return nil, fmt.Errorf("service registration lookup failed: %v", err) 163 } 164 ws.Add(iter.WatchCh()) 165 return iter, nil 166 } 167 168 // GetServiceRegistrationsByNamespace returns an iterator that contains all 169 // registrations belonging to the provided namespace. 170 func (s *StateStore) GetServiceRegistrationsByNamespace( 171 ws memdb.WatchSet, namespace string) (memdb.ResultIterator, error) { 172 txn := s.db.ReadTxn() 173 174 // Walk the entire table. 175 iter, err := txn.Get(TableServiceRegistrations, indexID+"_prefix", namespace, "") 176 if err != nil { 177 return nil, fmt.Errorf("service registration lookup failed: %v", err) 178 } 179 ws.Add(iter.WatchCh()) 180 181 return iter, nil 182 } 183 184 // GetServiceRegistrationByName returns an iterator that contains all service 185 // registrations whose namespace and name match the input parameters. This func 186 // therefore represents how to identify a single, collection of services that 187 // are logically grouped together. 188 func (s *StateStore) GetServiceRegistrationByName( 189 ws memdb.WatchSet, namespace, name string) (memdb.ResultIterator, error) { 190 191 txn := s.db.ReadTxn() 192 193 iter, err := txn.Get(TableServiceRegistrations, indexServiceName, namespace, name) 194 if err != nil { 195 return nil, fmt.Errorf("service registration lookup failed: %v", err) 196 } 197 ws.Add(iter.WatchCh()) 198 199 return iter, nil 200 } 201 202 // GetServiceRegistrationByID returns a single registration. The registration 203 // will be nil, if no matching entry was found; it is the responsibility of the 204 // caller to check for this. 205 func (s *StateStore) GetServiceRegistrationByID( 206 ws memdb.WatchSet, namespace, id string) (*structs.ServiceRegistration, error) { 207 208 txn := s.db.ReadTxn() 209 210 watchCh, existing, err := txn.FirstWatch(TableServiceRegistrations, indexID, namespace, id) 211 if err != nil { 212 return nil, fmt.Errorf("service registration lookup failed: %v", err) 213 } 214 ws.Add(watchCh) 215 216 if existing != nil { 217 return existing.(*structs.ServiceRegistration), nil 218 } 219 return nil, nil 220 } 221 222 // GetServiceRegistrationsByAllocID returns an iterator containing all the 223 // service registrations corresponding to a single allocation. 224 func (s *StateStore) GetServiceRegistrationsByAllocID( 225 ws memdb.WatchSet, allocID string) (memdb.ResultIterator, error) { 226 227 txn := s.db.ReadTxn() 228 229 iter, err := txn.Get(TableServiceRegistrations, indexAllocID, allocID) 230 if err != nil { 231 return nil, fmt.Errorf("service registration lookup failed: %v", err) 232 } 233 ws.Add(iter.WatchCh()) 234 235 return iter, nil 236 } 237 238 // GetServiceRegistrationsByJobID returns an iterator containing all the 239 // service registrations corresponding to a single job. 240 func (s *StateStore) GetServiceRegistrationsByJobID( 241 ws memdb.WatchSet, namespace, jobID string) (memdb.ResultIterator, error) { 242 243 txn := s.db.ReadTxn() 244 245 iter, err := txn.Get(TableServiceRegistrations, indexJob, namespace, jobID) 246 if err != nil { 247 return nil, fmt.Errorf("service registration lookup failed: %v", err) 248 } 249 ws.Add(iter.WatchCh()) 250 251 return iter, nil 252 } 253 254 // GetServiceRegistrationsByNodeID identifies all service registrations tied to 255 // the specified nodeID. This is useful for performing an in-memory lookup in 256 // order to avoid calling DeleteServiceRegistrationByNodeID via a Raft message. 257 func (s *StateStore) GetServiceRegistrationsByNodeID( 258 ws memdb.WatchSet, nodeID string) ([]*structs.ServiceRegistration, error) { 259 260 txn := s.db.ReadTxn() 261 262 iter, err := txn.Get(TableServiceRegistrations, indexNodeID, nodeID) 263 if err != nil { 264 return nil, fmt.Errorf("service registration lookup failed: %v", err) 265 } 266 ws.Add(iter.WatchCh()) 267 268 var result []*structs.ServiceRegistration 269 for raw := iter.Next(); raw != nil; raw = iter.Next() { 270 result = append(result, raw.(*structs.ServiceRegistration)) 271 } 272 273 return result, nil 274 }