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:
- documentation/design/*.md
script:
- 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
artifacts:
paths:
......
package app
//TODO: App should be a Singleton i guess
import (
"github.com/rivo/tview"
)
......@@ -56,6 +57,11 @@ func (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
func (a *App) SetFocus(v tview.Primitive) {
a.app.SetFocus(v)
......
package commands
import (
pb "code.fbi.h-da.de/cocsn/gosdn/api/proto"
"code.fbi.h-da.de/cocsn/gosdn/log"
"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"
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"
"time"
)
const (
......@@ -30,18 +34,32 @@ var CommandList = []command{
{"shutdown", "request controller to shutdown", goSDNShutdown},
}
var serviceConfig = `{
"loadBalancingPolicy": "round_robin",
"healthCheckConfig": {
"serviceName": ""
}
}`
//Connect creates a new connection to the gRPC server
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
func GoSDNLogStream(conn *grpc.ClientConn, clv *tview.TextView) error {
func GoSDNLogStream(app *app.App, conn *grpc.ClientConn, tv *tview.TextView) error {
var streamError error
c := pb.NewGrpcCliClient(conn)
stream, err := c.CreateLogStream(context.Background(), &emptypb.Empty{})
if err != nil {
log.Error(err)
return err
}
go func(stream pb.GrpcCli_CreateLogStreamClient) {
......@@ -49,10 +67,23 @@ func GoSDNLogStream(conn *grpc.ClientConn, clv *tview.TextView) error {
msg, err := stream.Recv()
if err != nil {
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
}
response := []byte(msg.Log)
clv.Write(response)
app.QueueUpdateDraw(func() {
tv.Write(response)
})
}
}(stream)
......@@ -68,11 +99,55 @@ func goSDNSayHello(conn *grpc.ClientConn) string {
defer cancel()
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
if err != nil {
log.Fatal(err)
return err.Error()
}
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 {
c := pb.NewGrpcCliClient(conn)
......@@ -83,7 +158,7 @@ func goSDNShutdown(conn *grpc.ClientConn) string {
defer cancel()
r, err := c.Shutdown(ctx, &pb.ShutdownRequest{Name: name})
if err != nil {
log.Fatal(err)
return err.Error()
}
return r.GetMessage()
}
......@@ -105,7 +180,7 @@ func TAPIGetEdge(conn *grpc.ClientConn) string {
defer cancel()
r, err := c.TAPIGetEdge(ctx, &pb.TAPIRequest{Name: name})
if err != nil {
log.Fatal(err)
return err.Error()
}
return r.GetMessage()
}
......@@ -121,7 +196,7 @@ func TAPIGetEdgeNode(conn *grpc.ClientConn) string {
defer cancel()
r, err := c.TAPIGetEdgeNode(ctx, &pb.TAPIRequest{Name: name})
if err != nil {
log.Fatal(err)
return err.Error()
}
return r.GetMessage()
}
......@@ -138,7 +213,7 @@ func TAPIGetLink(conn *grpc.ClientConn) string {
defer cancel()
r, err := c.TAPIGetLink(ctx, &pb.TAPIRequest{Name: name})
if err != nil {
log.Fatal(err)
return err.Error()
}
return r.GetMessage()
}
......@@ -18,7 +18,6 @@ func main() {
addr := strings.Join([]string{*addrIPv4, strconv.Itoa(*port)}, ":")
conn, err := grpc.Connect(addr)
if err != nil {
log.Fatal(err)
}
......
package views
import (
"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"
"google.golang.org/grpc"
......@@ -13,7 +14,7 @@ type ConsoleLogView struct {
}
//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{
consoleLogView: tview.NewTextView(),
title: title,
......@@ -26,7 +27,7 @@ func NewConsoleLogView(title string, conn *grpc.ClientConn) *ConsoleLogView {
SetBorder(true).
SetTitle(clv.title)
commands.GoSDNLogStream(conn, clv.consoleLogView)
commands.GoSDNLogStream(app, conn, clv.consoleLogView)
return clv
}
......
......@@ -2,9 +2,9 @@ package views
import (
"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"
"google.golang.org/grpc"
"time"
)
//GRPCStatusView is an application view to display the current status of
......@@ -26,13 +26,7 @@ func NewGRPCStatusView(app *app.App, conn *grpc.ClientConn) *GRPCStatusView {
SetBorder(true).
SetTitle("gRPC")
//TODO: gRPCs Health Check looks like a way better alternative here!
//--> no app.Draw() required anymore
sv.gRPCStatusView.SetChangedFunc(func() {
app.Draw()
})
go gRPCTicker(sv, conn)
commands.WatchHealth("", app, conn, sv.gRPCStatusView)
return sv
}
......@@ -47,19 +41,3 @@ func (sv *GRPCStatusView) SetContent(s string) {
sv.gRPCStatusView.Clear()
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
pages: tview.NewPages(),
pndInputView: NewAddPNDView("add PND", app),
resultView: tview.NewTextView(),
consoleLogView: NewConsoleLogView("logs", conn),
consoleLogView: NewConsoleLogView("logs", app, conn),
}
rv.resultView.
SetDynamicColors(true).
......
# example config
CliSocket = "localhost:55055"
DatabaseSocket = "bolt://172.17.0.4:7687"
DatabaseUser = ""
......
......@@ -17,6 +17,8 @@ import (
"code.fbi.h-da.de/cocsn/gosdn/log"
"code.fbi.h-da.de/cocsn/gosdn/sbi/restconf/client/ciena"
"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"
)
......@@ -99,8 +101,11 @@ func (s *server) Shutdown(ctx context.Context, in *pb.ShutdownRequest) (*pb.Shut
func getCLIGoing(core *Core) {
var logConnections []*logConnection
var logBuffer buf
var (
logConnections []*logConnection
logBuffer buf
system = ""
)
log.Info("Starting: GetCLIGoing")
// Boot-up the control interface for the cli
......@@ -110,14 +115,18 @@ func getCLIGoing(core *Core) {
}
cliControlServer := grpc.NewServer()
healthCheck := health.NewServer()
srv = &server{core: core, logConnections: logConnections}
//TODO: move?
wrt := io.MultiWriter(os.Stdout, &logBuffer)
log.Output(wrt)
healthpb.RegisterHealthServer(cliControlServer, healthCheck)
pb.RegisterGrpcCliServer(cliControlServer, srv)
healthCheck.SetServingStatus(system, healthpb.HealthCheckResponse_SERVING)
if err := cliControlServer.Serve(cliControlListener); err != nil {
log.Fatal(err)
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment