github.com/hhrutter/nomad@v0.6.0-rc2.0.20170723054333-80c4b03f0705/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  func allocationBucketExists(tx *bolt.Tx, allocID string) bool {
   112  	allocations := tx.Bucket(allocationsBucket)
   113  	if allocations == nil {
   114  		return false
   115  	}
   116  
   117  	// Retrieve the specific allocations bucket
   118  	alloc := allocations.Bucket([]byte(allocID))
   119  	return alloc != nil
   120  }
   121  
   122  // getTaskBucket returns the bucket used to persist state about a
   123  // particular task. If the root allocation bucket, the specific
   124  // allocation or task bucket doesn't exist, they will be created as long as the
   125  // transaction is writable.
   126  func getTaskBucket(tx *bolt.Tx, allocID, taskName string) (*bolt.Bucket, error) {
   127  	alloc, err := getAllocationBucket(tx, allocID)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  
   132  	// Retrieve the specific task bucket
   133  	w := tx.Writable()
   134  	key := []byte(taskName)
   135  	task := alloc.Bucket(key)
   136  	if task == nil {
   137  		if !w {
   138  			return nil, fmt.Errorf("Task bucket doesn't exist and transaction is not writable")
   139  		}
   140  
   141  		task, err = alloc.CreateBucket(key)
   142  		if err != nil {
   143  			return nil, err
   144  		}
   145  	}
   146  
   147  	return task, nil
   148  }
   149  
   150  // deleteAllocationBucket is used to delete an allocation bucket if it exists.
   151  func deleteAllocationBucket(tx *bolt.Tx, allocID string) error {
   152  	if !tx.Writable() {
   153  		return fmt.Errorf("transaction must be writable")
   154  	}
   155  
   156  	// Retrieve the root allocations bucket
   157  	allocations := tx.Bucket(allocationsBucket)
   158  	if allocations == nil {
   159  		return nil
   160  	}
   161  
   162  	// Check if the bucket exists
   163  	key := []byte(allocID)
   164  	if allocBkt := allocations.Bucket(key); allocBkt == nil {
   165  		return nil
   166  	}
   167  
   168  	return allocations.DeleteBucket(key)
   169  }
   170  
   171  // deleteTaskBucket is used to delete a task bucket if it exists.
   172  func deleteTaskBucket(tx *bolt.Tx, allocID, taskName string) error {
   173  	if !tx.Writable() {
   174  		return fmt.Errorf("transaction must be writable")
   175  	}
   176  
   177  	// Retrieve the root allocations bucket
   178  	allocations := tx.Bucket(allocationsBucket)
   179  	if allocations == nil {
   180  		return nil
   181  	}
   182  
   183  	// Retrieve the specific allocations bucket
   184  	alloc := allocations.Bucket([]byte(allocID))
   185  	if alloc == nil {
   186  		return nil
   187  	}
   188  
   189  	// Check if the bucket exists
   190  	key := []byte(taskName)
   191  	if taskBkt := alloc.Bucket(key); taskBkt == nil {
   192  		return nil
   193  	}
   194  
   195  	return alloc.DeleteBucket(key)
   196  }
   197  
   198  func getAllAllocationIDs(tx *bolt.Tx) ([]string, error) {
   199  	allocationsBkt := tx.Bucket(allocationsBucket)
   200  	if allocationsBkt == nil {
   201  		return nil, nil
   202  	}
   203  
   204  	// Create a cursor for iteration.
   205  	var allocIDs []string
   206  	c := allocationsBkt.Cursor()
   207  
   208  	// Iterate over all the buckets
   209  	for k, _ := c.First(); k != nil; k, _ = c.Next() {
   210  		allocIDs = append(allocIDs, string(k))
   211  	}
   212  
   213  	return allocIDs, nil
   214  }