Files
uncloud/internal/machine/api/pb/docker.proto
T
79dc05cb66 feat: service logs command to stream service logs (#196)
* chore: fix landing logo shadow

* feat: implement service logs command with colored output and strict ordering

* revert Makefile

* fix after rebase

* move logs command under services with root shortcut

* simplify server options for streaming logs and CLI flags

* refactor ContainerLogs grpc server

* update --tail flag

* minor docker.proto

* refactor client ServiceLogs and ContainerLogs

* move ProxyMachinesContext from api to client pkg

* minor refactor proto Stream

* add api/logs

* implement LogMerger

* fix LogMerger to correctly use semaphore

* increate inflish entries to 100 per stream

* send heartbeats

* refactor ContainerLogs to synchronise Send of entries and heartbeats to the steram

* minor logmerege

* remove ContainerName form ServiceLogEntryMetadata

* refactor ContainerLogs into docker.Service

* detect stalled container logs streams

* update logmerger tests

* fix comment in test

* refactor LogMerger with options

* uc logs: format one or multiple services

* make LogMerger emit heartbeats, emit entries <= watermark, rewrite tests

* update uc logs with new LogMerger

* go mod tidy

* fix after merge

---------

Co-authored-by: Evgenii Orlov <evgenii.orlov@semrush.com>
2025-12-02 19:50:48 +10:00

279 lines
7.6 KiB
Protocol Buffer

syntax = "proto3";
package api;
option go_package = "github.com/psviderski/uncloud/internal/machine/api/pb";
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
import "internal/machine/api/pb/common.proto";
service Docker {
rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse);
rpc InspectContainer(InspectContainerRequest) returns (InspectContainerResponse);
rpc StartContainer(StartContainerRequest) returns (google.protobuf.Empty);
rpc StopContainer(StopContainerRequest) returns (google.protobuf.Empty);
rpc ListContainers(ListContainersRequest) returns (ListContainersResponse);
rpc RemoveContainer(RemoveContainerRequest) returns (google.protobuf.Empty);
rpc ExecContainer(stream ExecContainerRequest) returns (stream ExecContainerResponse);
rpc ContainerLogs(ContainerLogsRequest) returns (stream ContainerLogEntry);
rpc PullImage(PullImageRequest) returns (stream JSONMessage);
rpc InspectImage(InspectImageRequest) returns (InspectImageResponse);
// InspectRemoteImage returns the image metadata for an image in a remote registry using the machine's
// Docker auth credentials if necessary.
rpc InspectRemoteImage(InspectRemoteImageRequest) returns (InspectRemoteImageResponse);
rpc ListImages(ListImagesRequest) returns (ListImagesResponse);
rpc CreateVolume(CreateVolumeRequest) returns (CreateVolumeResponse);
rpc ListVolumes(ListVolumesRequest) returns (ListVolumesResponse);
rpc RemoveVolume(RemoveVolumeRequest) returns (google.protobuf.Empty);
rpc CreateServiceContainer(CreateServiceContainerRequest) returns (CreateContainerResponse);
rpc InspectServiceContainer(InspectContainerRequest) returns (ServiceContainer);
rpc ListServiceContainers(ListServiceContainersRequest) returns (ListServiceContainersResponse);
rpc RemoveServiceContainer(RemoveContainerRequest) returns (google.protobuf.Empty);
}
message CreateContainerRequest {
// JSON serialised container.Config.
bytes config = 1;
// JSON serialised container.HostConfig.
bytes host_config = 2;
// JSON serialised network.NetworkingConfig.
bytes network_config = 3;
// JSON serialised ocispec.Platform.
bytes platform = 4;
string name = 5;
}
message CreateContainerResponse {
// JSON serialised container.CreateResponse.
bytes response = 1;
}
message InspectContainerRequest {
string id = 1;
}
message InspectContainerResponse {
// JSON serialised container.InspectResponse.
bytes response = 1;
}
message StartContainerRequest {
string id = 1;
// JSON serialised container.StartOptions.
bytes options = 2;
}
message StopContainerRequest {
string id = 1;
// JSON serialised container.StopOptions.
bytes options = 2;
}
message ListContainersRequest {
// JSON serialised container.ListOptions.
bytes options = 1;
}
message ListContainersResponse {
// Must contain only one repeated messages field to allow broadcasting ListContainers requests to multiple machines.
repeated MachineContainers messages = 1;
}
message MachineContainers {
Metadata metadata = 1;
// JSON serialised []container.InspectResponse.
bytes containers = 2;
}
message RemoveContainerRequest {
string id = 1;
// JSON serialised container.RemoveOptions.
bytes options = 2;
}
message ExecContainerRequest {
oneof payload {
// Initial configuration for the exec session. Must be sent as the first message.
ExecConfig config = 1;
// Raw stdin data to be written to the exec process.
bytes stdin = 2;
// TTY resize event (only used when TTY is enabled).
ResizeEvent resize = 3;
}
}
message ExecConfig {
// Container ID to execute the command in.
string container_id = 1;
// JSON serialised ExecOptions
bytes options = 2;
}
message ResizeEvent {
uint32 height = 1;
uint32 width = 2;
}
message ExecContainerResponse {
oneof payload {
// Exec instance ID returned after creating the exec.
string exec_id = 1;
// Raw stdout data from the exec process.
bytes stdout = 2;
// Raw stderr data from the exec process (only when TTY is disabled).
bytes stderr = 3;
// Exit code of the exec process. Sent as the final message.
int32 exit_code = 4;
}
}
message ContainerLogsRequest {
string container_id = 1;
// Options for logs retrieval.
bool follow = 2;
int32 tail = 3; // -1 means all
string since = 4; // https://www.rfc-editor.org/rfc/rfc3339.html timestamp or Go duration string
string until = 5; // https://www.rfc-editor.org/rfc/rfc3339.html timestamp or Go duration string
}
message ContainerLogEntry {
enum StreamType {
UNKNOWN = 0;
STDOUT = 1;
STDERR = 2;
HEARTBEAT = 3;
}
StreamType stream = 1;
google.protobuf.Timestamp timestamp = 2;
// Log line content. Empty for heartbeat entries.
bytes message = 3;
}
message PullImageRequest {
string image = 1;
// JSON serialised image.PullOptions.
bytes options = 2;
}
message JSONMessage {
// JSON serialised jsonmessage.JSONMessage.
bytes message = 1;
}
message InspectImageRequest {
string id = 1;
}
message InspectImageResponse {
// Must contain only one repeated messages field to allow broadcasting InspectImage requests to multiple machines.
repeated Image messages = 1;
}
message Image {
Metadata metadata = 1;
// JSON serialised image.InspectResponse.
bytes image = 2;
}
message InspectRemoteImageRequest {
string id = 1;
}
message InspectRemoteImageResponse {
// Must contain only one repeated messages field to allow broadcasting InspectRemoteImage requests to multiple machines.
repeated RemoteImage messages = 1;
}
message RemoteImage {
Metadata metadata = 1;
// Image reference in the canonical form with the digest.
string reference = 2;
// Raw JSON manifest from the registry.
bytes manifest = 3;
}
message ListImagesRequest {
// JSON serialised image.ListOptions.
bytes options = 1;
}
message ListImagesResponse {
// Must contain only one repeated messages field to allow broadcasting ListImages requests to multiple machines.
repeated MachineImages messages = 1;
}
message MachineImages {
Metadata metadata = 1;
// JSON serialised []image.Summary.
bytes images = 2;
// True if Docker uses the containerd image store, false if it uses its internal image store.
bool containerd_store = 3;
}
message CreateVolumeRequest {
// JSON serialised volume.CreateOptions.
bytes options = 1;
}
message CreateVolumeResponse {
// JSON serialised volume.Volume.
bytes volume = 1;
}
message ListVolumesRequest {
// JSON serialised volume.ListOptions.
bytes options = 1;
}
message ListVolumesResponse {
// Must contain only one repeated messages field to allow broadcasting ListVolumes requests to multiple machines.
repeated MachineVolumes messages = 1;
}
message MachineVolumes {
Metadata metadata = 1;
// JSON serialised volume.ListResponse.
bytes response = 2;
}
message RemoveVolumeRequest {
string id = 1;
bool force = 2;
}
message CreateServiceContainerRequest {
string service_id = 1;
// JSON serialised api.ServiceSpec.
bytes service_spec = 2;
string container_name = 3;
}
message ServiceContainer {
// JSON serialised container.InspectResponse.
bytes container = 1;
// JSON serialised api.ServiceSpec.
bytes service_spec = 2;
}
message ListServiceContainersRequest {
string service_id = 1;
// JSON serialised container.ListOptions.
bytes options = 2;
}
message ListServiceContainersResponse {
// Must contain only one repeated messages field to allow broadcasting ListServiceContainers requests
// to multiple machines.
repeated MachineServiceContainers messages = 1;
}
message MachineServiceContainers {
Metadata metadata = 1;
repeated ServiceContainer containers = 2;
}