feat(networkd): Add health api

This introduces a health/ready api for networkd. This
will allow us to better determine the state of networkd
and allow for some level of monitoring.

Signed-off-by: Brad Beam <brad.beam@talos-systems.com>
This commit is contained in:
Brad Beam 2020-01-22 22:14:58 +00:00 committed by Brad Beam
parent fae5e6915d
commit 88df1b50b8
7 changed files with 727 additions and 9 deletions

View File

@ -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/

508
api/health/health.pb.go Normal file
View File

@ -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",
}

43
api/health/health.proto Normal file
View File

@ -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;
}

View File

@ -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")
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}