feat: export service entrypoint

Move the entrypoint logic into its own package, so it can be imported from other projects.

Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
This commit is contained in:
Utku Ozdemir
2024-05-29 10:40:05 +02:00
parent 86e131779a
commit 74bca2da5c
7 changed files with 428 additions and 320 deletions

View File

@ -25,7 +25,7 @@ spec:
baseSpecPath: /api
vtProtobufEnabled: true
specs:
- source: api/storage/storage.proto
- source: api/storage/discovery.proto
subdirectory: storage
genGateway: false
---

View File

@ -2,7 +2,7 @@
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
#
# Generated on 2024-05-28T00:03:10Z by kres bcb280a.
# Generated on 2024-05-30T08:00:54Z by kres f249b6c.
ARG TOOLCHAIN
@ -21,7 +21,7 @@ RUN markdownlint --ignore "CHANGELOG.md" --ignore "**/node_modules/**" --ignore
# collects proto specs
FROM scratch AS proto-specs
ADD api/storage/storage.proto /api/storage/
ADD api/storage/discovery.proto /api/storage/
# base toolchain image
FROM --platform=${BUILDPLATFORM} ${TOOLCHAIN} AS toolchain
@ -37,6 +37,9 @@ ENV GOTOOLCHAIN ${GOTOOLCHAIN}
ARG GOEXPERIMENT
ENV GOEXPERIMENT ${GOEXPERIMENT}
ENV GOPATH /go
ARG GOIMPORTS_VERSION
RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg go install golang.org/x/tools/cmd/goimports@v${GOIMPORTS_VERSION}
RUN mv /go/bin/goimports /bin
ARG PROTOBUF_GO_VERSION
RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg go install google.golang.org/protobuf/cmd/protoc-gen-go@v${PROTOBUF_GO_VERSION}
RUN mv /go/bin/protoc-gen-go /bin
@ -46,9 +49,6 @@ RUN mv /go/bin/protoc-gen-go-grpc /bin
ARG GRPC_GATEWAY_VERSION
RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@v${GRPC_GATEWAY_VERSION}
RUN mv /go/bin/protoc-gen-grpc-gateway /bin
ARG GOIMPORTS_VERSION
RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg go install golang.org/x/tools/cmd/goimports@v${GOIMPORTS_VERSION}
RUN mv /go/bin/goimports /bin
ARG VTPROTOBUF_VERSION
RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg go install github.com/planetscale/vtprotobuf/cmd/protoc-gen-go-vtproto@v${VTPROTOBUF_VERSION}
RUN mv /go/bin/protoc-gen-go-vtproto /bin
@ -81,8 +81,8 @@ RUN --mount=type=cache,target=/go/pkg go list -mod=readonly all >/dev/null
# runs protobuf compiler
FROM tools AS proto-compile
COPY --from=proto-specs / /
RUN protoc -I/api --go_out=paths=source_relative:/api --go-grpc_out=paths=source_relative:/api --go-vtproto_out=paths=source_relative:/api --go-vtproto_opt=features=marshal+unmarshal+size+equal+clone /api/storage/storage.proto
RUN rm /api/storage/storage.proto
RUN protoc -I/api --go_out=paths=source_relative:/api --go-grpc_out=paths=source_relative:/api --go-vtproto_out=paths=source_relative:/api --go-vtproto_opt=features=marshal+unmarshal+size+equal+clone /api/storage/discovery.proto
RUN rm /api/storage/discovery.proto
RUN goimports -w -local github.com/siderolabs/discovery-service /api
RUN gofumpt -w /api

View File

