From 1cba1eef0ad669fb9fe50db85c19cf321c073209 Mon Sep 17 00:00:00 2001 From: Pavel Sviderski Date: Fri, 28 Mar 2025 19:01:19 +1000 Subject: [PATCH] refactor: handle CreateContainer server-side --- go.mod | 10 +- go.sum | 20 +- internal/machine/api/pb/docker.pb.go | 259 +++++++++++++++------- internal/machine/api/pb/docker.proto | 9 + internal/machine/api/pb/docker_grpc.pb.go | 56 ++++- internal/machine/docker/client.go | 28 +++ internal/machine/docker/server.go | 115 ++++++++++ pkg/api/service.go | 18 +- pkg/client/container.go | 82 +------ test/e2e/service_test.go | 8 +- 10 files changed, 423 insertions(+), 182 deletions(-) diff --git a/go.mod b/go.mod index 66ceed68..91e29870 100644 --- a/go.mod +++ b/go.mod @@ -32,6 +32,7 @@ require ( github.com/ipfs/go-ipld-format v0.6.0 github.com/ipfs/go-log/v2 v2.5.1 github.com/lmittmann/tint v1.0.5 + github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.0 github.com/siderolabs/discovery-api v0.1.4 github.com/siderolabs/discovery-client v0.1.9 @@ -44,7 +45,7 @@ require ( golang.org/x/crypto v0.32.0 golang.org/x/net v0.34.0 golang.org/x/sync v0.10.0 - golang.org/x/sys v0.29.0 + golang.org/x/sys v0.31.0 golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 @@ -119,7 +120,7 @@ require ( github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-sql-driver/mysql v1.7.1 // indirect + github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/go-viper/mapstructure/v2 v2.0.0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect @@ -133,7 +134,7 @@ require ( github.com/google/cel-go v0.20.1 // indirect github.com/google/flatbuffers v24.3.25+incompatible // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect + github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/mux v1.8.1 // indirect @@ -171,11 +172,13 @@ require ( github.com/jackc/pgtype v1.14.0 // indirect github.com/jackc/pgx/v4 v4.18.3 // indirect github.com/jbenet/goprocess v0.1.4 // indirect + github.com/jmoiron/sqlx v1.4.0 // indirect github.com/josharian/native v1.1.0 // indirect github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect + github.com/lib/pq v1.10.9 // indirect github.com/libdns/libdns v0.2.2 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-libp2p v0.35.4 // indirect @@ -226,7 +229,6 @@ require ( github.com/multiformats/go-varint v0.0.7 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/onsi/ginkgo/v2 v2.22.0 // indirect - github.com/opencontainers/go-digest v1.0.0 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pires/go-proxyproto v0.7.0 // indirect github.com/pkg/errors v0.9.1 // indirect diff --git a/go.sum b/go.sum index 23730101..d815c52a 100644 --- a/go.sum +++ b/go.sum @@ -334,8 +334,8 @@ github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ4 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= -github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= @@ -410,8 +410,8 @@ github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5b github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= -github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= -github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= @@ -599,8 +599,8 @@ github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8/go.mod h1:Vla75njaFJ8c github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d h1:jRQLvyVGL+iVtDElaEIDdKwpPqUIZJfzkNLV34htpEc= github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/jmoiron/sqlx v1.3.3 h1:j82X0bf7oQ27XeqxicSZsTU5suPwKElg3oyxNn43iTk= -github.com/jmoiron/sqlx v1.3.3/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= +github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= +github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -641,8 +641,9 @@ github.com/lib/pq v0.0.0-20150723085316-0dad96c0b94f/go.mod h1:5WUZQaWbwv1U+lTRe github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s= github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= @@ -703,6 +704,7 @@ github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw= github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o= @@ -1292,8 +1294,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/internal/machine/api/pb/docker.pb.go b/internal/machine/api/pb/docker.pb.go index b53f817d..cae51745 100644 --- a/internal/machine/api/pb/docker.pb.go +++ b/internal/machine/api/pb/docker.pb.go @@ -982,6 +982,70 @@ func (x *RemoteImage) GetManifest() []byte { return nil } +type CreateServiceContainerRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ServiceId string `protobuf:"bytes,1,opt,name=service_id,json=serviceId,proto3" json:"service_id,omitempty"` + // JSON serialised api.ServiceSpec. + ServiceSpec []byte `protobuf:"bytes,2,opt,name=service_spec,json=serviceSpec,proto3" json:"service_spec,omitempty"` + ContainerName string `protobuf:"bytes,3,opt,name=container_name,json=containerName,proto3" json:"container_name,omitempty"` +} + +func (x *CreateServiceContainerRequest) Reset() { + *x = CreateServiceContainerRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_internal_machine_api_pb_docker_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateServiceContainerRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateServiceContainerRequest) ProtoMessage() {} + +func (x *CreateServiceContainerRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_machine_api_pb_docker_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateServiceContainerRequest.ProtoReflect.Descriptor instead. +func (*CreateServiceContainerRequest) Descriptor() ([]byte, []int) { + return file_internal_machine_api_pb_docker_proto_rawDescGZIP(), []int{18} +} + +func (x *CreateServiceContainerRequest) GetServiceId() string { + if x != nil { + return x.ServiceId + } + return "" +} + +func (x *CreateServiceContainerRequest) GetServiceSpec() []byte { + if x != nil { + return x.ServiceSpec + } + return nil +} + +func (x *CreateServiceContainerRequest) GetContainerName() string { + if x != nil { + return x.ContainerName + } + return "" +} + var File_internal_machine_api_pb_docker_proto protoreflect.FileDescriptor var file_internal_machine_api_pb_docker_proto_rawDesc = []byte{ @@ -1071,53 +1135,67 @@ var file_internal_machine_api_pb_docker_proto_rawDesc = []byte{ 0x0a, 0x09, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, - 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x32, 0x98, 0x05, 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, 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, 0x12, 0x4f, 0x0a, 0x10, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, - 0x65, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, - 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, - 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, 0x12, 0x42, 0x0a, 0x0d, 0x53, 0x74, 0x6f, 0x70, - 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x53, 0x74, 0x6f, 0x70, 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, 0x12, 0x49, 0x0a, 0x0e, - 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x1a, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0f, 0x52, 0x65, 0x6d, 0x6f, 0x76, - 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, 0x12, - 0x36, 0x0a, 0x09, 0x50, 0x75, 0x6c, 0x6c, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x15, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x30, 0x01, 0x12, 0x43, 0x0a, 0x0c, 0x49, 0x6e, 0x73, 0x70, 0x65, - 0x63, 0x74, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, - 0x73, 0x70, 0x65, 0x63, 0x74, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x49, - 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x12, - 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x6d, 0x61, - 0x67, 0x65, 0x12, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, - 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, - 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 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, + 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x22, 0x88, 0x01, 0x0a, 0x1d, 0x43, 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, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, + 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, + 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, + 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, 0x12, 0x4f, 0x0a, 0x10, + 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x43, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, + 0x0e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, + 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 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, 0x12, 0x42, 0x0a, 0x0d, 0x53, 0x74, 0x6f, 0x70, 0x43, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x6f, 0x70, 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, 0x12, 0x49, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x43, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0f, 0x52, 0x65, 0x6d, 0x6f, 0x76, 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, 0x12, 0x36, 0x0a, 0x09, 0x50, 0x75, + 0x6c, 0x6c, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x75, + 0x6c, 0x6c, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x30, 0x01, 0x12, 0x43, 0x0a, 0x0c, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x49, 0x6d, 0x61, + 0x67, 0x65, 0x12, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, + 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x12, 0x49, 0x6e, 0x73, 0x70, 0x65, + 0x63, 0x74, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x1e, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, + 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, + 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, } var ( @@ -1132,36 +1210,37 @@ func file_internal_machine_api_pb_docker_proto_rawDescGZIP() []byte { return file_internal_machine_api_pb_docker_proto_rawDescData } -var file_internal_machine_api_pb_docker_proto_msgTypes = make([]protoimpl.MessageInfo, 18) +var file_internal_machine_api_pb_docker_proto_msgTypes = make([]protoimpl.MessageInfo, 19) var file_internal_machine_api_pb_docker_proto_goTypes = []any{ - (*CreateContainerRequest)(nil), // 0: api.CreateContainerRequest - (*CreateContainerResponse)(nil), // 1: api.CreateContainerResponse - (*InspectContainerRequest)(nil), // 2: api.InspectContainerRequest - (*InspectContainerResponse)(nil), // 3: api.InspectContainerResponse - (*StartContainerRequest)(nil), // 4: api.StartContainerRequest - (*StopContainerRequest)(nil), // 5: api.StopContainerRequest - (*ListContainersRequest)(nil), // 6: api.ListContainersRequest - (*ListContainersResponse)(nil), // 7: api.ListContainersResponse - (*MachineContainers)(nil), // 8: api.MachineContainers - (*RemoveContainerRequest)(nil), // 9: api.RemoveContainerRequest - (*PullImageRequest)(nil), // 10: api.PullImageRequest - (*JSONMessage)(nil), // 11: api.JSONMessage - (*InspectImageRequest)(nil), // 12: api.InspectImageRequest - (*InspectImageResponse)(nil), // 13: api.InspectImageResponse - (*Image)(nil), // 14: api.Image - (*InspectRemoteImageRequest)(nil), // 15: api.InspectRemoteImageRequest - (*InspectRemoteImageResponse)(nil), // 16: api.InspectRemoteImageResponse - (*RemoteImage)(nil), // 17: api.RemoteImage - (*Metadata)(nil), // 18: api.Metadata - (*emptypb.Empty)(nil), // 19: google.protobuf.Empty + (*CreateContainerRequest)(nil), // 0: api.CreateContainerRequest + (*CreateContainerResponse)(nil), // 1: api.CreateContainerResponse + (*InspectContainerRequest)(nil), // 2: api.InspectContainerRequest + (*InspectContainerResponse)(nil), // 3: api.InspectContainerResponse + (*StartContainerRequest)(nil), // 4: api.StartContainerRequest + (*StopContainerRequest)(nil), // 5: api.StopContainerRequest + (*ListContainersRequest)(nil), // 6: api.ListContainersRequest + (*ListContainersResponse)(nil), // 7: api.ListContainersResponse + (*MachineContainers)(nil), // 8: api.MachineContainers + (*RemoveContainerRequest)(nil), // 9: api.RemoveContainerRequest + (*PullImageRequest)(nil), // 10: api.PullImageRequest + (*JSONMessage)(nil), // 11: api.JSONMessage + (*InspectImageRequest)(nil), // 12: api.InspectImageRequest + (*InspectImageResponse)(nil), // 13: api.InspectImageResponse + (*Image)(nil), // 14: api.Image + (*InspectRemoteImageRequest)(nil), // 15: api.InspectRemoteImageRequest + (*InspectRemoteImageResponse)(nil), // 16: api.InspectRemoteImageResponse + (*RemoteImage)(nil), // 17: api.RemoteImage + (*CreateServiceContainerRequest)(nil), // 18: api.CreateServiceContainerRequest + (*Metadata)(nil), // 19: api.Metadata + (*emptypb.Empty)(nil), // 20: google.protobuf.Empty } var file_internal_machine_api_pb_docker_proto_depIdxs = []int32{ 8, // 0: api.ListContainersResponse.messages:type_name -> api.MachineContainers - 18, // 1: api.MachineContainers.metadata:type_name -> api.Metadata + 19, // 1: api.MachineContainers.metadata:type_name -> api.Metadata 14, // 2: api.InspectImageResponse.messages:type_name -> api.Image - 18, // 3: api.Image.metadata:type_name -> api.Metadata + 19, // 3: api.Image.metadata:type_name -> api.Metadata 17, // 4: api.InspectRemoteImageResponse.messages:type_name -> api.RemoteImage - 18, // 5: api.RemoteImage.metadata:type_name -> api.Metadata + 19, // 5: api.RemoteImage.metadata:type_name -> api.Metadata 0, // 6: api.Docker.CreateContainer:input_type -> api.CreateContainerRequest 2, // 7: api.Docker.InspectContainer:input_type -> api.InspectContainerRequest 4, // 8: api.Docker.StartContainer:input_type -> api.StartContainerRequest @@ -1171,17 +1250,19 @@ var file_internal_machine_api_pb_docker_proto_depIdxs = []int32{ 10, // 12: api.Docker.PullImage:input_type -> api.PullImageRequest 12, // 13: api.Docker.InspectImage:input_type -> api.InspectImageRequest 15, // 14: api.Docker.InspectRemoteImage:input_type -> api.InspectRemoteImageRequest - 1, // 15: api.Docker.CreateContainer:output_type -> api.CreateContainerResponse - 3, // 16: api.Docker.InspectContainer:output_type -> api.InspectContainerResponse - 19, // 17: api.Docker.StartContainer:output_type -> google.protobuf.Empty - 19, // 18: api.Docker.StopContainer:output_type -> google.protobuf.Empty - 7, // 19: api.Docker.ListContainers:output_type -> api.ListContainersResponse - 19, // 20: api.Docker.RemoveContainer:output_type -> google.protobuf.Empty - 11, // 21: api.Docker.PullImage:output_type -> api.JSONMessage - 13, // 22: api.Docker.InspectImage:output_type -> api.InspectImageResponse - 16, // 23: api.Docker.InspectRemoteImage:output_type -> api.InspectRemoteImageResponse - 15, // [15:24] is the sub-list for method output_type - 6, // [6:15] is the sub-list for method input_type + 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 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 @@ -1410,6 +1491,18 @@ func file_internal_machine_api_pb_docker_proto_init() { return nil } } + file_internal_machine_api_pb_docker_proto_msgTypes[18].Exporter = func(v any, i int) any { + switch v := v.(*CreateServiceContainerRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -1417,7 +1510,7 @@ func file_internal_machine_api_pb_docker_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_internal_machine_api_pb_docker_proto_rawDesc, NumEnums: 0, - NumMessages: 18, + NumMessages: 19, NumExtensions: 0, NumServices: 1, }, diff --git a/internal/machine/api/pb/docker.proto b/internal/machine/api/pb/docker.proto index c309a9d6..77c0f03c 100644 --- a/internal/machine/api/pb/docker.proto +++ b/internal/machine/api/pb/docker.proto @@ -19,6 +19,8 @@ service Docker { // 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 CreateServiceContainer(CreateServiceContainerRequest) returns (CreateContainerResponse); } message CreateContainerRequest { @@ -123,3 +125,10 @@ message RemoteImage { // Raw JSON manifest from the registry. bytes manifest = 3; } + +message CreateServiceContainerRequest { + string service_id = 1; + // JSON serialised api.ServiceSpec. + bytes service_spec = 2; + string container_name = 3; +} diff --git a/internal/machine/api/pb/docker_grpc.pb.go b/internal/machine/api/pb/docker_grpc.pb.go index 32ad607e..f3b6c43d 100644 --- a/internal/machine/api/pb/docker_grpc.pb.go +++ b/internal/machine/api/pb/docker_grpc.pb.go @@ -20,15 +20,16 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( - Docker_CreateContainer_FullMethodName = "/api.Docker/CreateContainer" - Docker_InspectContainer_FullMethodName = "/api.Docker/InspectContainer" - Docker_StartContainer_FullMethodName = "/api.Docker/StartContainer" - Docker_StopContainer_FullMethodName = "/api.Docker/StopContainer" - Docker_ListContainers_FullMethodName = "/api.Docker/ListContainers" - Docker_RemoveContainer_FullMethodName = "/api.Docker/RemoveContainer" - Docker_PullImage_FullMethodName = "/api.Docker/PullImage" - Docker_InspectImage_FullMethodName = "/api.Docker/InspectImage" - Docker_InspectRemoteImage_FullMethodName = "/api.Docker/InspectRemoteImage" + Docker_CreateContainer_FullMethodName = "/api.Docker/CreateContainer" + Docker_InspectContainer_FullMethodName = "/api.Docker/InspectContainer" + Docker_StartContainer_FullMethodName = "/api.Docker/StartContainer" + Docker_StopContainer_FullMethodName = "/api.Docker/StopContainer" + Docker_ListContainers_FullMethodName = "/api.Docker/ListContainers" + Docker_RemoveContainer_FullMethodName = "/api.Docker/RemoveContainer" + Docker_PullImage_FullMethodName = "/api.Docker/PullImage" + Docker_InspectImage_FullMethodName = "/api.Docker/InspectImage" + Docker_InspectRemoteImage_FullMethodName = "/api.Docker/InspectRemoteImage" + Docker_CreateServiceContainer_FullMethodName = "/api.Docker/CreateServiceContainer" ) // DockerClient is the client API for Docker service. @@ -46,6 +47,7 @@ type DockerClient interface { // InspectRemoteImage returns the image metadata for an image in a remote registry using the machine's // 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) } type dockerClient struct { @@ -155,6 +157,16 @@ func (c *dockerClient) InspectRemoteImage(ctx context.Context, in *InspectRemote return out, nil } +func (c *dockerClient) CreateServiceContainer(ctx context.Context, in *CreateServiceContainerRequest, opts ...grpc.CallOption) (*CreateContainerResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CreateContainerResponse) + err := c.cc.Invoke(ctx, Docker_CreateServiceContainer_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. @@ -170,6 +182,7 @@ type DockerServer interface { // InspectRemoteImage returns the image metadata for an image in a remote registry using the machine's // Docker auth credentials if necessary. InspectRemoteImage(context.Context, *InspectRemoteImageRequest) (*InspectRemoteImageResponse, error) + CreateServiceContainer(context.Context, *CreateServiceContainerRequest) (*CreateContainerResponse, error) mustEmbedUnimplementedDockerServer() } @@ -207,6 +220,9 @@ func (UnimplementedDockerServer) InspectImage(context.Context, *InspectImageRequ func (UnimplementedDockerServer) InspectRemoteImage(context.Context, *InspectRemoteImageRequest) (*InspectRemoteImageResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method InspectRemoteImage not implemented") } +func (UnimplementedDockerServer) CreateServiceContainer(context.Context, *CreateServiceContainerRequest) (*CreateContainerResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateServiceContainer not implemented") +} func (UnimplementedDockerServer) mustEmbedUnimplementedDockerServer() {} func (UnimplementedDockerServer) testEmbeddedByValue() {} @@ -383,6 +399,24 @@ func _Docker_InspectRemoteImage_Handler(srv interface{}, ctx context.Context, de return interceptor(ctx, in, info, handler) } +func _Docker_CreateServiceContainer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateServiceContainerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DockerServer).CreateServiceContainer(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Docker_CreateServiceContainer_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DockerServer).CreateServiceContainer(ctx, req.(*CreateServiceContainerRequest)) + } + 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) @@ -422,6 +456,10 @@ var Docker_ServiceDesc = grpc.ServiceDesc{ MethodName: "InspectRemoteImage", Handler: _Docker_InspectRemoteImage_Handler, }, + { + MethodName: "CreateServiceContainer", + Handler: _Docker_CreateServiceContainer_Handler, + }, }, Streams: []grpc.StreamDesc{ { diff --git a/internal/machine/docker/client.go b/internal/machine/docker/client.go index 019d775a..9e5f45b6 100644 --- a/internal/machine/docker/client.go +++ b/internal/machine/docker/client.go @@ -339,3 +339,31 @@ func parseRemoteImageMessage(msg *pb.RemoteImage) (api.MachineRemoteImage, error return mri, nil } + +// CreateServiceContainer creates a new container for the service with the given specifications. +func (c *Client) CreateServiceContainer( + ctx context.Context, serviceID string, spec api.ServiceSpec, containerName string, +) (container.CreateResponse, error) { + var resp container.CreateResponse + + specBytes, err := json.Marshal(spec) + if err != nil { + return resp, fmt.Errorf("marshal service spec: %w", err) + } + grpcResp, err := c.grpcClient.CreateServiceContainer(ctx, &pb.CreateServiceContainerRequest{ + ServiceId: serviceID, + ServiceSpec: specBytes, + ContainerName: containerName, + }) + if err != nil { + if status.Convert(err).Code() == codes.NotFound { + return resp, errdefs.NotFound(err) + } + return resp, err + } + + if err = json.Unmarshal(grpcResp.Response, &resp); err != nil { + return resp, fmt.Errorf("unmarshal gRPC response: %w", err) + } + return resp, nil +} diff --git a/internal/machine/docker/server.go b/internal/machine/docker/server.go index 4c1f6412..46f2f903 100644 --- a/internal/machine/docker/server.go +++ b/internal/machine/docker/server.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "errors" + "fmt" "github.com/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" @@ -11,17 +12,22 @@ import ( "github.com/docker/docker/api/types/image" "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" + "github.com/docker/go-connections/nat" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/psviderski/uncloud/internal/machine/api/pb" + "github.com/psviderski/uncloud/internal/secret" + "github.com/psviderski/uncloud/pkg/api" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/emptypb" "io" + "strconv" + "strings" ) // Server implements the gRPC Docker service that proxies requests to the Docker daemon. @@ -311,3 +317,112 @@ func (s *Server) InspectRemoteImage( }, }, nil } + +// CreateServiceContainer creates a new container for the service with the given specifications. +func (s *Server) CreateServiceContainer( + ctx context.Context, req *pb.CreateServiceContainerRequest, +) (*pb.CreateContainerResponse, error) { + if !api.ValidateServiceID(req.ServiceId) { + return nil, status.Errorf(codes.InvalidArgument, "invalid service ID: '%s'", req.ServiceId) + } + + var spec api.ServiceSpec + if err := json.Unmarshal(req.ServiceSpec, &spec); err != nil { + return nil, status.Errorf(codes.InvalidArgument, "unmarshal service spec: %v", err) + } + spec.ApplyDefaults() + if err := spec.Validate(); err != nil { + return nil, status.Errorf(codes.InvalidArgument, "invalid service spec: %v", err) + } + + containerName := req.ContainerName + if containerName == "" { + suffix, err := secret.RandomAlphaNumeric(4) + if err != nil { + return nil, fmt.Errorf("generate random suffix: %w", err) + } + containerName = fmt.Sprintf("%s-%s", spec.Name, suffix) + } + + // TODO: do not set the immutable hash as container label once container diff uses the spec stored in DB. + specHash, err := spec.ImmutableHash() + if err != nil { + return nil, fmt.Errorf("calculate immutable hash for service spec: %w", err) + } + + config := &container.Config{ + Cmd: spec.Container.Command, + Entrypoint: spec.Container.Entrypoint, + Hostname: containerName, + Image: spec.Container.Image, + Labels: map[string]string{ + api.LabelServiceID: req.ServiceId, + api.LabelServiceName: spec.Name, + api.LabelServiceMode: spec.Mode, + api.LabelServiceSpecHash: specHash, + api.LabelManaged: "", + }, + } + if spec.Mode == "" { + config.Labels[api.LabelServiceMode] = api.ServiceModeReplicated + } + + // TODO: do not set the ports as container labels once migrated to retrieve them from the spec in DB. + if len(spec.Ports) > 0 { + encodedPorts := make([]string, len(spec.Ports)) + for i, p := range spec.Ports { + encodedPorts[i], err = p.String() + if err != nil { + return nil, fmt.Errorf("encode service port spec: %w", err) + } + } + + config.Labels[api.LabelServicePorts] = strings.Join(encodedPorts, ",") + } + + portBindings := make(nat.PortMap) + for _, p := range spec.Ports { + if p.Mode != api.PortModeHost { + continue + } + port := nat.Port(fmt.Sprintf("%d/%s", p.ContainerPort, p.Protocol)) + portBindings[port] = []nat.PortBinding{ + { + HostPort: strconv.Itoa(int(p.PublishedPort)), + }, + } + if p.HostIP.IsValid() { + portBindings[port][0].HostIP = p.HostIP.String() + } + } + hostConfig := &container.HostConfig{ + Binds: spec.Container.Volumes, + Init: spec.Container.Init, + PortBindings: portBindings, + // Always restart service containers if they exit or a machine restarts. + // For one-off containers and batch jobs we plan to use a different service type/mode. + RestartPolicy: container.RestartPolicy{ + Name: container.RestartPolicyAlways, + }, + } + networkConfig := &network.NetworkingConfig{ + EndpointsConfig: map[string]*network.EndpointSettings{ + NetworkName: {}, + }, + } + + resp, err := s.client.ContainerCreate(ctx, config, hostConfig, networkConfig, nil, containerName) + if err != nil { + if client.IsErrNotFound(err) { + return nil, status.Error(codes.NotFound, err.Error()) + } + return nil, status.Error(codes.Internal, err.Error()) + } + + respBytes, err := json.Marshal(resp) + if err != nil { + return nil, status.Errorf(codes.Internal, "marshal response: %v", err) + } + + return &pb.CreateContainerResponse{Response: respBytes}, nil +} diff --git a/pkg/api/service.go b/pkg/api/service.go index 449753c4..b098ccd9 100644 --- a/pkg/api/service.go +++ b/pkg/api/service.go @@ -44,7 +44,21 @@ type ServiceSpec struct { // Ports defines what service ports to publish to make the service accessible outside the cluster. Ports []PortSpec // Replicas is the number of containers to run for the service. Only valid for a replicated service. - Replicas uint + Replicas uint `json:",omitempty"` +} + +func (s *ServiceSpec) ApplyDefaults() { + if s.Mode == "" { + s.Mode = ServiceModeReplicated + } + // Ensure the replicated service has at least one replica. + if s.Mode == ServiceModeReplicated && s.Replicas == 0 { + s.Replicas = 1 + } + + if s.Container.PullPolicy == "" { + s.Container.PullPolicy = PullPolicyMissing + } } func (s *ServiceSpec) Validate() error { @@ -58,6 +72,8 @@ func (s *ServiceSpec) Validate() error { return fmt.Errorf("invalid mode: %q", s.Mode) } + // TODO: validate the service name is a valid DNS label. + for _, p := range s.Ports { if (p.Mode == "" || p.Mode == PortModeIngress) && p.Protocol != ProtocolHTTP && p.Protocol != ProtocolHTTPS { diff --git a/pkg/client/container.go b/pkg/client/container.go index 95e2ba31..83a27347 100644 --- a/pkg/client/container.go +++ b/pkg/client/container.go @@ -6,15 +6,11 @@ import ( "fmt" "github.com/docker/compose/v2/pkg/progress" "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/network" dockerclient "github.com/docker/docker/client" "github.com/docker/docker/pkg/jsonmessage" - "github.com/docker/go-connections/nat" - machinedocker "github.com/psviderski/uncloud/internal/machine/docker" "github.com/psviderski/uncloud/internal/secret" "github.com/psviderski/uncloud/pkg/api" "google.golang.org/grpc/status" - "strconv" "strings" ) @@ -24,8 +20,9 @@ func (cli *Client) CreateContainer( ) (container.CreateResponse, error) { var resp container.CreateResponse - if !api.ValidateServiceID(serviceID) { - return resp, fmt.Errorf("invalid service ID: '%s'", serviceID) + spec.ApplyDefaults() + if err := spec.Validate(); err != nil { + return resp, fmt.Errorf("invalid service spec: %w", err) } // TODO: validate spec.Name is consistent with serviceID if this is not the first container in the service. @@ -40,71 +37,6 @@ func (cli *Client) CreateContainer( } containerName := fmt.Sprintf("%s-%s", spec.Name, suffix) - specHash, err := spec.ImmutableHash() - if err != nil { - return resp, fmt.Errorf("calculate immutable hash for service spec: %w", err) - } - - config := &container.Config{ - Cmd: spec.Container.Command, - Entrypoint: spec.Container.Entrypoint, - Hostname: containerName, - Image: spec.Container.Image, - Labels: map[string]string{ - api.LabelServiceID: serviceID, - api.LabelServiceName: spec.Name, - api.LabelServiceMode: spec.Mode, - api.LabelServiceSpecHash: specHash, - api.LabelManaged: "", - }, - } - if spec.Mode == "" { - config.Labels[api.LabelServiceMode] = api.ServiceModeReplicated - } - - if len(spec.Ports) > 0 { - encodedPorts := make([]string, len(spec.Ports)) - for i, p := range spec.Ports { - encodedPorts[i], err = p.String() - if err != nil { - return resp, fmt.Errorf("encode service port spec: %w", err) - } - } - - config.Labels[api.LabelServicePorts] = strings.Join(encodedPorts, ",") - } - - portBindings := make(nat.PortMap) - for _, p := range spec.Ports { - if p.Mode != api.PortModeHost { - continue - } - port := nat.Port(fmt.Sprintf("%d/%s", p.ContainerPort, p.Protocol)) - portBindings[port] = []nat.PortBinding{ - { - HostPort: strconv.Itoa(int(p.PublishedPort)), - }, - } - if p.HostIP.IsValid() { - portBindings[port][0].HostIP = p.HostIP.String() - } - } - hostConfig := &container.HostConfig{ - Binds: spec.Container.Volumes, - Init: spec.Container.Init, - PortBindings: portBindings, - // Always restart service containers if they exit or a machine restarts. - // For one-off containers and batch jobs we plan to use a different service type/mode. - RestartPolicy: container.RestartPolicy{ - Name: container.RestartPolicyAlways, - }, - } - netConfig := &network.NetworkingConfig{ - EndpointsConfig: map[string]*network.EndpointSettings{ - machinedocker.NetworkName: {}, - }, - } - // Proxy Docker gRPC requests to the selected machine. ctx = proxyToMachine(ctx, machine.Machine) @@ -113,12 +45,12 @@ func (cli *Client) CreateContainer( pw.Event(progress.CreatingEvent(eventID)) if spec.Container.PullPolicy == api.PullPolicyAlways { - if err = cli.pullImageWithProgress(ctx, config.Image, machine.Machine.Name, eventID); err != nil { + if err = cli.pullImageWithProgress(ctx, spec.Container.Image, machine.Machine.Name, eventID); err != nil { return resp, err } } - resp, err = cli.Docker.CreateContainer(ctx, config, hostConfig, netConfig, nil, containerName) + resp, err = cli.Docker.CreateServiceContainer(ctx, serviceID, spec, containerName) if err != nil { switch spec.Container.PullPolicy { case api.PullPolicyAlways, api.PullPolicyNever: @@ -134,10 +66,10 @@ func (cli *Client) CreateContainer( } // Pull the missing image and create the container again. - if err = cli.pullImageWithProgress(ctx, config.Image, machine.Machine.Name, eventID); err != nil { + if err = cli.pullImageWithProgress(ctx, spec.Container.Image, machine.Machine.Name, eventID); err != nil { return resp, err } - if resp, err = cli.Docker.CreateContainer(ctx, config, hostConfig, netConfig, nil, containerName); err != nil { + if resp, err = cli.Docker.CreateServiceContainer(ctx, serviceID, spec, containerName); err != nil { return resp, err } } diff --git a/test/e2e/service_test.go b/test/e2e/service_test.go index 875c7a80..7f673479 100644 --- a/test/e2e/service_test.go +++ b/test/e2e/service_test.go @@ -766,9 +766,15 @@ func TestServiceLifecycle(t *testing.T) { t.Parallel() invalidIDs := []string{"", "invalid", "651aef23ae90"} + spec := api.ServiceSpec{ + Name: "invalid-service-id", + Container: api.ContainerSpec{ + Image: "portainer/pause:latest", + }, + } for _, invalidID := range invalidIDs { - _, err := cli.CreateContainer(ctx, invalidID, api.ServiceSpec{}, c.Machines[0].Name) + _, err := cli.CreateContainer(ctx, invalidID, spec, c.Machines[0].Name) require.ErrorContains(t, err, "invalid service ID") } })