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  }