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  }