github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/agent/storage.go (about) 1 package agent 2 3 import ( 4 "github.com/docker/swarmkit/api" 5 "github.com/gogo/protobuf/proto" 6 bolt "go.etcd.io/bbolt" 7 ) 8 9 // Layout: 10 // 11 // bucket(v1.tasks.<id>) -> 12 // data (task protobuf) 13 // status (task status protobuf) 14 // assigned (key present) 15 var ( 16 bucketKeyStorageVersion = []byte("v1") 17 bucketKeyTasks = []byte("tasks") 18 bucketKeyAssigned = []byte("assigned") 19 bucketKeyData = []byte("data") 20 bucketKeyStatus = []byte("status") 21 ) 22 23 // InitDB prepares a database for writing task data. 24 // 25 // Proper buckets will be created if they don't already exist. 26 func InitDB(db *bolt.DB) error { 27 return db.Update(func(tx *bolt.Tx) error { 28 _, err := createBucketIfNotExists(tx, bucketKeyStorageVersion, bucketKeyTasks) 29 return err 30 }) 31 } 32 33 // GetTask retrieves the task with id from the datastore. 34 func GetTask(tx *bolt.Tx, id string) (*api.Task, error) { 35 var t api.Task 36 37 if err := withTaskBucket(tx, id, func(bkt *bolt.Bucket) error { 38 p := bkt.Get(bucketKeyData) 39 if p == nil { 40 return errTaskUnknown 41 } 42 43 return proto.Unmarshal(p, &t) 44 }); err != nil { 45 return nil, err 46 } 47 48 return &t, nil 49 } 50 51 // WalkTasks walks all tasks in the datastore. 52 func WalkTasks(tx *bolt.Tx, fn func(task *api.Task) error) error { 53 bkt := getTasksBucket(tx) 54 if bkt == nil { 55 return nil 56 } 57 58 return bkt.ForEach(func(k, v []byte) error { 59 tbkt := bkt.Bucket(k) 60 61 p := tbkt.Get(bucketKeyData) 62 var t api.Task 63 if err := proto.Unmarshal(p, &t); err != nil { 64 return err 65 } 66 67 return fn(&t) 68 }) 69 } 70 71 // TaskAssigned returns true if the task is assigned to the node. 72 func TaskAssigned(tx *bolt.Tx, id string) bool { 73 bkt := getTaskBucket(tx, id) 74 if bkt == nil { 75 return false 76 } 77 78 return len(bkt.Get(bucketKeyAssigned)) > 0 79 } 80 81 // GetTaskStatus returns the current status for the task. 82 func GetTaskStatus(tx *bolt.Tx, id string) (*api.TaskStatus, error) { 83 var ts api.TaskStatus 84 if err := withTaskBucket(tx, id, func(bkt *bolt.Bucket) error { 85 p := bkt.Get(bucketKeyStatus) 86 if p == nil { 87 return errTaskUnknown 88 } 89 90 return proto.Unmarshal(p, &ts) 91 }); err != nil { 92 return nil, err 93 } 94 95 return &ts, nil 96 } 97 98 // WalkTaskStatus calls fn for the status of each task. 99 func WalkTaskStatus(tx *bolt.Tx, fn func(id string, status *api.TaskStatus) error) error { 100 bkt := getTasksBucket(tx) 101 if bkt == nil { 102 return nil 103 } 104 105 return bkt.ForEach(func(k, v []byte) error { 106 tbkt := bkt.Bucket(k) 107 108 p := tbkt.Get(bucketKeyStatus) 109 var ts api.TaskStatus 110 if err := proto.Unmarshal(p, &ts); err != nil { 111 return err 112 } 113 114 return fn(string(k), &ts) 115 }) 116 } 117 118 // PutTask places the task into the database. 119 func PutTask(tx *bolt.Tx, task *api.Task) error { 120 return withCreateTaskBucketIfNotExists(tx, task.ID, func(bkt *bolt.Bucket) error { 121 taskCopy := *task 122 taskCopy.Status = api.TaskStatus{} // blank out the status. 123 124 p, err := proto.Marshal(&taskCopy) 125 if err != nil { 126 return err 127 } 128 return bkt.Put(bucketKeyData, p) 129 }) 130 } 131 132 // PutTaskStatus updates the status for the task with id. 133 func PutTaskStatus(tx *bolt.Tx, id string, status *api.TaskStatus) error { 134 // this used to be withCreateTaskBucketIfNotExists, but that could lead 135 // to weird race conditions, and was not necessary. 136 return withTaskBucket(tx, id, func(bkt *bolt.Bucket) error { 137 p, err := proto.Marshal(status) 138 if err != nil { 139 return err 140 } 141 return bkt.Put(bucketKeyStatus, p) 142 }) 143 } 144 145 // DeleteTask completely removes the task from the database. 146 func DeleteTask(tx *bolt.Tx, id string) error { 147 bkt := getTasksBucket(tx) 148 if bkt == nil { 149 return nil 150 } 151 152 return bkt.DeleteBucket([]byte(id)) 153 } 154 155 // SetTaskAssignment sets the current assignment state. 156 func SetTaskAssignment(tx *bolt.Tx, id string, assigned bool) error { 157 return withTaskBucket(tx, id, func(bkt *bolt.Bucket) error { 158 if assigned { 159 return bkt.Put(bucketKeyAssigned, []byte{0xFF}) 160 } 161 return bkt.Delete(bucketKeyAssigned) 162 }) 163 } 164 165 func createBucketIfNotExists(tx *bolt.Tx, keys ...[]byte) (*bolt.Bucket, error) { 166 bkt, err := tx.CreateBucketIfNotExists(keys[0]) 167 if err != nil { 168 return nil, err 169 } 170 171 for _, key := range keys[1:] { 172 bkt, err = bkt.CreateBucketIfNotExists(key) 173 if err != nil { 174 return nil, err 175 } 176 } 177 178 return bkt, nil 179 } 180 181 func withCreateTaskBucketIfNotExists(tx *bolt.Tx, id string, fn func(bkt *bolt.Bucket) error) error { 182 bkt, err := createBucketIfNotExists(tx, bucketKeyStorageVersion, bucketKeyTasks, []byte(id)) 183 if err != nil { 184 return err 185 } 186 187 return fn(bkt) 188 } 189 190 func withTaskBucket(tx *bolt.Tx, id string, fn func(bkt *bolt.Bucket) error) error { 191 bkt := getTaskBucket(tx, id) 192 if bkt == nil { 193 return errTaskUnknown 194 } 195 196 return fn(bkt) 197 } 198 199 func getTaskBucket(tx *bolt.Tx, id string) *bolt.Bucket { 200 return getBucket(tx, bucketKeyStorageVersion, bucketKeyTasks, []byte(id)) 201 } 202 203 func getTasksBucket(tx *bolt.Tx) *bolt.Bucket { 204 return getBucket(tx, bucketKeyStorageVersion, bucketKeyTasks) 205 } 206 207 func getBucket(tx *bolt.Tx, keys ...[]byte) *bolt.Bucket { 208 bkt := tx.Bucket(keys[0]) 209 210 for _, key := range keys[1:] { 211 if bkt == nil { 212 break 213 } 214 bkt = bkt.Bucket(key) 215 } 216 217 return bkt 218 }