@ -1,8 +1,8 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
// protoc-gen-go v1.34.1
// protoc v4.24.4
// source: storage/storage.proto
// source: storage/discovery.proto
package storagepb
@ -34,7 +34,7 @@ type StateSnapshot struct {
func (x *StateSnapshot) Reset() {
*x = StateSnapshot{}
if protoimpl.UnsafeEnabled {
mi := &file_storage_storage_proto_msgTypes[0]
mi := &file_storage_discovery_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -47,7 +47,7 @@ func (x *StateSnapshot) String() string {
func (*StateSnapshot) ProtoMessage() {}
func (x *StateSnapshot) ProtoReflect() protoreflect.Message {
mi := &file_storage_storage_proto_msgTypes[0]
mi := &file_storage_discovery_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -60,7 +60,7 @@ func (x *StateSnapshot) ProtoReflect() protoreflect.Message {
// Deprecated: Use StateSnapshot.ProtoReflect.Descriptor instead.
func (*StateSnapshot) Descriptor() ([]byte, []int) {
return file_storage_storage_proto_rawDescGZIP(), []int{0}
return file_storage_discovery_proto_rawDescGZIP(), []int{0}
}
func (x *StateSnapshot) GetClusters() []*ClusterSnapshot {
@ -83,7 +83,7 @@ type ClusterSnapshot struct {
func (x *ClusterSnapshot) Reset() {
*x = ClusterSnapshot{}
if protoimpl.UnsafeEnabled {
mi := &file_storage_storage_proto_msgTypes[1]
mi := &file_storage_discovery_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -96,7 +96,7 @@ func (x *ClusterSnapshot) String() string {
func (*ClusterSnapshot) ProtoMessage() {}
func (x *ClusterSnapshot) ProtoReflect() protoreflect.Message {
mi := &file_storage_storage_proto_msgTypes[1]
mi := &file_storage_discovery_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -109,7 +109,7 @@ func (x *ClusterSnapshot) ProtoReflect() protoreflect.Message {
// Deprecated: Use ClusterSnapshot.ProtoReflect.Descriptor instead.
func (*ClusterSnapshot) Descriptor() ([]byte, []int) {
return file_storage_storage_proto_rawDescGZIP(), []int{1}
return file_storage_discovery_proto_rawDescGZIP(), []int{1}
}
func (x *ClusterSnapshot) GetId() string {
@ -141,7 +141,7 @@ type AffiliateSnapshot struct {
func (x *AffiliateSnapshot) Reset() {
*x = AffiliateSnapshot{}
if protoimpl.UnsafeEnabled {
mi := &file_storage_storage_proto_msgTypes[2]
mi := &file_storage_discovery_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -154,7 +154,7 @@ func (x *AffiliateSnapshot) String() string {
func (*AffiliateSnapshot) ProtoMessage() {}
func (x *AffiliateSnapshot) ProtoReflect() protoreflect.Message {
mi := &file_storage_storage_proto_msgTypes[2]
mi := &file_storage_discovery_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -167,7 +167,7 @@ func (x *AffiliateSnapshot) ProtoReflect() protoreflect.Message {
// Deprecated: Use AffiliateSnapshot.ProtoReflect.Descriptor instead.
func (*AffiliateSnapshot) Descriptor() ([]byte, []int) {
return file_storage_storage_proto_rawDescGZIP(), []int{2}
return file_storage_discovery_proto_rawDescGZIP(), []int{2}
}
func (x *AffiliateSnapshot) GetId() string {
@ -211,7 +211,7 @@ type EndpointSnapshot struct {
func (x *EndpointSnapshot) Reset() {
*x = EndpointSnapshot{}
if protoimpl.UnsafeEnabled {
mi := &file_storage_storage_proto_msgTypes[3]
mi := &file_storage_discovery_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -224,7 +224,7 @@ func (x *EndpointSnapshot) String() string {
func (*EndpointSnapshot) ProtoMessage() {}
func (x *EndpointSnapshot) ProtoReflect() protoreflect.Message {
mi := &file_storage_storage_proto_msgTypes[3]
mi := &file_storage_discovery_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -237,7 +237,7 @@ func (x *EndpointSnapshot) ProtoReflect() protoreflect.Message {
// Deprecated: Use EndpointSnapshot.ProtoReflect.Descriptor instead.
func (*EndpointSnapshot) Descriptor() ([]byte, []int) {
return file_storage_storage_proto_rawDescGZIP(), []int{3}
return file_storage_discovery_proto_rawDescGZIP(), []int{3}
}
func (x *EndpointSnapshot) GetExpiration() *timestamppb.Timestamp {
@ -254,72 +254,73 @@ func (x *EndpointSnapshot) GetData() []byte {
return nil
}
var File_storage_storage_proto protoreflect.FileDescriptor
var File_storage_discovery_proto protoreflect.FileDescriptor
var file_storage_storage_proto_rawDesc = []byte{
0x0a, 0x15, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x18, 0x73, 0x69, 0x64, 0x65, 0x72, 0x6f, 0x2e,
0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67,
0x65, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x22, 0x56, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73,
0x68, 0x6f, 0x74, 0x12, 0x45, 0x0a, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x18,
0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x73, 0x69, 0x64, 0x65, 0x72, 0x6f, 0x2e, 0x64,
0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74,
0x52, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x22, 0x6e, 0x0a, 0x0f, 0x43, 0x6c,
0x75, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x0e, 0x0a,
0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x4b, 0x0a,
0x0a, 0x61, 0x66, 0x66, 0x69, 0x6c, 0x69, 0x61, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x2b, 0x2e, 0x73, 0x69, 0x64, 0x65, 0x72, 0x6f, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f,
0x76, 0x65, 0x72, 0x79, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x41, 0x66, 0x66,
0x69, 0x6c, 0x69, 0x61, 0x74, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x0a,
0x61, 0x66, 0x66, 0x69, 0x6c, 0x69, 0x61, 0x74, 0x65, 0x73, 0x22, 0xbd, 0x01, 0x0a, 0x11, 0x41,
var file_storage_discovery_proto_rawDesc = []byte{
0x0a, 0x17, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76,
0x65, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x18, 0x73, 0x69, 0x64, 0x65, 0x72,
0x6f, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x2e, 0x73, 0x74, 0x6f, 0x72,
0x61, 0x67, 0x65, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x22, 0x56, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x6e, 0x61,
0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x45, 0x0a, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72,
0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x73, 0x69, 0x64, 0x65, 0x72, 0x6f,
0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61,
0x67, 0x65, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68,
0x6f, 0x74, 0x52, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x22, 0x6e, 0x0a, 0x0f,
0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12,
0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12,
0x4b, 0x0a, 0x0a, 0x61, 0x66, 0x66, 0x69, 0x6c, 0x69, 0x61, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x73, 0x69, 0x64, 0x65, 0x72, 0x6f, 0x2e, 0x64, 0x69, 0x73,
0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x41,
0x66, 0x66, 0x69, 0x6c, 0x69, 0x61, 0x74, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74,
0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64,
0x12, 0x3a, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02,
0x52, 0x0a, 0x61, 0x66, 0x66, 0x69, 0x6c, 0x69, 0x61, 0x74, 0x65, 0x73, 0x22, 0xbd, 0x01, 0x0a,
0x11, 0x41, 0x66, 0x66, 0x69, 0x6c, 0x69, 0x61, 0x74, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68,
0x6f, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02,
0x69, 0x64, 0x12, 0x3a, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
0x6d, 0x70, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12,
0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61,
0x74, 0x61, 0x12, 0x48, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18,
0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x73, 0x69, 0x64, 0x65, 0x72, 0x6f, 0x2e, 0x64,
0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f,
0x74, 0x52, 0x09, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x62, 0x0a, 0x10,
0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74,
0x12, 0x3a, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04,
0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61,
0x12, 0x48, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x73, 0x69, 0x64, 0x65, 0x72, 0x6f, 0x2e, 0x64, 0x69, 0x73,
0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x45,
0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52,
0x09, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x62, 0x0a, 0x10, 0x45, 0x6e,
0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x3a,
0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a,
0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61,
0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x42, 0x37,
0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x69, 0x64,
0x65, 0x72, 0x6f, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72,
0x79, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x74,
0x6f, 0x72, 0x61, 0x67, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61,
0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73,
0x69, 0x64, 0x65, 0x72, 0x6f, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76,
0x65, 0x72, 0x79, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f,
0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
}
var (
file_storage_storage_proto_rawDescOnce sync.Once
file_storage_storage_proto_rawDescData = file_storage_storage_proto_rawDesc
file_storage_discovery_proto_rawDescOnce sync.Once
file_storage_discovery_proto_rawDescData = file_storage_discovery_proto_rawDesc
)
func file_storage_storage_proto_rawDescGZIP() []byte {
file_storage_storage_proto_rawDescOnce.Do(func() {
file_storage_storage_proto_rawDescData = protoimpl.X.CompressGZIP(file_storage_storage_proto_rawDescData)
func file_storage_discovery_proto_rawDescGZIP() []byte {
file_storage_discovery_proto_rawDescOnce.Do(func() {
file_storage_discovery_proto_rawDescData = protoimpl.X.CompressGZIP(file_storage_discovery_proto_rawDescData)
})
return file_storage_storage_proto_rawDescData
return file_storage_discovery_proto_rawDescData
}
var file_storage_storage_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_storage_storage_proto_goTypes = []interface{}{
var file_storage_discovery_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_storage_discovery_proto_goTypes = []interface{}{
(*StateSnapshot)(nil), // 0: sidero.discovery.storage.StateSnapshot
(*ClusterSnapshot)(nil), // 1: sidero.discovery.storage.ClusterSnapshot
(*AffiliateSnapshot)(nil), // 2: sidero.discovery.storage.AffiliateSnapshot
(*EndpointSnapshot)(nil), // 3: sidero.discovery.storage.EndpointSnapshot
(*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp
}
var file_storage_storage_proto_depIdxs = []int32{
var file_storage_discovery_proto_depIdxs = []int32{
1, // 0: sidero.discovery.storage.StateSnapshot.clusters:type_name -> sidero.discovery.storage.ClusterSnapshot
2, // 1: sidero.discovery.storage.ClusterSnapshot.affiliates:type_name -> sidero.discovery.storage.AffiliateSnapshot
4, // 2: sidero.discovery.storage.AffiliateSnapshot.expiration:type_name -> google.protobuf.Timestamp
@ -332,13 +333,13 @@ var file_storage_storage_proto_depIdxs = []int32{
0, // [0:5] is the sub-list for field type_name
}
func init() { file_storage_storage_proto_init() }
func file_storage_storage_proto_init() {
if File_storage_storage_proto != nil {
func init() { file_storage_discovery_proto_init() }
func file_storage_discovery_proto_init() {
if File_storage_discovery_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_storage_storage_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
file_storage_discovery_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*StateSnapshot); i {
case 0:
return &v.state
@ -350,7 +351,7 @@ func file_storage_storage_proto_init() {
return nil
}
}
file_storage_storage_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
file_storage_discovery_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ClusterSnapshot); i {
case 0:
return &v.state
@ -362,7 +363,7 @@ func file_storage_storage_proto_init() {
return nil
}
}
file_storage_storage_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
file_storage_discovery_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AffiliateSnapshot); i {
case 0:
return &v.state
@ -374,7 +375,7 @@ func file_storage_storage_proto_init() {
return nil
}
}
file_storage_storage_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
file_storage_discovery_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*EndpointSnapshot); i {
case 0:
return &v.state
@ -391,18 +392,18 @@ func file_storage_storage_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_storage_storage_proto_rawDesc,
RawDescriptor: file_storage_discovery_proto_rawDesc,
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_storage_storage_proto_goTypes,
DependencyIndexes: file_storage_storage_proto_depIdxs,
MessageInfos: file_storage_storage_proto_msgTypes,
GoTypes: file_storage_discovery_proto_goTypes,
DependencyIndexes: file_storage_discovery_proto_depIdxs,
MessageInfos: file_storage_discovery_proto_msgTypes,
}.Build()
File_storage_storage_proto = out.File
file_storage_storage_proto_rawDesc = nil
file_storage_storage_proto_goTypes = nil
file_storage_storage_proto_depIdxs = nil
File_storage_discovery_proto = out.File
file_storage_discovery_proto_rawDesc = nil
file_storage_discovery_proto_goTypes = nil
file_storage_discovery_proto_depIdxs = nil
}

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-vtproto. DO NOT EDIT.
// protoc-gen-go-vtproto version: v0.6.0
// source: storage/storage.proto
// source: storage/discovery.proto
package storagepb

View File

@ -8,40 +8,19 @@ package main
import (
"context"
"errors"
"flag"
"fmt"
"log"
"net"
"net/http"
"os"
"os/signal"
"syscall"
"time"
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery"
"github.com/jonboulle/clockwork"
prom "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/siderolabs/discovery-api/api/v1alpha1/server/pb"
"github.com/prometheus/client_golang/prometheus"
"github.com/siderolabs/go-debug"
"go.uber.org/zap"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/experimental"
"google.golang.org/grpc/keepalive"
"google.golang.org/grpc/status"
"github.com/siderolabs/discovery-service/internal/landing"
"github.com/siderolabs/discovery-service/internal/limiter"
_ "github.com/siderolabs/discovery-service/internal/proto"
"github.com/siderolabs/discovery-service/internal/state"
"github.com/siderolabs/discovery-service/internal/state/storage"
"github.com/siderolabs/discovery-service/pkg/limits"
"github.com/siderolabs/discovery-service/pkg/server"
"github.com/siderolabs/discovery-service/pkg/service"
)
var (
@ -95,7 +74,28 @@ func main() {
zap.ReplaceGlobals(logger)
zap.RedirectStdLog(logger)
if err = signalHandler(context.Background(), logger, run); err != nil {
if err = signalHandler(context.Background(), logger, func(ctx context.Context, logger *zap.Logger) error {
return service.Run(ctx, service.Options{
SnapshotsEnabled: snapshotsEnabled,
SnapshotPath: snapshotPath,
SnapshotInterval: snapshotInterval,
RedirectEndpoint: redirectEndpoint,
ListenAddr: listenAddr,
GCInterval: gcInterval,
LandingServerEnabled: true,
LandingAddr: landingAddr,
DebugServerEnabled: true,
DebugAddr: debugAddr,
MetricsServerEnabled: true,
MetricsAddr: metricsAddr,
MetricsRegisterer: prometheus.DefaultRegisterer,
}, logger)
}); err != nil {
logger.Error("service failed", zap.Error(err))
os.Exit(1)
@ -108,214 +108,3 @@ func signalHandler(ctx context.Context, logger *zap.Logger, f func(ctx context.C
return f(ctx, logger)
}
func recoveryHandler(logger *zap.Logger) grpc_recovery.RecoveryHandlerFunc {
return func(p interface{}) error {
if logger != nil {
logger.Error("grpc panic", zap.Any("panic", p), zap.Stack("stack"))
}
return status.Errorf(codes.Internal, "%v", p)
}
}
func interceptorLogger(l *zap.Logger) logging.Logger {
return logging.LoggerFunc(func(_ context.Context, lvl logging.Level, msg string, fields ...any) {
f := make([]zap.Field, 0, len(fields)/2)
for i := 0; i < len(fields); i += 2 {
key := fields[i].(string) //nolint:forcetypeassert,errcheck
value := fields[i+1]
switch v := value.(type) {
case string:
f = append(f, zap.String(key, v))
case int:
f = append(f, zap.Int(key, v))
case bool:
f = append(f, zap.Bool(key, v))
default:
f = append(f, zap.Any(key, v))
}
}
logger := l.WithOptions(zap.AddCallerSkip(1)).With(f...)
switch lvl {
case logging.LevelDebug:
logger.Debug(msg)
case logging.LevelInfo:
logger.Info(msg)
case logging.LevelWarn:
logger.Warn(msg)
case logging.LevelError:
logger.Error(msg)
default:
panic(fmt.Sprintf("unknown level %v", lvl))
}
})
}
func run(ctx context.Context, logger *zap.Logger) error {
logger.Info("service starting")
defer logger.Info("service shut down")
recoveryOpt := grpc_recovery.WithRecoveryHandler(recoveryHandler(logger))
limiter := limiter.NewIPRateLimiter(limits.IPRateRequestsPerSecondMax, limits.IPRateBurstSizeMax)
metrics := grpc_prometheus.NewServerMetrics(
grpc_prometheus.WithServerHandlingTimeHistogram(grpc_prometheus.WithHistogramBuckets([]float64{0.01, 0.1, 0.25, 0.5, 1.0, 2.5})),
)
loggingOpts := []logging.Option{
logging.WithLogOnEvents(logging.StartCall, logging.FinishCall),
logging.WithFieldsFromContext(logging.ExtractFields),
}
//nolint:contextcheck
serverOptions := []grpc.ServerOption{
grpc.ChainUnaryInterceptor(
server.AddLoggingFieldsUnaryServerInterceptor(),
logging.UnaryServerInterceptor(interceptorLogger(logger), loggingOpts...),
server.RateLimitUnaryServerInterceptor(limiter),
metrics.UnaryServerInterceptor(),
grpc_recovery.UnaryServerInterceptor(recoveryOpt),
),
grpc.ChainStreamInterceptor(
server.AddLoggingFieldsStreamServerInterceptor(),
server.RateLimitStreamServerInterceptor(limiter),
logging.StreamServerInterceptor(interceptorLogger(logger), loggingOpts...),
metrics.StreamServerInterceptor(),
grpc_recovery.StreamServerInterceptor(recoveryOpt),
),
grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{
MinTime: 10 * time.Second,
}),
grpc.SharedWriteBuffer(true),
experimental.RecvBufferPool(grpc.NewSharedBufferPool()),
grpc.ReadBufferSize(16 * 1024),
grpc.WriteBufferSize(16 * 1024),
}
state := state.NewState(logger)
prom.MustRegister(state)
var stateStorage *storage.Storage
if snapshotsEnabled {
stateStorage = storage.New(snapshotPath, state, logger)
prom.MustRegister(stateStorage)
if err := stateStorage.Load(); err != nil {
logger.Warn("failed to load state from storage", zap.Error(err))
}
} else {
logger.Info("snapshots are disabled")
}
srv := server.NewClusterServer(state, ctx.Done(), redirectEndpoint)
prom.MustRegister(srv)
lis, err := net.Listen("tcp", listenAddr)
if err != nil {
return fmt.Errorf("failed to listen: %w", err)
}
landingLis, err := net.Listen("tcp", landingAddr)
if err != nil {
return fmt.Errorf("failed to listen: %w", err)
}
s := grpc.NewServer(serverOptions...)
pb.RegisterClusterServer(s, srv)
metrics.InitializeMetrics(s)
if err = prom.Register(metrics); err != nil {
return fmt.Errorf("failed to register metrics: %w", err)
}
var metricsMux http.ServeMux
metricsMux.Handle("/metrics", promhttp.Handler())
metricsServer := http.Server{
Addr: metricsAddr,
Handler: &metricsMux,
}
landingServer := http.Server{
Handler: landing.Handler(state, logger),
}
eg, ctx := errgroup.WithContext(ctx)
if snapshotsEnabled {
eg.Go(func() error {
return stateStorage.Start(ctx, clockwork.NewRealClock(), snapshotInterval)
})
}
eg.Go(func() error {
logger.Info("gRPC server starting", zap.Stringer("address", lis.Addr()))
if err := s.Serve(lis); err != nil {
return fmt.Errorf("failed to serve: %w", err)
}
return nil
})
eg.Go(func() error {
logger.Info("landing server starting", zap.Stringer("address", landingLis.Addr()))
if err := landingServer.Serve(landingLis); err != nil && !errors.Is(err, http.ErrServerClosed) {
return fmt.Errorf("failed to serve: %w", err)
}
return nil
})
eg.Go(func() error {
logger.Info("metrics starting", zap.String("address", metricsServer.Addr))
if err := metricsServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
return err
}
return nil
})
eg.Go(func() error {
<-ctx.Done()
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second)
defer shutdownCancel()
s.GracefulStop()
landingServer.Shutdown(ctx) //nolint:errcheck
metricsServer.Shutdown(shutdownCtx) //nolint:errcheck,contextcheck
return nil
})
eg.Go(func() error {
state.RunGC(ctx, logger, gcInterval)
return nil
})
eg.Go(func() error {
limiter.RunGC(ctx)
return nil
})
eg.Go(func() error {
return debug.ListenAndServe(ctx, debugAddr, func(msg string) { logger.Info(msg) })
})
return eg.Wait()
}

318
pkg/service/service.go Normal file
View File

@ -0,0 +1,318 @@
// Copyright (c) 2024 Sidero Labs, Inc.
//
// Use of this software is governed by the Business Source License
// included in the LICENSE file.
// Package service implements the high-level service entry point.
package service
import (
"context"
"errors"
"fmt"
"net"
"net/http"
"time"
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery"
"github.com/jonboulle/clockwork"
prom "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/siderolabs/discovery-api/api/v1alpha1/server/pb"
"github.com/siderolabs/go-debug"
"go.uber.org/zap"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/experimental"
"google.golang.org/grpc/keepalive"
"google.golang.org/grpc/status"
"github.com/siderolabs/discovery-service/internal/landing"
"github.com/siderolabs/discovery-service/internal/limiter"
"github.com/siderolabs/discovery-service/internal/state"
"github.com/siderolabs/discovery-service/internal/state/storage"
"github.com/siderolabs/discovery-service/pkg/limits"
"github.com/siderolabs/discovery-service/pkg/server"
)
// Options are the configuration options for the service.
type Options struct {
MetricsRegisterer prom.Registerer
LandingAddr string
MetricsAddr string
SnapshotPath string
DebugAddr string
RedirectEndpoint string
ListenAddr string
GCInterval time.Duration
SnapshotInterval time.Duration
LandingServerEnabled bool
DebugServerEnabled bool
MetricsServerEnabled bool
SnapshotsEnabled bool
}
// Run starts the service with the given options.
func Run(ctx context.Context, options Options, logger *zap.Logger) error {
logger.Info("service starting")
defer logger.Info("service shut down")
recoveryOpt := grpc_recovery.WithRecoveryHandler(recoveryHandler(logger))
limiter := limiter.NewIPRateLimiter(limits.IPRateRequestsPerSecondMax, limits.IPRateBurstSizeMax)
metrics := grpc_prometheus.NewServerMetrics(
grpc_prometheus.WithServerHandlingTimeHistogram(grpc_prometheus.WithHistogramBuckets([]float64{0.01, 0.1, 0.25, 0.5, 1.0, 2.5})),
)
loggingOpts := []logging.Option{
logging.WithLogOnEvents(logging.StartCall, logging.FinishCall),
logging.WithFieldsFromContext(logging.ExtractFields),
}
//nolint:contextcheck
serverOptions := []grpc.ServerOption{
grpc.ChainUnaryInterceptor(
server.AddLoggingFieldsUnaryServerInterceptor(),
logging.UnaryServerInterceptor(interceptorLogger(logger), loggingOpts...),
server.RateLimitUnaryServerInterceptor(limiter),
metrics.UnaryServerInterceptor(),
grpc_recovery.UnaryServerInterceptor(recoveryOpt),
),
grpc.ChainStreamInterceptor(
server.AddLoggingFieldsStreamServerInterceptor(),
server.RateLimitStreamServerInterceptor(limiter),
logging.StreamServerInterceptor(interceptorLogger(logger), loggingOpts...),
metrics.StreamServerInterceptor(),
grpc_recovery.StreamServerInterceptor(recoveryOpt),
),
grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{
MinTime: 10 * time.Second,
}),
grpc.SharedWriteBuffer(true),
experimental.RecvBufferPool(grpc.NewSharedBufferPool()),
grpc.ReadBufferSize(16 * 1024),
grpc.WriteBufferSize(16 * 1024),
}
state := state.NewState(logger)
var stateStorage *storage.Storage
if options.SnapshotsEnabled {
stateStorage = storage.New(options.SnapshotPath, state, logger)
if err := stateStorage.Load(); err != nil {
logger.Warn("failed to load state from storage", zap.Error(err))
}
} else {
logger.Info("snapshots are disabled")
}
srv := server.NewClusterServer(state, ctx.Done(), options.RedirectEndpoint)
lis, err := net.Listen("tcp", options.ListenAddr)
if err != nil {
return fmt.Errorf("failed to listen: %w", err)
}
landingLis, err := net.Listen("tcp", options.LandingAddr)
if err != nil {
return fmt.Errorf("failed to listen: %w", err)
}
s := grpc.NewServer(serverOptions...)
pb.RegisterClusterServer(s, srv)
metrics.InitializeMetrics(s)
var (
metricsServer http.Server
landingServer http.Server
)
if options.MetricsServerEnabled {
var metricsMux http.ServeMux
metricsMux.Handle("/metrics", promhttp.Handler())
metricsServer = http.Server{
Addr: options.MetricsAddr,
Handler: &metricsMux,
}
}
if options.LandingServerEnabled {
landingServer = http.Server{
Handler: landing.Handler(state, logger),
}
}
eg, ctx := errgroup.WithContext(ctx)
if options.SnapshotsEnabled {
eg.Go(func() error {
return stateStorage.Start(ctx, clockwork.NewRealClock(), options.SnapshotInterval)
})
}
eg.Go(func() error {
logger.Info("gRPC server starting", zap.Stringer("address", lis.Addr()))
if serveErr := s.Serve(lis); serveErr != nil {
return fmt.Errorf("failed to serve: %w", serveErr)
}
return nil
})
if options.LandingServerEnabled {
eg.Go(func() error {
logger.Info("landing server starting", zap.Stringer("address", landingLis.Addr()))
if serveErr := landingServer.Serve(landingLis); serveErr != nil && !errors.Is(serveErr, http.ErrServerClosed) {
return fmt.Errorf("failed to serve: %w", serveErr)
}
return nil
})
}
if options.MetricsServerEnabled {
eg.Go(func() error {
logger.Info("metrics starting", zap.String("address", metricsServer.Addr))
if serveErr := metricsServer.ListenAndServe(); serveErr != nil && !errors.Is(serveErr, http.ErrServerClosed) {
return serveErr
}
return nil
})
}
eg.Go(func() error {
<-ctx.Done()
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second)
defer shutdownCancel()
s.GracefulStop()
if options.LandingServerEnabled {
landingServer.Shutdown(ctx) //nolint:errcheck
}
if options.MetricsServerEnabled {
metricsServer.Shutdown(shutdownCtx) //nolint:errcheck,contextcheck
}
return nil
})
eg.Go(func() error {
state.RunGC(ctx, logger, options.GCInterval)
return nil
})
eg.Go(func() error {
limiter.RunGC(ctx)
return nil
})
if options.DebugServerEnabled {
eg.Go(func() error {
return debug.ListenAndServe(ctx, options.DebugAddr, func(msg string) { logger.Info(msg) })
})
}
if options.MetricsRegisterer != nil {
collectors := []prom.Collector{state, srv, metrics, stateStorage}
defer unregisterCollectors(options.MetricsRegisterer, collectors...)
if err = registerCollectors(options.MetricsRegisterer, collectors...); err != nil {
return fmt.Errorf("failed to register collectors: %w", err)
}
}
return eg.Wait()
}
func recoveryHandler(logger *zap.Logger) grpc_recovery.RecoveryHandlerFunc {
return func(p interface{}) error {
if logger != nil {
logger.Error("grpc panic", zap.Any("panic", p), zap.Stack("stack"))
}
return status.Errorf(codes.Internal, "%v", p)
}
}
func interceptorLogger(l *zap.Logger) logging.Logger {
return logging.LoggerFunc(func(_ context.Context, lvl logging.Level, msg string, fields ...any) {
f := make([]zap.Field, 0, len(fields)/2)
for i := 0; i < len(fields); i += 2 {
key := fields[i].(string) //nolint:forcetypeassert,errcheck
value := fields[i+1]
switch v := value.(type) {
case string:
f = append(f, zap.String(key, v))
case int:
f = append(f, zap.Int(key, v))
case bool:
f = append(f, zap.Bool(key, v))
default:
f = append(f, zap.Any(key, v))
}
}
logger := l.WithOptions(zap.AddCallerSkip(1)).With(f...)
switch lvl {
case logging.LevelDebug:
logger.Debug(msg)
case logging.LevelInfo:
logger.Info(msg)
case logging.LevelWarn:
logger.Warn(msg)
case logging.LevelError:
logger.Error(msg)
default:
panic(fmt.Sprintf("unknown level %v", lvl))
}
})
}
func unregisterCollectors(registerer prom.Registerer, collectors ...prom.Collector) {
for _, collector := range collectors {
if collector == nil {
continue
}
registerer.Unregister(collector)
}
}
func registerCollectors(registerer prom.Registerer, collectors ...prom.Collector) (err error) {
for _, collector := range collectors {
if collector == nil {
continue
}
if err = registerer.Register(collector); err != nil {
return fmt.Errorf("failed to register collector: %w", err)
}
}
return nil
}