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