mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 11:03:34 +00:00
chore: handle RemoveContainer server-side that deletes container from machine db
This commit is contained in:
@@ -1144,7 +1144,7 @@ var file_internal_machine_api_pb_docker_proto_rawDesc = []byte{
|
||||
0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x12, 0x25, 0x0a, 0x0e,
|
||||
0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e,
|
||||
0x61, 0x6d, 0x65, 0x32, 0xf4, 0x05, 0x0a, 0x06, 0x44, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x12, 0x4c,
|
||||
0x61, 0x6d, 0x65, 0x32, 0xc3, 0x06, 0x0a, 0x06, 0x44, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x12, 0x4c,
|
||||
0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65,
|
||||
0x72, 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f,
|
||||
0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c,
|
||||
@@ -1191,11 +1191,16 @@ var file_internal_machine_api_pb_docker_proto_rawDesc = []byte{
|
||||
0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x74,
|
||||
0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61,
|
||||
0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e,
|
||||
0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69,
|
||||
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x73, 0x76, 0x69, 0x64, 0x65, 0x72,
|
||||
0x73, 0x6b, 0x69, 0x2f, 0x75, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x69, 0x6e, 0x74, 0x65,
|
||||
0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69,
|
||||
0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x16, 0x52, 0x65,
|
||||
0x6d, 0x6f, 0x76, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61,
|
||||
0x69, 0x6e, 0x65, 0x72, 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76,
|
||||
0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74,
|
||||
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x73, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73,
|
||||
0x6b, 0x69, 0x2f, 0x75, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72,
|
||||
0x6e, 0x61, 0x6c, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f,
|
||||
0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -1251,18 +1256,20 @@ var file_internal_machine_api_pb_docker_proto_depIdxs = []int32{
|
||||
12, // 13: api.Docker.InspectImage:input_type -> api.InspectImageRequest
|
||||
15, // 14: api.Docker.InspectRemoteImage:input_type -> api.InspectRemoteImageRequest
|
||||
18, // 15: api.Docker.CreateServiceContainer:input_type -> api.CreateServiceContainerRequest
|
||||
1, // 16: api.Docker.CreateContainer:output_type -> api.CreateContainerResponse
|
||||
3, // 17: api.Docker.InspectContainer:output_type -> api.InspectContainerResponse
|
||||
20, // 18: api.Docker.StartContainer:output_type -> google.protobuf.Empty
|
||||
20, // 19: api.Docker.StopContainer:output_type -> google.protobuf.Empty
|
||||
7, // 20: api.Docker.ListContainers:output_type -> api.ListContainersResponse
|
||||
20, // 21: api.Docker.RemoveContainer:output_type -> google.protobuf.Empty
|
||||
11, // 22: api.Docker.PullImage:output_type -> api.JSONMessage
|
||||
13, // 23: api.Docker.InspectImage:output_type -> api.InspectImageResponse
|
||||
16, // 24: api.Docker.InspectRemoteImage:output_type -> api.InspectRemoteImageResponse
|
||||
1, // 25: api.Docker.CreateServiceContainer:output_type -> api.CreateContainerResponse
|
||||
16, // [16:26] is the sub-list for method output_type
|
||||
6, // [6:16] is the sub-list for method input_type
|
||||
9, // 16: api.Docker.RemoveServiceContainer:input_type -> api.RemoveContainerRequest
|
||||
1, // 17: api.Docker.CreateContainer:output_type -> api.CreateContainerResponse
|
||||
3, // 18: api.Docker.InspectContainer:output_type -> api.InspectContainerResponse
|
||||
20, // 19: api.Docker.StartContainer:output_type -> google.protobuf.Empty
|
||||
20, // 20: api.Docker.StopContainer:output_type -> google.protobuf.Empty
|
||||
7, // 21: api.Docker.ListContainers:output_type -> api.ListContainersResponse
|
||||
20, // 22: api.Docker.RemoveContainer:output_type -> google.protobuf.Empty
|
||||
11, // 23: api.Docker.PullImage:output_type -> api.JSONMessage
|
||||
13, // 24: api.Docker.InspectImage:output_type -> api.InspectImageResponse
|
||||
16, // 25: api.Docker.InspectRemoteImage:output_type -> api.InspectRemoteImageResponse
|
||||
1, // 26: api.Docker.CreateServiceContainer:output_type -> api.CreateContainerResponse
|
||||
20, // 27: api.Docker.RemoveServiceContainer:output_type -> google.protobuf.Empty
|
||||
17, // [17:28] is the sub-list for method output_type
|
||||
6, // [6:17] is the sub-list for method input_type
|
||||
6, // [6:6] is the sub-list for extension type_name
|
||||
6, // [6:6] is the sub-list for extension extendee
|
||||
0, // [0:6] is the sub-list for field type_name
|
||||
|
||||
@@ -21,6 +21,7 @@ service Docker {
|
||||
rpc InspectRemoteImage(InspectRemoteImageRequest) returns (InspectRemoteImageResponse);
|
||||
|
||||
rpc CreateServiceContainer(CreateServiceContainerRequest) returns (CreateContainerResponse);
|
||||
rpc RemoveServiceContainer(RemoveContainerRequest) returns (google.protobuf.Empty);
|
||||
}
|
||||
|
||||
message CreateContainerRequest {
|
||||
|
||||
@@ -30,6 +30,7 @@ const (
|
||||
Docker_InspectImage_FullMethodName = "/api.Docker/InspectImage"
|
||||
Docker_InspectRemoteImage_FullMethodName = "/api.Docker/InspectRemoteImage"
|
||||
Docker_CreateServiceContainer_FullMethodName = "/api.Docker/CreateServiceContainer"
|
||||
Docker_RemoveServiceContainer_FullMethodName = "/api.Docker/RemoveServiceContainer"
|
||||
)
|
||||
|
||||
// DockerClient is the client API for Docker service.
|
||||
@@ -48,6 +49,7 @@ type DockerClient interface {
|
||||
// Docker auth credentials if necessary.
|
||||
InspectRemoteImage(ctx context.Context, in *InspectRemoteImageRequest, opts ...grpc.CallOption) (*InspectRemoteImageResponse, error)
|
||||
CreateServiceContainer(ctx context.Context, in *CreateServiceContainerRequest, opts ...grpc.CallOption) (*CreateContainerResponse, error)
|
||||
RemoveServiceContainer(ctx context.Context, in *RemoveContainerRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
}
|
||||
|
||||
type dockerClient struct {
|
||||
@@ -167,6 +169,16 @@ func (c *dockerClient) CreateServiceContainer(ctx context.Context, in *CreateSer
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *dockerClient) RemoveServiceContainer(ctx context.Context, in *RemoveContainerRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(emptypb.Empty)
|
||||
err := c.cc.Invoke(ctx, Docker_RemoveServiceContainer_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// DockerServer is the server API for Docker service.
|
||||
// All implementations must embed UnimplementedDockerServer
|
||||
// for forward compatibility.
|
||||
@@ -183,6 +195,7 @@ type DockerServer interface {
|
||||
// Docker auth credentials if necessary.
|
||||
InspectRemoteImage(context.Context, *InspectRemoteImageRequest) (*InspectRemoteImageResponse, error)
|
||||
CreateServiceContainer(context.Context, *CreateServiceContainerRequest) (*CreateContainerResponse, error)
|
||||
RemoveServiceContainer(context.Context, *RemoveContainerRequest) (*emptypb.Empty, error)
|
||||
mustEmbedUnimplementedDockerServer()
|
||||
}
|
||||
|
||||
@@ -223,6 +236,9 @@ func (UnimplementedDockerServer) InspectRemoteImage(context.Context, *InspectRem
|
||||
func (UnimplementedDockerServer) CreateServiceContainer(context.Context, *CreateServiceContainerRequest) (*CreateContainerResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CreateServiceContainer not implemented")
|
||||
}
|
||||
func (UnimplementedDockerServer) RemoveServiceContainer(context.Context, *RemoveContainerRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method RemoveServiceContainer not implemented")
|
||||
}
|
||||
func (UnimplementedDockerServer) mustEmbedUnimplementedDockerServer() {}
|
||||
func (UnimplementedDockerServer) testEmbeddedByValue() {}
|
||||
|
||||
@@ -417,6 +433,24 @@ func _Docker_CreateServiceContainer_Handler(srv interface{}, ctx context.Context
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Docker_RemoveServiceContainer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RemoveContainerRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(DockerServer).RemoveServiceContainer(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Docker_RemoveServiceContainer_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(DockerServer).RemoveServiceContainer(ctx, req.(*RemoveContainerRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// Docker_ServiceDesc is the grpc.ServiceDesc for Docker service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
@@ -460,6 +494,10 @@ var Docker_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "CreateServiceContainer",
|
||||
Handler: _Docker_CreateServiceContainer_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "RemoveServiceContainer",
|
||||
Handler: _Docker_RemoveServiceContainer_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
@@ -18,7 +20,6 @@ import (
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Client is a gRPC client for the Docker service that provides a similar interface to the Docker HTTP client.
|
||||
@@ -367,3 +368,23 @@ func (c *Client) CreateServiceContainer(
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// RemoveServiceContainer stops (kills after grace period) and removes a service container with the given ID.
|
||||
// A service container is a container that has been created with CreateServiceContainer.
|
||||
func (c *Client) RemoveServiceContainer(ctx context.Context, id string, opts container.RemoveOptions) error {
|
||||
optsBytes, err := json.Marshal(opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal options: %w", err)
|
||||
}
|
||||
|
||||
_, err = c.grpcClient.RemoveServiceContainer(ctx, &pb.RemoveContainerRequest{
|
||||
Id: id,
|
||||
Options: optsBytes,
|
||||
})
|
||||
if err != nil {
|
||||
if status.Convert(err).Code() == codes.NotFound {
|
||||
return errdefs.NotFound(err)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -32,6 +34,8 @@ import (
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
var fullDockerIDRegex = regexp.MustCompile(`^[a-f0-9]{64}$`)
|
||||
|
||||
// Server implements the gRPC Docker service that proxies requests to the Docker daemon.
|
||||
type Server struct {
|
||||
pb.UnimplementedDockerServer
|
||||
@@ -450,3 +454,34 @@ func (s *Server) CreateServiceContainer(
|
||||
|
||||
return &pb.CreateContainerResponse{Response: respBytes}, nil
|
||||
}
|
||||
|
||||
// RemoveServiceContainer stops (kills after grace period) and removes a service container with the given ID.
|
||||
// The difference between this method and RemoveContainer is that it also removes the container from the machine
|
||||
// database.
|
||||
func (s *Server) RemoveServiceContainer(ctx context.Context, req *pb.RemoveContainerRequest) (*emptypb.Empty, error) {
|
||||
ctrID := req.Id
|
||||
// If the ID is not a full Docker ID, inspect the container to get its full ID.
|
||||
if !fullDockerIDRegex.MatchString(req.Id) {
|
||||
ctr, err := s.client.ContainerInspect(ctx, req.Id)
|
||||
if err != nil {
|
||||
if client.IsErrNotFound(err) {
|
||||
return nil, status.Error(codes.NotFound, err.Error())
|
||||
}
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
ctrID = ctr.ID
|
||||
}
|
||||
|
||||
resp, err := s.RemoveContainer(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err = s.db.ExecContext(ctx, `DELETE FROM containers WHERE id = $1`, ctrID); err != nil {
|
||||
slog.Error("Failed to remove container from machine database.", "err", err, "id", ctrID)
|
||||
// Do not return an error because the container has already been removed from the Docker daemon.
|
||||
// The orphaned db record will be ignored and eventually cleaned up by the garbage collector.
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
dockerclient "github.com/docker/docker/client"
|
||||
@@ -11,7 +13,6 @@ import (
|
||||
"github.com/psviderski/uncloud/internal/secret"
|
||||
"github.com/psviderski/uncloud/pkg/api"
|
||||
"google.golang.org/grpc/status"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CreateContainer creates a new container for the given service on the specified machine.
|
||||
@@ -263,9 +264,9 @@ func (cli *Client) StopContainer(
|
||||
|
||||
// RemoveContainer removes the specified container within the service.
|
||||
func (cli *Client) RemoveContainer(
|
||||
ctx context.Context, serviceID, containerID string, opts container.RemoveOptions,
|
||||
ctx context.Context, serviceID, containerNameOrID string, opts container.RemoveOptions,
|
||||
) error {
|
||||
ctr, err := cli.InspectContainer(ctx, serviceID, containerID)
|
||||
ctr, err := cli.InspectContainer(ctx, serviceID, containerNameOrID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -280,7 +281,7 @@ func (cli *Client) RemoveContainer(
|
||||
eventID := fmt.Sprintf("Container %s on %s", ctr.Container.NameWithoutSlash(), machine.Machine.Name)
|
||||
|
||||
pw.Event(progress.RemovingEvent(eventID))
|
||||
if err = cli.Docker.RemoveContainer(ctx, ctr.Container.ID, opts); err != nil {
|
||||
if err = cli.Docker.RemoveServiceContainer(ctx, ctr.Container.ID, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
pw.Event(progress.RemovedEvent(eventID))
|
||||
|
||||
Reference in New Issue
Block a user