Skip to content
Snippets Groups Projects
Commit c02df12d authored by Malte Bauch's avatar Malte Bauch
Browse files

Merge branch '53-add-grpc-health-check-for-gosdn-tview' into 'v.0.1.0-codename-threadbare'

Resolve "Add gRPC Health Check for goSDN-tview"

See merge request cocsn/gosdn!72
parents 7ae17cc4 8fd676b2
No related branches found
No related tags found
3 merge requests!90Develop,!72Resolve "Add gRPC Health Check for goSDN-tview",!53V.0.1.0 Codename Threadbare
Pipeline #54477 passed
...@@ -11,7 +11,7 @@ documentation:pdf: ...@@ -11,7 +11,7 @@ documentation:pdf:
- documentation/design/*.md - documentation/design/*.md
script: script:
- cd documentation/design - cd documentation/design
- pandoc --filter pandoc-citeproc --bibliography=bibliography.bib --csl=acm-sig-proceedings.csl - pandoc --citeproc --bibliography=bibliography.bib --csl=acm-sig-proceedings.csl
--variable papersize=a4paper -s *.md -o documentation.pdf --variable papersize=a4paper -s *.md -o documentation.pdf
artifacts: artifacts:
paths: paths:
......
package app package app
//TODO: App should be a Singleton i guess
import ( import (
"github.com/rivo/tview" "github.com/rivo/tview"
) )
...@@ -56,6 +57,11 @@ func (a *App) Draw() { ...@@ -56,6 +57,11 @@ func (a *App) Draw() {
a.app.Draw() a.app.Draw()
} }
//QueueUpdateDraw calls tview.QueueUpdateDraw()
func (a *App) QueueUpdateDraw(f func()) {
a.app.QueueUpdateDraw(f)
}
//SetFocus sets the focus on new tview.Primitive //SetFocus sets the focus on new tview.Primitive
func (a *App) SetFocus(v tview.Primitive) { func (a *App) SetFocus(v tview.Primitive) {
a.app.SetFocus(v) a.app.SetFocus(v)
......
package commands package commands
import ( import (
pb "code.fbi.h-da.de/cocsn/gosdn/api/proto"
"code.fbi.h-da.de/cocsn/gosdn/log"
"context" "context"
"time"
pb "code.fbi.h-da.de/cocsn/gosdn/api/proto"
"code.fbi.h-da.de/cocsn/gosdn/cmd/gosdn-tview/app"
"github.com/rivo/tview" "github.com/rivo/tview"
grpc "google.golang.org/grpc" grpc "google.golang.org/grpc"
//Package google.golang.org/grpc/health is needed to make gRPC Health Check work
_ "google.golang.org/grpc/health"
healthpb "google.golang.org/grpc/health/grpc_health_v1"
"google.golang.org/protobuf/types/known/emptypb" "google.golang.org/protobuf/types/known/emptypb"
"time"
) )
const ( const (
...@@ -30,18 +34,32 @@ var CommandList = []command{ ...@@ -30,18 +34,32 @@ var CommandList = []command{
{"shutdown", "request controller to shutdown", goSDNShutdown}, {"shutdown", "request controller to shutdown", goSDNShutdown},
} }
var serviceConfig = `{
"loadBalancingPolicy": "round_robin",
"healthCheckConfig": {
"serviceName": ""
}
}`
//Connect creates a new connection to the gRPC server //Connect creates a new connection to the gRPC server
func Connect(address string) (*grpc.ClientConn, error) { func Connect(address string) (*grpc.ClientConn, error) {
return grpc.Dial(address, grpc.WithInsecure(), grpc.WithTimeout(5*time.Second), grpc.WithBlock()) options := []grpc.DialOption{
grpc.WithInsecure(),
grpc.WithBlock(),
grpc.WithDefaultServiceConfig(serviceConfig),
grpc.WithTimeout(5 * time.Second),
}
return grpc.Dial(address, options...)
} }
//GoSDNLogStream creates a continuous gRPC stream to recieve goSDN logs //GoSDNLogStream creates a continuous gRPC stream to recieve goSDN logs
func GoSDNLogStream(conn *grpc.ClientConn, clv *tview.TextView) error { func GoSDNLogStream(app *app.App, conn *grpc.ClientConn, tv *tview.TextView) error {
var streamError error var streamError error
c := pb.NewGrpcCliClient(conn) c := pb.NewGrpcCliClient(conn)
stream, err := c.CreateLogStream(context.Background(), &emptypb.Empty{}) stream, err := c.CreateLogStream(context.Background(), &emptypb.Empty{})
if err != nil { if err != nil {
log.Error(err) return err
} }
go func(stream pb.GrpcCli_CreateLogStreamClient) { go func(stream pb.GrpcCli_CreateLogStreamClient) {
...@@ -49,10 +67,23 @@ func GoSDNLogStream(conn *grpc.ClientConn, clv *tview.TextView) error { ...@@ -49,10 +67,23 @@ func GoSDNLogStream(conn *grpc.ClientConn, clv *tview.TextView) error {
msg, err := stream.Recv() msg, err := stream.Recv()
if err != nil { if err != nil {
streamError = err streamError = err
go func() {
ticker := time.NewTicker(5 * time.Second)
for range ticker.C {
if err := GoSDNLogStream(app, conn, tv); err == nil {
ticker.Stop()
return
}
}
}()
break break
} }
response := []byte(msg.Log) response := []byte(msg.Log)
clv.Write(response) app.QueueUpdateDraw(func() {
tv.Write(response)
})
} }
}(stream) }(stream)
...@@ -68,11 +99,55 @@ func goSDNSayHello(conn *grpc.ClientConn) string { ...@@ -68,11 +99,55 @@ func goSDNSayHello(conn *grpc.ClientConn) string {
defer cancel() defer cancel()
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name}) r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
if err != nil { if err != nil {
log.Fatal(err) return err.Error()
} }
return r.GetMessage() return r.GetMessage()
} }
//WatchHealth continuous gRPC Health Check stream to recieve health changes
func WatchHealth(service string, app *app.App, conn *grpc.ClientConn, tv *tview.TextView) error {
var streamError error
c := healthpb.NewHealthClient(conn)
stream, err := c.Watch(context.Background(), &healthpb.HealthCheckRequest{Service: service})
if err != nil {
app.QueueUpdateDraw(func() {
tv.Clear()
tv.SetText(err.Error())
})
return err
}
go func(stream healthpb.Health_WatchClient) {
for {
msg, err := stream.Recv()
if err != nil {
streamError = err
go func() {
ticker := time.NewTicker(5 * time.Second)
for range ticker.C {
if err := WatchHealth(service, app, conn, tv); err == nil {
ticker.Stop()
return
}
}
}()
break
}
app.QueueUpdateDraw(func() {
tv.Clear()
tv.SetText(msg.GetStatus().String())
})
}
}(stream)
return streamError
}
func goSDNShutdown(conn *grpc.ClientConn) string { func goSDNShutdown(conn *grpc.ClientConn) string {
c := pb.NewGrpcCliClient(conn) c := pb.NewGrpcCliClient(conn)
...@@ -83,7 +158,7 @@ func goSDNShutdown(conn *grpc.ClientConn) string { ...@@ -83,7 +158,7 @@ func goSDNShutdown(conn *grpc.ClientConn) string {
defer cancel() defer cancel()
r, err := c.Shutdown(ctx, &pb.ShutdownRequest{Name: name}) r, err := c.Shutdown(ctx, &pb.ShutdownRequest{Name: name})
if err != nil { if err != nil {
log.Fatal(err) return err.Error()
} }
return r.GetMessage() return r.GetMessage()
} }
...@@ -105,7 +180,7 @@ func TAPIGetEdge(conn *grpc.ClientConn) string { ...@@ -105,7 +180,7 @@ func TAPIGetEdge(conn *grpc.ClientConn) string {
defer cancel() defer cancel()
r, err := c.TAPIGetEdge(ctx, &pb.TAPIRequest{Name: name}) r, err := c.TAPIGetEdge(ctx, &pb.TAPIRequest{Name: name})
if err != nil { if err != nil {
log.Fatal(err) return err.Error()
} }
return r.GetMessage() return r.GetMessage()
} }
...@@ -121,7 +196,7 @@ func TAPIGetEdgeNode(conn *grpc.ClientConn) string { ...@@ -121,7 +196,7 @@ func TAPIGetEdgeNode(conn *grpc.ClientConn) string {
defer cancel() defer cancel()
r, err := c.TAPIGetEdgeNode(ctx, &pb.TAPIRequest{Name: name}) r, err := c.TAPIGetEdgeNode(ctx, &pb.TAPIRequest{Name: name})
if err != nil { if err != nil {
log.Fatal(err) return err.Error()
} }
return r.GetMessage() return r.GetMessage()
} }
...@@ -138,7 +213,7 @@ func TAPIGetLink(conn *grpc.ClientConn) string { ...@@ -138,7 +213,7 @@ func TAPIGetLink(conn *grpc.ClientConn) string {
defer cancel() defer cancel()
r, err := c.TAPIGetLink(ctx, &pb.TAPIRequest{Name: name}) r, err := c.TAPIGetLink(ctx, &pb.TAPIRequest{Name: name})
if err != nil { if err != nil {
log.Fatal(err) return err.Error()
} }
return r.GetMessage() return r.GetMessage()
} }
...@@ -18,7 +18,6 @@ func main() { ...@@ -18,7 +18,6 @@ func main() {
addr := strings.Join([]string{*addrIPv4, strconv.Itoa(*port)}, ":") addr := strings.Join([]string{*addrIPv4, strconv.Itoa(*port)}, ":")
conn, err := grpc.Connect(addr) conn, err := grpc.Connect(addr)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
......
package views package views
import ( import (
"code.fbi.h-da.de/cocsn/gosdn/cmd/gosdn-tview/app"
commands "code.fbi.h-da.de/cocsn/gosdn/cmd/gosdn-tview/grpc" commands "code.fbi.h-da.de/cocsn/gosdn/cmd/gosdn-tview/grpc"
"github.com/rivo/tview" "github.com/rivo/tview"
"google.golang.org/grpc" "google.golang.org/grpc"
...@@ -13,7 +14,7 @@ type ConsoleLogView struct { ...@@ -13,7 +14,7 @@ type ConsoleLogView struct {
} }
//NewConsoleLogView creates a new ConsoleLogView //NewConsoleLogView creates a new ConsoleLogView
func NewConsoleLogView(title string, conn *grpc.ClientConn) *ConsoleLogView { func NewConsoleLogView(title string, app *app.App, conn *grpc.ClientConn) *ConsoleLogView {
clv := &ConsoleLogView{ clv := &ConsoleLogView{
consoleLogView: tview.NewTextView(), consoleLogView: tview.NewTextView(),
title: title, title: title,
...@@ -26,7 +27,7 @@ func NewConsoleLogView(title string, conn *grpc.ClientConn) *ConsoleLogView { ...@@ -26,7 +27,7 @@ func NewConsoleLogView(title string, conn *grpc.ClientConn) *ConsoleLogView {
SetBorder(true). SetBorder(true).
SetTitle(clv.title) SetTitle(clv.title)
commands.GoSDNLogStream(conn, clv.consoleLogView) commands.GoSDNLogStream(app, conn, clv.consoleLogView)
return clv return clv
} }
......
...@@ -2,9 +2,9 @@ package views ...@@ -2,9 +2,9 @@ package views
import ( import (
"code.fbi.h-da.de/cocsn/gosdn/cmd/gosdn-tview/app" "code.fbi.h-da.de/cocsn/gosdn/cmd/gosdn-tview/app"
commands "code.fbi.h-da.de/cocsn/gosdn/cmd/gosdn-tview/grpc"
"github.com/rivo/tview" "github.com/rivo/tview"
"google.golang.org/grpc" "google.golang.org/grpc"
"time"
) )
//GRPCStatusView is an application view to display the current status of //GRPCStatusView is an application view to display the current status of
...@@ -26,13 +26,7 @@ func NewGRPCStatusView(app *app.App, conn *grpc.ClientConn) *GRPCStatusView { ...@@ -26,13 +26,7 @@ func NewGRPCStatusView(app *app.App, conn *grpc.ClientConn) *GRPCStatusView {
SetBorder(true). SetBorder(true).
SetTitle("gRPC") SetTitle("gRPC")
//TODO: gRPCs Health Check looks like a way better alternative here! commands.WatchHealth("", app, conn, sv.gRPCStatusView)
//--> no app.Draw() required anymore
sv.gRPCStatusView.SetChangedFunc(func() {
app.Draw()
})
go gRPCTicker(sv, conn)
return sv return sv
} }
...@@ -47,19 +41,3 @@ func (sv *GRPCStatusView) SetContent(s string) { ...@@ -47,19 +41,3 @@ func (sv *GRPCStatusView) SetContent(s string) {
sv.gRPCStatusView.Clear() sv.gRPCStatusView.Clear()
sv.gRPCStatusView.SetText(s) sv.gRPCStatusView.SetText(s)
} }
func gRPCTicker(sv *GRPCStatusView, conn *grpc.ClientConn) {
sv.SetContent(checkStatus(conn))
//TODO: refactor -> get rid of hardcoded values
ticker := time.NewTicker(5 * time.Second)
for range ticker.C {
sv.SetContent(checkStatus(conn))
}
}
func checkStatus(conn *grpc.ClientConn) string {
if str := conn.GetState().String(); str == "READY" || str == "IDLE" {
return "[green]" + "connected"
}
return "[red]" + "disconnected"
}
...@@ -24,7 +24,7 @@ func NewResultAndInputView(title string, app *app.App, commandListView tview.Pri ...@@ -24,7 +24,7 @@ func NewResultAndInputView(title string, app *app.App, commandListView tview.Pri
pages: tview.NewPages(), pages: tview.NewPages(),
pndInputView: NewAddPNDView("add PND", app), pndInputView: NewAddPNDView("add PND", app),
resultView: tview.NewTextView(), resultView: tview.NewTextView(),
consoleLogView: NewConsoleLogView("logs", conn), consoleLogView: NewConsoleLogView("logs", app, conn),
} }
rv.resultView. rv.resultView.
SetDynamicColors(true). SetDynamicColors(true).
......
# example config
CliSocket = "localhost:55055" CliSocket = "localhost:55055"
DatabaseSocket = "bolt://172.17.0.4:7687" DatabaseSocket = "bolt://172.17.0.4:7687"
DatabaseUser = "" DatabaseUser = ""
......
...@@ -17,6 +17,8 @@ import ( ...@@ -17,6 +17,8 @@ import (
"code.fbi.h-da.de/cocsn/gosdn/log" "code.fbi.h-da.de/cocsn/gosdn/log"
"code.fbi.h-da.de/cocsn/gosdn/sbi/restconf/client/ciena" "code.fbi.h-da.de/cocsn/gosdn/sbi/restconf/client/ciena"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/health"
healthpb "google.golang.org/grpc/health/grpc_health_v1"
"google.golang.org/protobuf/types/known/emptypb" "google.golang.org/protobuf/types/known/emptypb"
) )
...@@ -99,8 +101,11 @@ func (s *server) Shutdown(ctx context.Context, in *pb.ShutdownRequest) (*pb.Shut ...@@ -99,8 +101,11 @@ func (s *server) Shutdown(ctx context.Context, in *pb.ShutdownRequest) (*pb.Shut
func getCLIGoing(core *Core) { func getCLIGoing(core *Core) {
var logConnections []*logConnection var (
var logBuffer buf logConnections []*logConnection
logBuffer buf
system = ""
)
log.Info("Starting: GetCLIGoing") log.Info("Starting: GetCLIGoing")
// Boot-up the control interface for the cli // Boot-up the control interface for the cli
...@@ -110,14 +115,18 @@ func getCLIGoing(core *Core) { ...@@ -110,14 +115,18 @@ func getCLIGoing(core *Core) {
} }
cliControlServer := grpc.NewServer() cliControlServer := grpc.NewServer()
healthCheck := health.NewServer()
srv = &server{core: core, logConnections: logConnections} srv = &server{core: core, logConnections: logConnections}
//TODO: move? //TODO: move?
wrt := io.MultiWriter(os.Stdout, &logBuffer) wrt := io.MultiWriter(os.Stdout, &logBuffer)
log.Output(wrt) log.Output(wrt)
healthpb.RegisterHealthServer(cliControlServer, healthCheck)
pb.RegisterGrpcCliServer(cliControlServer, srv) pb.RegisterGrpcCliServer(cliControlServer, srv)
healthCheck.SetServingStatus(system, healthpb.HealthCheckResponse_SERVING)
if err := cliControlServer.Serve(cliControlListener); err != nil { if err := cliControlServer.Serve(cliControlListener); err != nil {
log.Fatal(err) log.Fatal(err)
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment