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 }