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  }