diff --git a/Dockerfile b/Dockerfile index a4190e315..6c5257bb3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -41,6 +41,8 @@ FROM build AS generate-build # Common needs to be at or near the top to satisfy the subsequent imports COPY ./api/common/common.proto /api/common/common.proto RUN protoc -I/api --go_out=plugins=grpc,paths=source_relative:/api common/common.proto +COPY ./api/health/health.proto /api/health/health.proto +RUN protoc -I/api --go_out=plugins=grpc,paths=source_relative:/api health/health.proto COPY ./api/os/os.proto /api/os/os.proto RUN protoc -I/api --go_out=plugins=grpc,paths=source_relative:/api os/os.proto COPY ./api/security/security.proto /api/security/security.proto @@ -56,6 +58,7 @@ RUN gofumports -w -local github.com/talos-systems/talos /api/ FROM scratch AS generate COPY --from=generate-build /api/common/common.pb.go /api/common/ +COPY --from=generate-build /api/health/health.pb.go /api/health/ COPY --from=generate-build /api/os/os.pb.go /api/os/ COPY --from=generate-build /api/security/security.pb.go /api/security/ COPY --from=generate-build /api/machine/machine.pb.go /api/machine/ diff --git a/api/health/health.pb.go b/api/health/health.pb.go new file mode 100644 index 000000000..e94eb7aea --- /dev/null +++ b/api/health/health.pb.go @@ -0,0 +1,508 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: health/health.proto + +package health + +import ( + context "context" + fmt "fmt" + math "math" + + proto "github.com/golang/protobuf/proto" + empty "github.com/golang/protobuf/ptypes/empty" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = proto.Marshal + _ = fmt.Errorf + _ = math.Inf +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type HealthCheck_ServingStatus int32 + +const ( + HealthCheck_UNKNOWN HealthCheck_ServingStatus = 0 + HealthCheck_SERVING HealthCheck_ServingStatus = 1 + HealthCheck_NOT_SERVING HealthCheck_ServingStatus = 2 +) + +var HealthCheck_ServingStatus_name = map[int32]string{ + 0: "UNKNOWN", + 1: "SERVING", + 2: "NOT_SERVING", +} + +var HealthCheck_ServingStatus_value = map[string]int32{ + "UNKNOWN": 0, + "SERVING": 1, + "NOT_SERVING": 2, +} + +func (x HealthCheck_ServingStatus) String() string { + return proto.EnumName(HealthCheck_ServingStatus_name, int32(x)) +} + +func (HealthCheck_ServingStatus) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_e55f18a46a9fb2dc, []int{1, 0} +} + +type ReadyCheck_ReadyStatus int32 + +const ( + ReadyCheck_UNKNOWN ReadyCheck_ReadyStatus = 0 + ReadyCheck_READY ReadyCheck_ReadyStatus = 1 + ReadyCheck_NOT_READY ReadyCheck_ReadyStatus = 2 +) + +var ReadyCheck_ReadyStatus_name = map[int32]string{ + 0: "UNKNOWN", + 1: "READY", + 2: "NOT_READY", +} + +var ReadyCheck_ReadyStatus_value = map[string]int32{ + "UNKNOWN": 0, + "READY": 1, + "NOT_READY": 2, +} + +func (x ReadyCheck_ReadyStatus) String() string { + return proto.EnumName(ReadyCheck_ReadyStatus_name, int32(x)) +} + +func (ReadyCheck_ReadyStatus) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_e55f18a46a9fb2dc, []int{3, 0} +} + +type HealthWatchRequest struct { + IntervalSeconds int64 `protobuf:"varint,1,opt,name=interval_seconds,json=intervalSeconds,proto3" json:"interval_seconds,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *HealthWatchRequest) Reset() { *m = HealthWatchRequest{} } +func (m *HealthWatchRequest) String() string { return proto.CompactTextString(m) } +func (*HealthWatchRequest) ProtoMessage() {} +func (*HealthWatchRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e55f18a46a9fb2dc, []int{0} +} + +func (m *HealthWatchRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_HealthWatchRequest.Unmarshal(m, b) +} + +func (m *HealthWatchRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_HealthWatchRequest.Marshal(b, m, deterministic) +} + +func (m *HealthWatchRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_HealthWatchRequest.Merge(m, src) +} + +func (m *HealthWatchRequest) XXX_Size() int { + return xxx_messageInfo_HealthWatchRequest.Size(m) +} + +func (m *HealthWatchRequest) XXX_DiscardUnknown() { + xxx_messageInfo_HealthWatchRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_HealthWatchRequest proto.InternalMessageInfo + +func (m *HealthWatchRequest) GetIntervalSeconds() int64 { + if m != nil { + return m.IntervalSeconds + } + return 0 +} + +type HealthCheck struct { + Status HealthCheck_ServingStatus `protobuf:"varint,1,opt,name=status,proto3,enum=health.HealthCheck_ServingStatus" json:"status,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *HealthCheck) Reset() { *m = HealthCheck{} } +func (m *HealthCheck) String() string { return proto.CompactTextString(m) } +func (*HealthCheck) ProtoMessage() {} +func (*HealthCheck) Descriptor() ([]byte, []int) { + return fileDescriptor_e55f18a46a9fb2dc, []int{1} +} + +func (m *HealthCheck) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_HealthCheck.Unmarshal(m, b) +} + +func (m *HealthCheck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_HealthCheck.Marshal(b, m, deterministic) +} + +func (m *HealthCheck) XXX_Merge(src proto.Message) { + xxx_messageInfo_HealthCheck.Merge(m, src) +} + +func (m *HealthCheck) XXX_Size() int { + return xxx_messageInfo_HealthCheck.Size(m) +} + +func (m *HealthCheck) XXX_DiscardUnknown() { + xxx_messageInfo_HealthCheck.DiscardUnknown(m) +} + +var xxx_messageInfo_HealthCheck proto.InternalMessageInfo + +func (m *HealthCheck) GetStatus() HealthCheck_ServingStatus { + if m != nil { + return m.Status + } + return HealthCheck_UNKNOWN +} + +type HealthCheckResponse struct { + Messages []*HealthCheck `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *HealthCheckResponse) Reset() { *m = HealthCheckResponse{} } +func (m *HealthCheckResponse) String() string { return proto.CompactTextString(m) } +func (*HealthCheckResponse) ProtoMessage() {} +func (*HealthCheckResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e55f18a46a9fb2dc, []int{2} +} + +func (m *HealthCheckResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_HealthCheckResponse.Unmarshal(m, b) +} + +func (m *HealthCheckResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_HealthCheckResponse.Marshal(b, m, deterministic) +} + +func (m *HealthCheckResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_HealthCheckResponse.Merge(m, src) +} + +func (m *HealthCheckResponse) XXX_Size() int { + return xxx_messageInfo_HealthCheckResponse.Size(m) +} + +func (m *HealthCheckResponse) XXX_DiscardUnknown() { + xxx_messageInfo_HealthCheckResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_HealthCheckResponse proto.InternalMessageInfo + +func (m *HealthCheckResponse) GetMessages() []*HealthCheck { + if m != nil { + return m.Messages + } + return nil +} + +type ReadyCheck struct { + Status ReadyCheck_ReadyStatus `protobuf:"varint,1,opt,name=status,proto3,enum=health.ReadyCheck_ReadyStatus" json:"status,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ReadyCheck) Reset() { *m = ReadyCheck{} } +func (m *ReadyCheck) String() string { return proto.CompactTextString(m) } +func (*ReadyCheck) ProtoMessage() {} +func (*ReadyCheck) Descriptor() ([]byte, []int) { + return fileDescriptor_e55f18a46a9fb2dc, []int{3} +} + +func (m *ReadyCheck) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ReadyCheck.Unmarshal(m, b) +} + +func (m *ReadyCheck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ReadyCheck.Marshal(b, m, deterministic) +} + +func (m *ReadyCheck) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReadyCheck.Merge(m, src) +} + +func (m *ReadyCheck) XXX_Size() int { + return xxx_messageInfo_ReadyCheck.Size(m) +} + +func (m *ReadyCheck) XXX_DiscardUnknown() { + xxx_messageInfo_ReadyCheck.DiscardUnknown(m) +} + +var xxx_messageInfo_ReadyCheck proto.InternalMessageInfo + +func (m *ReadyCheck) GetStatus() ReadyCheck_ReadyStatus { + if m != nil { + return m.Status + } + return ReadyCheck_UNKNOWN +} + +type ReadyCheckResponse struct { + Messages []*ReadyCheck `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ReadyCheckResponse) Reset() { *m = ReadyCheckResponse{} } +func (m *ReadyCheckResponse) String() string { return proto.CompactTextString(m) } +func (*ReadyCheckResponse) ProtoMessage() {} +func (*ReadyCheckResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e55f18a46a9fb2dc, []int{4} +} + +func (m *ReadyCheckResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ReadyCheckResponse.Unmarshal(m, b) +} + +func (m *ReadyCheckResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ReadyCheckResponse.Marshal(b, m, deterministic) +} + +func (m *ReadyCheckResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReadyCheckResponse.Merge(m, src) +} + +func (m *ReadyCheckResponse) XXX_Size() int { + return xxx_messageInfo_ReadyCheckResponse.Size(m) +} + +func (m *ReadyCheckResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ReadyCheckResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ReadyCheckResponse proto.InternalMessageInfo + +func (m *ReadyCheckResponse) GetMessages() []*ReadyCheck { + if m != nil { + return m.Messages + } + return nil +} + +func init() { + proto.RegisterEnum("health.HealthCheck_ServingStatus", HealthCheck_ServingStatus_name, HealthCheck_ServingStatus_value) + proto.RegisterEnum("health.ReadyCheck_ReadyStatus", ReadyCheck_ReadyStatus_name, ReadyCheck_ReadyStatus_value) + proto.RegisterType((*HealthWatchRequest)(nil), "health.HealthWatchRequest") + proto.RegisterType((*HealthCheck)(nil), "health.HealthCheck") + proto.RegisterType((*HealthCheckResponse)(nil), "health.HealthCheckResponse") + proto.RegisterType((*ReadyCheck)(nil), "health.ReadyCheck") + proto.RegisterType((*ReadyCheckResponse)(nil), "health.ReadyCheckResponse") +} + +func init() { proto.RegisterFile("health/health.proto", fileDescriptor_e55f18a46a9fb2dc) } + +var fileDescriptor_e55f18a46a9fb2dc = []byte{ + // 401 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x92, 0xdb, 0xab, 0xd3, 0x40, + 0x10, 0xc6, 0xcd, 0x39, 0x24, 0x7a, 0x26, 0xd4, 0x86, 0x2d, 0x88, 0xa4, 0x20, 0x9a, 0xa7, 0x16, + 0x71, 0x23, 0x55, 0x04, 0x2f, 0x20, 0xd6, 0xd6, 0x0b, 0x42, 0x0a, 0x1b, 0xb5, 0xe8, 0x4b, 0xd9, + 0xa6, 0x6b, 0x12, 0x4c, 0xb2, 0xb1, 0xbb, 0x29, 0xd4, 0x67, 0xff, 0x37, 0xff, 0x2d, 0x49, 0x36, + 0x69, 0xd3, 0x8b, 0xe7, 0x29, 0x99, 0xc9, 0x37, 0x33, 0xdf, 0x2f, 0x7c, 0xd0, 0x8b, 0x18, 0x4d, + 0x64, 0xe4, 0xaa, 0x07, 0xce, 0xd7, 0x5c, 0x72, 0x64, 0xa8, 0xca, 0xee, 0x87, 0x9c, 0x87, 0x09, + 0x73, 0xab, 0xee, 0xb2, 0xf8, 0xe1, 0xb2, 0x34, 0x97, 0x5b, 0x25, 0x72, 0x5e, 0x03, 0xfa, 0x50, + 0xc9, 0xe6, 0x54, 0x06, 0x11, 0x61, 0xbf, 0x0a, 0x26, 0x24, 0x1a, 0x82, 0x15, 0x67, 0x92, 0xad, + 0x37, 0x34, 0x59, 0x08, 0x16, 0xf0, 0x6c, 0x25, 0xee, 0x6a, 0xf7, 0xb5, 0xc1, 0x25, 0xe9, 0x36, + 0x7d, 0x5f, 0xb5, 0x9d, 0x3f, 0x1a, 0x98, 0x6a, 0xc3, 0xdb, 0x88, 0x05, 0x3f, 0xd1, 0x73, 0x30, + 0x84, 0xa4, 0xb2, 0x50, 0x03, 0xb7, 0x47, 0x0f, 0x70, 0x6d, 0xaa, 0x25, 0xc2, 0x3e, 0x5b, 0x6f, + 0xe2, 0x2c, 0xf4, 0x2b, 0x21, 0xa9, 0x07, 0x9c, 0x17, 0xd0, 0x39, 0xf8, 0x80, 0x4c, 0xb8, 0xf9, + 0xc5, 0xfb, 0xe4, 0xcd, 0xe6, 0x9e, 0x75, 0xa3, 0x2c, 0xfc, 0x29, 0xf9, 0xfa, 0xd1, 0x7b, 0x6f, + 0x69, 0xa8, 0x0b, 0xa6, 0x37, 0xfb, 0xbc, 0x68, 0x1a, 0x17, 0xce, 0x3b, 0xe8, 0xb5, 0x0e, 0x10, + 0x26, 0x72, 0x9e, 0x09, 0x86, 0x5c, 0xb8, 0x95, 0x32, 0x21, 0x68, 0xc8, 0x4a, 0x3f, 0x97, 0x03, + 0x73, 0xd4, 0x3b, 0xe3, 0x87, 0xec, 0x44, 0xce, 0x6f, 0x00, 0xc2, 0xe8, 0x6a, 0xab, 0x60, 0x9e, + 0x1d, 0xc1, 0xdc, 0x6b, 0x86, 0xf7, 0x1a, 0xf5, 0x7a, 0x44, 0xf2, 0x14, 0xcc, 0x56, 0xfb, 0x90, + 0xe3, 0x0a, 0x74, 0x32, 0x7d, 0x33, 0xf9, 0x66, 0x69, 0xa8, 0x03, 0x57, 0x25, 0x85, 0x2a, 0x2f, + 0x9c, 0x09, 0xa0, 0xfd, 0xde, 0x1d, 0x02, 0x3e, 0x41, 0x40, 0xa7, 0x2e, 0xf6, 0x04, 0xa3, 0xbf, + 0x1a, 0x18, 0x8a, 0x0d, 0xbd, 0x02, 0x5d, 0x71, 0xdc, 0xc1, 0x2a, 0x03, 0xb8, 0xc9, 0x00, 0x9e, + 0x96, 0x19, 0xb0, 0xfb, 0xe7, 0x7e, 0x46, 0x73, 0x78, 0x0c, 0x7a, 0x15, 0x0a, 0x64, 0x1f, 0xaa, + 0xda, 0x49, 0xb9, 0x76, 0xc3, 0x63, 0x0d, 0xbd, 0x04, 0xbd, 0x32, 0xf9, 0x5f, 0x07, 0xf6, 0x19, + 0x96, 0x7a, 0x7c, 0xfc, 0xf0, 0xfb, 0x30, 0x8c, 0x65, 0x54, 0x2c, 0x71, 0xc0, 0x53, 0x57, 0xd2, + 0x84, 0x8b, 0x47, 0x62, 0x2b, 0x24, 0x4b, 0x85, 0xaa, 0x5c, 0x9a, 0xc7, 0x75, 0xe6, 0x97, 0x46, + 0xb5, 0xf8, 0xc9, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xad, 0xa0, 0x2a, 0x23, 0x0b, 0x03, 0x00, + 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ context.Context + _ grpc.ClientConn +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// HealthClient is the client API for Health service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type HealthClient interface { + Check(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*HealthCheckResponse, error) + Watch(ctx context.Context, in *HealthWatchRequest, opts ...grpc.CallOption) (Health_WatchClient, error) + Ready(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*ReadyCheckResponse, error) +} + +type healthClient struct { + cc *grpc.ClientConn +} + +func NewHealthClient(cc *grpc.ClientConn) HealthClient { + return &healthClient{cc} +} + +func (c *healthClient) Check(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*HealthCheckResponse, error) { + out := new(HealthCheckResponse) + err := c.cc.Invoke(ctx, "/health.Health/Check", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *healthClient) Watch(ctx context.Context, in *HealthWatchRequest, opts ...grpc.CallOption) (Health_WatchClient, error) { + stream, err := c.cc.NewStream(ctx, &_Health_serviceDesc.Streams[0], "/health.Health/Watch", opts...) + if err != nil { + return nil, err + } + x := &healthWatchClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type Health_WatchClient interface { + Recv() (*HealthCheckResponse, error) + grpc.ClientStream +} + +type healthWatchClient struct { + grpc.ClientStream +} + +func (x *healthWatchClient) Recv() (*HealthCheckResponse, error) { + m := new(HealthCheckResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *healthClient) Ready(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*ReadyCheckResponse, error) { + out := new(ReadyCheckResponse) + err := c.cc.Invoke(ctx, "/health.Health/Ready", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// HealthServer is the server API for Health service. +type HealthServer interface { + Check(context.Context, *empty.Empty) (*HealthCheckResponse, error) + Watch(*HealthWatchRequest, Health_WatchServer) error + Ready(context.Context, *empty.Empty) (*ReadyCheckResponse, error) +} + +func RegisterHealthServer(s *grpc.Server, srv HealthServer) { + s.RegisterService(&_Health_serviceDesc, srv) +} + +func _Health_Check_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(empty.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HealthServer).Check(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/health.Health/Check", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HealthServer).Check(ctx, req.(*empty.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Health_Watch_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(HealthWatchRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(HealthServer).Watch(m, &healthWatchServer{stream}) +} + +type Health_WatchServer interface { + Send(*HealthCheckResponse) error + grpc.ServerStream +} + +type healthWatchServer struct { + grpc.ServerStream +} + +func (x *healthWatchServer) Send(m *HealthCheckResponse) error { + return x.ServerStream.SendMsg(m) +} + +func _Health_Ready_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(empty.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HealthServer).Ready(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/health.Health/Ready", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HealthServer).Ready(ctx, req.(*empty.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +var _Health_serviceDesc = grpc.ServiceDesc{ + ServiceName: "health.Health", + HandlerType: (*HealthServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Check", + Handler: _Health_Check_Handler, + }, + { + MethodName: "Ready", + Handler: _Health_Ready_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "Watch", + Handler: _Health_Watch_Handler, + ServerStreams: true, + }, + }, + Metadata: "health/health.proto", +} diff --git a/api/health/health.proto b/api/health/health.proto new file mode 100644 index 000000000..b7a11e2f5 --- /dev/null +++ b/api/health/health.proto @@ -0,0 +1,43 @@ +syntax = "proto3"; + +package health; + +option go_package = "github.com/talos-systems/talos/api/health"; + +import "google/protobuf/empty.proto"; + +service Health { + rpc Check(google.protobuf.Empty) returns (HealthCheckResponse); + rpc Watch(HealthWatchRequest) returns (stream HealthCheckResponse); + rpc Ready(google.protobuf.Empty) returns (ReadyCheckResponse); +} + +message HealthWatchRequest { + int64 interval_seconds = 1; +} + +message HealthCheck { + enum ServingStatus { + UNKNOWN = 0; + SERVING = 1; + NOT_SERVING = 2; + } + ServingStatus status = 1; +} + +message HealthCheckResponse { + repeated HealthCheck messages = 1; +} + +message ReadyCheck { + enum ReadyStatus { + UNKNOWN = 0; + READY = 1; + NOT_READY = 2; + } + ReadyStatus status = 1; +} + +message ReadyCheckResponse { + repeated ReadyCheck messages = 1; +} diff --git a/internal/app/machined/pkg/system/services/networkd.go b/internal/app/machined/pkg/system/services/networkd.go index 9f74602ed..915b86d7a 100644 --- a/internal/app/machined/pkg/system/services/networkd.go +++ b/internal/app/machined/pkg/system/services/networkd.go @@ -7,6 +7,7 @@ package services import ( "context" + "errors" "fmt" "os" "path/filepath" @@ -14,10 +15,12 @@ import ( containerdapi "github.com/containerd/containerd" "github.com/containerd/containerd/oci" + "github.com/golang/protobuf/ptypes/empty" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/syndtr/gocapability/capability" "google.golang.org/grpc" + healthapi "github.com/talos-systems/talos/api/health" "github.com/talos-systems/talos/internal/app/machined/pkg/system/health" "github.com/talos-systems/talos/internal/app/machined/pkg/system/runner" "github.com/talos-systems/talos/internal/app/machined/pkg/system/runner/containerd" @@ -120,11 +123,36 @@ func (n *Networkd) Runner(config runtime.Configurator) (runner.Runner, error) { // HealthFunc implements the HealthcheckedService interface func (n *Networkd) HealthFunc(runtime.Configurator) health.Check { return func(ctx context.Context) error { - conn, err := grpc.Dial("unix:"+constants.NetworkSocketPath, grpc.WithInsecure()) - if err != nil { + var ( + conn *grpc.ClientConn + err error + hcResp *healthapi.HealthCheckResponse + readyResp *healthapi.ReadyCheckResponse + ) + + if conn, err = grpc.Dial("unix:"+constants.NetworkSocketPath, grpc.WithInsecure()); err != nil { return err } - return conn.Close() + defer conn.Close() //nolint: errcheck + + nClient := healthapi.NewHealthClient(conn) + if readyResp, err = nClient.Ready(ctx, &empty.Empty{}); err != nil { + return err + } + + if readyResp.Messages[0].Status != healthapi.ReadyCheck_READY { + return errors.New("networkd is not ready") + } + + if hcResp, err = nClient.Check(ctx, &empty.Empty{}); err != nil { + return err + } + + if hcResp.Messages[0].Status == healthapi.HealthCheck_SERVING { + return nil + } + + return errors.New("networkd is unhealthy") } } diff --git a/internal/app/networkd/pkg/networkd/networkd.go b/internal/app/networkd/pkg/networkd/networkd.go index da3f4fd2f..6509dba08 100644 --- a/internal/app/networkd/pkg/networkd/networkd.go +++ b/internal/app/networkd/pkg/networkd/networkd.go @@ -41,6 +41,9 @@ type Networkd struct { hostname string resolvers []string + + sync.Mutex + ready bool } // New takes the supplied configuration and creates an abstract representation @@ -258,6 +261,8 @@ func (n *Networkd) Configure() (err error) { return err } + n.SetReady() + return nil } @@ -389,3 +394,19 @@ func (n *Networkd) decideHostname() (hostname string, domainname string, address // Only return the hostname portion of the name ( strip domain bits off ) return hostname, domainname, address, nil } + +// Ready exposes the readiness state of networkd. +func (n *Networkd) Ready() bool { + n.Lock() + defer n.Unlock() + + return n.ready +} + +// SetReady sets the readiness state of networkd. +func (n *Networkd) SetReady() { + n.Lock() + defer n.Unlock() + + n.ready = true +} diff --git a/internal/app/networkd/pkg/reg/reg.go b/internal/app/networkd/pkg/reg/reg.go index 2be4b3698..874f13f3b 100644 --- a/internal/app/networkd/pkg/reg/reg.go +++ b/internal/app/networkd/pkg/reg/reg.go @@ -6,15 +6,18 @@ package reg import ( "context" + "errors" "fmt" "log" "net" + "time" "github.com/golang/protobuf/ptypes/empty" "github.com/jsimonetti/rtnetlink" "golang.org/x/sys/unix" "google.golang.org/grpc" + healthapi "github.com/talos-systems/talos/api/health" networkapi "github.com/talos-systems/talos/api/network" "github.com/talos-systems/talos/internal/app/networkd/pkg/networkd" ) @@ -42,6 +45,7 @@ func NewRegistrator(n *networkd.Networkd) *Registrator { // Register implements the factory.Registrator interface. func (r *Registrator) Register(s *grpc.Server) { networkapi.RegisterNetworkServiceServer(s, r) + healthapi.RegisterHealthServer(s, r) } // Routes returns the hosts routing table. @@ -149,3 +153,67 @@ func toCIDR(family uint8, prefix net.IP, prefixLen int) string { return ipNet.String() } + +// Check implements the Health api and provides visibilty into the state of networkd. +// Ready signifies the daemon (api) is healthy and ready to serve requests. +func (r *Registrator) Check(ctx context.Context, in *empty.Empty) (reply *healthapi.HealthCheckResponse, err error) { + reply = &healthapi.HealthCheckResponse{ + Messages: []*healthapi.HealthCheck{ + { + Status: healthapi.HealthCheck_SERVING, + }, + }, + } + + return reply, nil +} + +// Watch implements the Health api and provides visibilty into the state of networkd. +// Ready signifies the daemon (api) is healthy and ready to serve requests. +func (r *Registrator) Watch(in *healthapi.HealthWatchRequest, srv healthapi.Health_WatchServer) (err error) { + if in == nil { + return errors.New("an input interval is required") + } + + var ( + resp *healthapi.HealthCheckResponse + ticker = time.NewTicker(time.Duration(in.IntervalSeconds) * time.Second) + ) + + for range ticker.C { + // select { + // case <-srv.Context().Done(): + // return nil + // case <-ticker.C: + resp, err = r.Check(srv.Context(), &empty.Empty{}) + if err != nil { + return err + } + + if err = srv.Send(resp); err != nil { + return err + } + } + + return nil +} + +// Ready implements the Health api and provides visibility to the state of networkd. +// Ready signifies the initial network configuration ( interfaces, routes, hostname, resolv.conf ) +// settings have been applied. +// Not Ready signifies that the initial network configuration still needs to happen. +func (r *Registrator) Ready(ctx context.Context, in *empty.Empty) (reply *healthapi.ReadyCheckResponse, err error) { + rdy := &healthapi.ReadyCheck{Status: healthapi.ReadyCheck_NOT_READY} + + if r.Networkd.Ready() { + rdy.Status = healthapi.ReadyCheck_READY + } + + reply = &healthapi.ReadyCheckResponse{ + Messages: []*healthapi.ReadyCheck{ + rdy, + }, + } + + return reply, nil +} diff --git a/internal/app/networkd/pkg/reg/reg_test.go b/internal/app/networkd/pkg/reg/reg_test.go index bc6f77d2d..d8497ee56 100644 --- a/internal/app/networkd/pkg/reg/reg_test.go +++ b/internal/app/networkd/pkg/reg/reg_test.go @@ -17,6 +17,7 @@ import ( "golang.org/x/sys/unix" "google.golang.org/grpc" + healthapi "github.com/talos-systems/talos/api/health" networkapi "github.com/talos-systems/talos/api/network" "github.com/talos-systems/talos/internal/app/networkd/pkg/networkd" "github.com/talos-systems/talos/pkg/grpc/factory" @@ -27,14 +28,12 @@ type NetworkdSuite struct { } func TestNetworkdSuite(t *testing.T) { - // Hide all our state transition messages - // log.SetOutput(ioutil.Discard) suite.Run(t, new(NetworkdSuite)) } // nolint: dupl func (suite *NetworkdSuite) TestRoutes() { - server, listener := suite.fakeNetworkdRPC() + _, server, listener := suite.fakeNetworkdRPC() // nolint: errcheck defer os.Remove(listener.Addr().String()) @@ -55,7 +54,7 @@ func (suite *NetworkdSuite) TestRoutes() { // nolint: dupl func (suite *NetworkdSuite) TestInterfaces() { - server, listener := suite.fakeNetworkdRPC() + _, server, listener := suite.fakeNetworkdRPC() // nolint: errcheck defer os.Remove(listener.Addr().String()) @@ -74,7 +73,7 @@ func (suite *NetworkdSuite) TestInterfaces() { suite.Assert().Greater(len(resp.Messages[0].Interfaces), 0) } -func (suite *NetworkdSuite) fakeNetworkdRPC() (*grpc.Server, net.Listener) { +func (suite *NetworkdSuite) fakeNetworkdRPC() (*networkd.Networkd, *grpc.Server, net.Listener) { // Create networkd instance n, err := networkd.New(nil) suite.Assert().NoError(err) @@ -91,7 +90,7 @@ func (suite *NetworkdSuite) fakeNetworkdRPC() (*grpc.Server, net.Listener) { ) suite.Assert().NoError(err) - return server, listener + return n, server, listener } func (suite *NetworkdSuite) TestToCIDR() { @@ -100,3 +99,51 @@ func (suite *NetworkdSuite) TestToCIDR() { suite.Assert().Equal(toCIDR(unix.AF_INET, nil, 0), "0.0.0.0/0") suite.Assert().Equal(toCIDR(unix.AF_INET6, nil, 0), "::/0") } + +func (suite *NetworkdSuite) TestHealthAPI() { + nwd, server, listener := suite.fakeNetworkdRPC() + + // nolint: errcheck + defer os.Remove(listener.Addr().String()) + + defer server.Stop() + + // nolint: errcheck + go server.Serve(listener) + + conn, err := grpc.Dial(fmt.Sprintf("%s://%s", "unix", listener.Addr().String()), grpc.WithInsecure()) + suite.Assert().NoError(err) + + // Verify base state + nClient := healthapi.NewHealthClient(conn) + hcResp, err := nClient.Check(context.Background(), &empty.Empty{}) + suite.Assert().NoError(err) + suite.Assert().Equal(healthapi.HealthCheck_SERVING, hcResp.Messages[0].Status) + + rResp, err := nClient.Ready(context.Background(), &empty.Empty{}) + suite.Assert().NoError(err) + suite.Assert().Equal(healthapi.ReadyCheck_NOT_READY, rResp.Messages[0].Status) + + // Trigger ready condition + nwd.SetReady() + suite.Assert().NoError(err) + rResp, err = nClient.Ready(context.Background(), &empty.Empty{}) + suite.Assert().NoError(err) + suite.Assert().Equal(healthapi.ReadyCheck_READY, rResp.Messages[0].Status) + + // Test watch + ctx, cancel := context.WithCancel(context.Background()) + stream, err := nClient.Watch(ctx, &healthapi.HealthWatchRequest{IntervalSeconds: 1}) + suite.Require().NoError(err) + + for i := 0; i < 2; i++ { + hcResp, err = stream.Recv() + suite.Assert().NoError(err) + suite.Assert().Equal(healthapi.HealthCheck_SERVING, hcResp.Messages[0].Status) + } + + cancel() + + _, err = stream.Recv() + suite.Assert().Error(err) +}