mirror of
https://github.com/siderolabs/discovery-service.git
synced 2025-03-14 09:55:08 +00:00
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:
@ -25,7 +25,7 @@ spec:
|
||||
baseSpecPath: /api
|
||||
vtProtobufEnabled: true
|
||||
specs:
|
||||
- source: api/storage/storage.proto
|
||||
- source: api/storage/discovery.proto
|
||||
subdirectory: storage
|
||||
genGateway: false
|
||||
---
|
||||
|
14
Dockerfile
14
Dockerfile
@ -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
|
||||
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
|
@ -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
318
pkg/service/service.go
Normal 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
|
||||
}
|
Reference in New Issue
Block a user