api/services/content: define the content service

Bring the content service into the containerd API. This allows the
content store to be coordinated in the containerd daemon with minimal
effort. For the most part, this API follows the conventions and behavior
of the existing content store implementation with a few caveats.
Specifically, we remove the object oriented transaction mechanism in
favor of a very rich `Write` call.

Pains are taken to reduce race conditions around when having multiple
writers to a single piece of content. Clients should be able to race
towards getting a write lock on a reference, then wait on each other.

For the most part, this should be generically pluggable to allow
implementations of the content store to be swapped out.

We'll follow this up with an implementation to validate the model.

Signed-off-by: Stephen J Day <stephen.day@docker.com>
This commit is contained in:
Stephen J Day 2017-02-13 18:44:01 -08:00
parent 4cb31d9615
commit baaf7543dc
No known key found for this signature in database
GPG Key ID: 67B3DED84EDC823F
2 changed files with 2591 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,201 @@
syntax = "proto3";
package containerd.v1;
import "gogoproto/gogo.proto";
import "google/protobuf/timestamp.proto";
// Content provides access to a content addressable storage system.
service Content {
// Info returns information about a committed object.
//
// This call can be used for getting the size of content and checking for
// existence.
rpc Info(InfoRequest) returns (InfoResponse);
// Read allows one to read an object based on the offset into the content.
//
// The requested data may be returned in one or more messages.
rpc Read(ReadRequest) returns (stream ReadResponse);
// Status returns the status of ongoing object ingestions, started via
// Write.
//
// For active ingestions, the status will be streamed until the client
// closes the connection or all matched ingestions are committed.
rpc Status(StatusRequest) returns (stream StatusResponse);
// Write begins or resumes writes to a resource identified by a unique ref.
// Only one active stream may exist at a time for each ref.
//
// Once a write stream has started, it may only write to a single ref, thus
// once a stream is started, the ref may be ommitted on subsequent writes.
//
// For any write transaction represented by a ref, only a single write may
// be made to a given offset. If overlapping writes occur, it is an error.
// Writes should be sequential and implementations may throw an error if
// this is required.
//
// If expected_digest is set and already part of the content store, the
// write will fail.
//
// When completed, the commit flag should be set to true. If expected size
// or digest is set, the content will be validated against those values.
rpc Write(stream WriteRequest) returns (stream WriteResponse);
}
message InfoRequest {
string digest = 1 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false];
}
message InfoResponse {
// Digest is the hash identity of the blob.
string digest = 1 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false];
// Size is the total number of bytes in the blob.
int64 size = 2;
// CommittedAt provides the time at which the blob was committed.
google.protobuf.Timestamp committed_at = 3 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
}
// ReadRequest defines the fields that make up a request to read a portion of
// data from a stored object.
message ReadRequest {
// Digest is the hash identity to read.
string digest = 1 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false];
// Offset specifies the number of bytes from the start at which to begin
// the read. If zero or less, the read will be from the start. This uses
// standard zero-indexed semantics.
int64 offset = 2;
// size is the total size of the read. If zero, the entire blob will be
// returned by the service.
int64 size = 3;
}
// ReadResponse carries byte data for a read request.
message ReadResponse {
int64 offset = 1; // offset of the returned data
bytes data = 2; // actual data
}
// WriteAction defines the behavior of a WriteRequest.
enum WriteAction {
option (gogoproto.goproto_enum_prefix) = false;
option (gogoproto.enum_customname) = "WriteAction";
// WriteActionStat instructs the writer to return the current status while
// holding the lock on the write.
STAT = 0 [(gogoproto.enumvalue_customname)="WriteActionStat"];
// WriteActionWrite sets the action for the write request to write data.
//
// Any data included will be written at the provided offset. The
// transaction will be left open for further writes.
//
// This is the default.
WRITE = 1 [(gogoproto.enumvalue_customname)="WriteActionWrite"];
// WriteActionCommit will write any outstanding data in the message and
// commit the write, storing it under the digest.
//
// This can be used in a single message to send the data, verify it and
// commit it.
//
// This action will always terminate the write.
COMMIT = 2 [(gogoproto.enumvalue_customname)="WriteActionCommit"];
// WriteActionAbort will release any resources associated with the write
// and free up the ref for a completely new set of writes.
//
// This action will always terminate the write.
ABORT = -1 [(gogoproto.enumvalue_customname)="WriteActionAbort"];
}
// WriteRequest writes data to the request ref at offset.
message WriteRequest {
// Action sets the behavior of the write.
//
// When this is a write and the ref is not yet allocated, the ref will be
// allocated and the data will be written at offset.
//
// If the action is write and the ref is allocated, it will accept data to
// an offset that has not yet been written.
//
// If the action is write and there is no data, the current write status
// will be returned. This works differently from status because the stream
// holds a lock.
WriteAction action = 1;
// Ref identifies the pre-commit object to write to.
string ref = 2;
// ExpectedSize can be set to have the service validate the total size of
// the of committed content.
//
// The latest value before or with the commit action message will be use to
// validate the content. It is only required on one message for the write.
//
// If the value is zero or less, no validation of the final content will be
// performed.
int64 expected_size = 3;
// ExpectedDigest can be set to have the service validate the final content
// against the provided digest.
//
// If the digest is already present in the object store, an AlreadyPresent
// error will be returned.
//
// Only the latest version will be used to check the content against the
// digest. It is only required to include it on a single message, before or
// with the commit action message.
string expected_digest = 4 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false];
// Offset specifies the number of bytes from the start at which to begin
// the write. If zero or less, the write will be from the start. This uses
// standard zero-indexed semantics.
int64 offset = 5;
// Data is the actual bytes to be written.
//
// If this is empty and the message is not a commit, a response will be
// returned with the current write state.
bytes data = 6;
}
// WriteResponse is returned on the culmination of a write call.
message WriteResponse {
// Action contains the action for the final message of the stream. A writer
// should confirm that they match the intended result.
WriteAction action = 1;
// Offset provides the current "committed" size for the Write.
int64 offset = 2;
// Digest, if present, includes the digest up to the currently committed
// bytes. If action is commit, this field will be set. It is implementation
// defined if this is set for other actions, except abort. On abort, this
// will be empty.
string digest = 3 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false];
// StartedAt is the time at which the write first started.
google.protobuf.Timestamp started_at = 4 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
// UpdatedAt is the time the write was last updated.
google.protobuf.Timestamp updated_at = 5 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
}
message StatusRequest {
repeated string refs = 1;
repeated string prefix = 2;
}
message StatusResponse {
string ref = 1;
int64 offset = 2;
google.protobuf.Timestamp started_at = 3 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
google.protobuf.Timestamp updated_at = 4 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
}