github.com/emate/nomad@v0.8.2-wo-binpacking/client/state_database.go (about)

     1  package client
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  
     7  	"github.com/boltdb/bolt"
     8  	"github.com/hashicorp/nomad/nomad/structs"
     9  	"github.com/ugorji/go/codec"
    10  )
    11  
    12  /*
    13  The client has a boltDB backed state store. The schema as of 0.6 looks as follows:
    14  
    15  allocations/ (bucket)
    16  |--> <alloc-id>/ (bucket)
    17      |--> alloc_runner persisted objects (k/v)
    18  	|--> <task-name>/ (bucket)
    19          |--> task_runner persisted objects (k/v)
    20  */
    21  
    22  var (
    23  	// allocationsBucket is the bucket name containing all allocation related
    24  	// data
    25  	allocationsBucket = []byte("allocations")
    26  )
    27  
    28  func putObject(bkt *bolt.Bucket, key []byte, obj interface{}) error {
    29  	if !bkt.Writable() {
    30  		return fmt.Errorf("bucket must be writable")
    31  	}
    32  
    33  	// Serialize the object
    34  	var buf bytes.Buffer
    35  	if err := codec.NewEncoder(&buf, structs.MsgpackHandle).Encode(obj); err != nil {
    36  		return fmt.Errorf("failed to encode passed object: %v", err)
    37  	}
    38  
    39  	if err := bkt.Put(key, buf.Bytes()); err != nil {
    40  		return fmt.Errorf("failed to write data at key %v: %v", string(key), err)
    41  	}
    42  
    43  	return nil
    44  }
    45  
    46  func putData(bkt *bolt.Bucket, key, value []byte) error {
    47  	if !bkt.Writable() {
    48  		return fmt.Errorf("bucket must be writable")
    49  	}
    50  
    51  	if err := bkt.Put(key, value); err != nil {
    52  		return fmt.Errorf("failed to write data at key %v: %v", string(key), err)
    53  	}
    54  
    55  	return nil
    56  }
    57  
    58  func getObject(bkt *bolt.Bucket, key []byte, obj interface{}) error {
    59  	// Get the data
    60  	data := bkt.Get(key)
    61  	if data == nil {
    62  		return fmt.Errorf("no data at key %v", string(key))
    63  	}
    64  
    65  	// Deserialize the object
    66  	if err := codec.NewDecoderBytes(data, structs.MsgpackHandle).Decode(obj); err != nil {
    67  		return fmt.Errorf("failed to decode data into passed object: %v", err)
    68  	}
    69  
    70  	return nil
    71  }
    72  
    73  // getAllocationBucket returns the bucket used to persist state about a
    74  // particular allocation. If the root allocation bucket or the specific
    75  // allocation bucket doesn't exist, it will be created as long as the
    76  // transaction is writable.
    77  func getAllocationBucket(tx *bolt.Tx, allocID string) (*bolt.Bucket, error) {
    78  	var err error
    79  	w := tx.Writable()
    80  
    81  	// Retrieve the root allocations bucket
    82  	allocations := tx.Bucket(allocationsBucket)
    83  	if allocations == nil {
    84  		if !w {
    85  			return nil, fmt.Errorf("Allocations bucket doesn't exist and transaction is not writable")
    86  		}
    87  
    88  		allocations, err = tx.CreateBucket(allocationsBucket)
    89  		if err != nil {
    90  			return nil, err
    91  		}
    92  	}
    93  
    94  	// Retrieve the specific allocations bucket
    95  	key := []byte(allocID)
    96  	alloc := allocations.Bucket(key)
    97  	if alloc == nil {
    98  		if !w {
    99  			return nil, fmt.Errorf("Allocation bucket doesn't exist and transaction is not writable")
   100  		}
   101  
   102  		alloc, err = allocations.CreateBucket(key)
   103  		if err != nil {
   104  			return nil, err
   105  		}
   106  	}
   107  
   108  	return alloc, nil
   109  }
   110  
   111  // getTaskBucket returns the bucket used to persist state about a
   112  // particular task. If the root allocation bucket, the specific
   113  // allocation or task bucket doesn't exist, they will be created as long as the
   114  // transaction is writable.
   115  func getTaskBucket(tx *bolt.Tx, allocID, taskName string) (*bolt.Bucket, error) {
   116  	alloc, err := getAllocationBucket(tx, allocID)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  
   121  	// Retrieve the specific task bucket
   122  	w := tx.Writable()
   123  	key := []byte(taskName)
   124  	task := alloc.Bucket(key)
   125  	if task == nil {
   126  		if !w {
   127  			return nil, fmt.Errorf("Task bucket doesn't exist and transaction is not writable")
   128  		}
   129  
   130  		task, err = alloc.CreateBucket(key)
   131  		if err != nil {
   132  			return nil, err
   133  		}
   134  	}
   135  
   136  	return task, nil
   137  }
   138  
   139  // deleteAllocationBucket is used to delete an allocation bucket if it exists.
   140  func deleteAllocationBucket(tx *bolt.Tx, allocID string) error {
   141  	if !tx.Writable() {
   142  		return fmt.Errorf("transaction must be writable")
   143  	}
   144  
   145  	// Retrieve the root allocations bucket
   146  	allocations := tx.Bucket(allocationsBucket)
   147  	if allocations == nil {
   148  		return nil
   149  	}
   150  
   151  	// Check if the bucket exists
   152  	key := []byte(allocID)
   153  	if allocBkt := allocations.Bucket(key); allocBkt == nil {
   154  		return nil
   155  	}
   156  
   157  	return allocations.DeleteBucket(key)
   158  }
   159  
   160  // deleteTaskBucket is used to delete a task bucket if it exists.
   161  func deleteTaskBucket(tx *bolt.Tx, allocID, taskName string) error {
   162  	if !tx.Writable() {
   163  		return fmt.Errorf("transaction must be writable")
   164  	}
   165  
   166  	// Retrieve the root allocations bucket
   167  	allocations := tx.Bucket(allocationsBucket)
   168  	if allocations == nil {
   169  		return nil
   170  	}
   171  
   172  	// Retrieve the specific allocations bucket
   173  	alloc := allocations.Bucket([]byte(allocID))
   174  	if alloc == nil {
   175  		return nil
   176  	}
   177  
   178  	// Check if the bucket exists
   179  	key := []byte(taskName)
   180  	if taskBkt := alloc.Bucket(key); taskBkt == nil {
   181  		return nil
   182  	}
   183  
   184  	return alloc.DeleteBucket(key)
   185  }
   186  
   187  func getAllAllocationIDs(tx *bolt.Tx) ([]string, error) {
   188  	allocationsBkt := tx.Bucket(allocationsBucket)
   189  	if allocationsBkt == nil {
   190  		return nil, nil
   191  	}
   192  
   193  	// Create a cursor for iteration.
   194  	var allocIDs []string
   195  	c := allocationsBkt.Cursor()
   196  
   197  	// Iterate over all the buckets
   198  	for k, _ := c.First(); k != nil; k, _ = c.Next() {
   199  		allocIDs = append(allocIDs, string(k))
   200  	}
   201  
   202  	return allocIDs, nil
   203  }