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

Merge branch '28-simple-ncurse-alike-cli-to-manage-gosdn' into 'v.0.1.0-codename-threadbare'

Resolve "Simple ncurse-alike cli to manage gosdn"

See merge request cocsn/gosdn!59
parents a73fa7dc c4a6242b
Branches
Tags
3 merge requests!90Develop,!59Resolve "Simple ncurse-alike cli to manage gosdn",!53V.0.1.0 Codename Threadbare
Pipeline #53024 passed
This commit is part of merge request !53. Comments created here will be created in the context of that merge request.
package app
import "github.com/rivo/tview"
type view interface {
GetContent() tview.Primitive
}
type App struct {
app *tview.Application
pages *tview.Pages
}
func NewApp() *App {
a := &App{
app: tview.NewApplication(),
}
return a
}
func (a *App) SetRoot(v view) {
a.pages = v.GetContent().(*tview.Pages)
a.app.SetRoot(a.pages, true)
}
func (a *App) SwitchPage(s string) {
if a.pages.HasPage(s) {
a.pages.SwitchToPage(s)
}
}
func (a *App) AddPage(name string, p view) {
a.pages.AddPage(name, p.GetContent(), true, false)
}
func (a *App) Run() error {
return a.app.Run()
}
func (a *App) Stop() {
a.app.Stop()
}
func (a *App) Draw() {
a.app.Draw()
}
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"
grpc "google.golang.org/grpc"
"time"
)
type commandType int
const (
GoSDN commandType = iota
Database
)
const (
defaultName = "gosdn-cli"
)
type command struct {
Name string
Description string
//CommandType commandType
Function func(conn *grpc.ClientConn) string
}
var CommandList = []command{
{"hello", "test connection to goSDN controller", goSDNSayHello},
{"shutdown", "request goSDN controller to shutdown", goSDNShutdown},
{"testdb", "test all database connections", goSDNTestDB},
{"tapigetedge", "get list of edges", TAPIGetEdge},
{"tapigetedgenode", "get list of edgenodes", TAPIGetEdgeNode},
{"tapigetlink", "get list of links", TAPIGetLink},
}
func Connect() (*grpc.ClientConn, error) {
address := "localhost:55055"
return grpc.Dial(address, grpc.WithInsecure(), grpc.WithTimeout(5*time.Second), grpc.WithBlock())
}
func goSDNSayHello(conn *grpc.ClientConn) string {
c := pb.NewGrpcCliClient(conn)
// Contact the server and print out its response.
name := defaultName
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
if err != nil {
log.Fatal(err)
}
return r.GetMessage()
}
func goSDNShutdown(conn *grpc.ClientConn) string {
c := pb.NewGrpcCliClient(conn)
// Contact the server and print out its response.
name := defaultName
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.Shutdown(ctx, &pb.ShutdownRequest{Name: name})
if err != nil {
log.Fatal(err)
}
return r.GetMessage()
}
func goSDNTestDB(conn *grpc.ClientConn) string {
// TODO: fill with code and also see if grpc interface has this stub implemented.
return "not implemented yet"
}
// TAPIGetEdge triggers the GetEdge function of the Ciena
// flavoured TAPI client
func TAPIGetEdge(conn *grpc.ClientConn) string {
c := pb.NewGrpcCliClient(conn)
// Contact the server and print out its response.
name := defaultName
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.TAPIGetEdge(ctx, &pb.TAPIRequest{Name: name})
if err != nil {
log.Fatal(err)
}
return r.GetMessage()
}
// TAPIGetEdgeNode triggers the GetEdgeNode function of the Ciena
// flavoured TAPI client
func TAPIGetEdgeNode(conn *grpc.ClientConn) string {
c := pb.NewGrpcCliClient(conn)
// Contact the server and print out its response.
name := defaultName
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.TAPIGetEdgeNode(ctx, &pb.TAPIRequest{Name: name})
if err != nil {
log.Fatal(err)
}
return r.GetMessage()
}
// TAPIGetLink triggers the GetLink function of the Ciena
// flavoured TAPI client
func TAPIGetLink(conn *grpc.ClientConn) string {
c := pb.NewGrpcCliClient(conn)
// Contact the server and print out its response.
name := defaultName
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.TAPIGetLink(ctx, &pb.TAPIRequest{Name: name})
if err != nil {
log.Fatal(err)
}
return r.GetMessage()
}
package main
import (
"code.fbi.h-da.de/cocsn/gosdn/cmd/gosdn-tview/app"
grpc "code.fbi.h-da.de/cocsn/gosdn/cmd/gosdn-tview/grpc"
"code.fbi.h-da.de/cocsn/gosdn/cmd/gosdn-tview/views"
"code.fbi.h-da.de/cocsn/gosdn/log"
)
func main() {
conn, err := grpc.Connect()
if err != nil {
log.Fatal(err)
}
app := app.NewApp()
mainView := views.NewMainView(app, conn)
app.SetRoot(mainView)
app.Run()
defer app.Stop()
}
package views
import (
"code.fbi.h-da.de/cocsn/gosdn/cmd/gosdn-tview/app"
"github.com/rivo/tview"
)
type AddPNDView struct {
addPNDView *tview.Form
}
func NewAddPNDView(app *app.App) *AddPNDView {
pndv := &AddPNDView{
addPNDView: tview.NewForm(),
}
pndv.addPNDView.
SetBorder(true).
SetTitle("Add new PND")
pndv.addPNDView.
AddInputField("Name", "", 20, nil, nil).
AddInputField("description", "", 20, nil, nil).
AddDropDown("SI", []string{"Southbound 1", "Southbound 2", "Southbound 3", "Southbound 4"}, 0, nil).
AddButton("Send", func() {
//TODO: call grpc function here
}).
AddButton("Abort", func() {
app.SwitchPage("main")
}).
SetCancelFunc(func() {
app.SwitchPage("main")
})
return pndv
}
func (pndv *AddPNDView) GetContent() tview.Primitive {
return pndv.addPNDView
}
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"
)
type CommandListView struct {
commandsList *tview.List
}
func NewCommandListView() *CommandListView {
cv := &CommandListView{
commandsList: tview.NewList(),
}
cv.commandsList.
SetBorder(true).
SetTitle("Commands")
return cv
}
func (cv *CommandListView) GetContent() tview.Primitive {
return cv.commandsList
}
func (cv *CommandListView) GetCommands(app *app.App, rv *ResultAndInputView,
conn *grpc.ClientConn) {
//TODO: create own command in grpc -> commands
cv.commandsList.AddItem("AddPND", "closes the application", '!', func() {
rv.ChangeContentView("addPND")
app.SetFocus(rv.GetContent())
})
for i, command := range commands.CommandList {
f := command.Function
cv.commandsList.
AddItem(command.Name, command.Description, rune('a'+i), func() {
r := f(conn)
rv.SetContent(r)
rv.ChangeContentView("result")
})
}
cv.commandsList.AddItem("quit", "closes the application", 'q', func() {
app.Stop()
})
}
package views
import (
"code.fbi.h-da.de/cocsn/gosdn/cmd/gosdn-tview/app"
"github.com/rivo/tview"
"time"
)
type DatabaseStatusView struct {
databaseStatusView *tview.TextView
}
func NewDatabaseStatusView(app *app.App) *DatabaseStatusView {
dv := &DatabaseStatusView{
databaseStatusView: tview.NewTextView(),
}
dv.databaseStatusView.
SetDynamicColors(true).
SetTextAlign(tview.AlignCenter).
SetRegions(true).
SetBorder(true).
SetTitle("Database")
go databaseTicker(dv)
return dv
}
func (dv *DatabaseStatusView) GetContent() tview.Primitive {
return dv.databaseStatusView
}
func (dv *DatabaseStatusView) SetContent(s string) {
dv.databaseStatusView.Clear()
dv.databaseStatusView.SetText(s)
}
func databaseTicker(dv *DatabaseStatusView) {
ticker := time.NewTicker(5 * time.Second)
for t := range ticker.C {
dv.SetContent(t.String())
}
}
package views
import (
"code.fbi.h-da.de/cocsn/gosdn/cmd/gosdn-tview/app"
"github.com/rivo/tview"
"google.golang.org/grpc"
)
type FooterView struct {
footerView *tview.Flex
databaseStatusView *DatabaseStatusView
gRPCStatusView *GRPCStatusView
}
func NewFooterView(app *app.App, conn *grpc.ClientConn) *FooterView {
fw := &FooterView{
footerView: tview.NewFlex(),
databaseStatusView: NewDatabaseStatusView(app),
gRPCStatusView: NewGRPCStatusView(app, conn),
}
fw.footerView.
SetBorder(true).
SetTitle("Status")
fw.footerView.
AddItem(fw.gRPCStatusView.GetContent(), 0, 1, false).
AddItem(fw.databaseStatusView.GetContent(), 0, 1, false)
return fw
}
func (fw *FooterView) GetContent() tview.Primitive {
return fw.footerView
}
package views
import (
"code.fbi.h-da.de/cocsn/gosdn/cmd/gosdn-tview/app"
"github.com/rivo/tview"
"google.golang.org/grpc"
"time"
)
type GRPCStatusView struct {
gRPCStatusView *tview.TextView
}
func NewGRPCStatusView(app *app.App, conn *grpc.ClientConn) *GRPCStatusView {
sv := &GRPCStatusView{
gRPCStatusView: tview.NewTextView(),
}
sv.gRPCStatusView.
SetDynamicColors(true).
SetTextAlign(tview.AlignCenter).
SetRegions(true).
SetBorder(true).
SetTitle("gRPC")
//TODO: maybe there is another way to do this.
// pretty ugly atm, since it re-draws every 5 seconds...
sv.gRPCStatusView.SetChangedFunc(func() {
app.Draw()
})
go gRPCTicker(sv, conn)
return sv
}
func (sv *GRPCStatusView) GetContent() tview.Primitive {
return sv.gRPCStatusView
}
func (sv *GRPCStatusView) SetContent(s string) {
sv.gRPCStatusView.Clear()
sv.gRPCStatusView.SetText(s)
}
func gRPCTicker(sv *GRPCStatusView, conn *grpc.ClientConn) {
//TODO: refactor -> get rid of hardcoded values
ticker := time.NewTicker(5 * time.Second)
for range ticker.C {
if str := conn.GetState().String(); str == "READY" || str == "IDLE" {
sv.SetContent("[green]" + "connected")
} else {
sv.SetContent("[red]" + "disconnected")
}
}
}
package views
import "github.com/rivo/tview"
var goSDNAscii = ` ____ ____ _ _ _ _ _ _ _ ____ _ _ _
__ _ ___/ ___|| _ \| \ | | | | | | ___ ___| |__ ___ ___| |__ _ _| | ___ | _ \ __ _ _ __ _ __ ___ ___| |_ __ _ __| | |_
/ _ |/ _ \___ \| | | | \| | _____ | |_| |/ _ \ / __| '_ \/ __|/ __| '_ \| | | | |/ _ \ | | | |/ _ | '__| '_ _ \/ __| __/ _ |/ _ | __|
| (_| | (_) |__) | |_| | |\ | |_____| | _ | (_) | (__| | | \__ \ (__| | | | |_| | | __/ | |_| | (_| | | | | | | | \__ \ || (_| | (_| | |_
\__ |\___/____/|____/|_| \_| |_| |_|\___/ \___|_| |_|___/\___|_| |_|\__,_|_|\___| |____/ \__,_|_| |_| |_| |_|___/\__\__,_|\__,_|\__|
|___/ `
type HeaderView struct {
headerFlex *tview.Flex
titleView *tview.TextView
}
func NewHeaderView() *HeaderView {
//TODO: change to uses FlexBox if there is more to display in the header
hv := &HeaderView{
titleView: tview.NewTextView(),
}
hv.titleView.
SetText(goSDNAscii).
SetTextAlign(tview.AlignCenter).
SetBorder(true)
return hv
}
func (hv *HeaderView) GetContent() tview.Primitive {
return hv.titleView
}
package views
import (
"code.fbi.h-da.de/cocsn/gosdn/cmd/gosdn-tview/app"
"github.com/rivo/tview"
"google.golang.org/grpc"
)
type MainView struct {
pages *tview.Pages
mainGrid *tview.Grid
commandsListView *CommandListView
resultAndInputView *ResultAndInputView
headerView *HeaderView
footerView *FooterView
}
func NewMainView(app *app.App, conn *grpc.ClientConn) *MainView {
mv := &MainView{
pages: tview.NewPages(),
mainGrid: tview.NewGrid(),
commandsListView: NewCommandListView(),
resultAndInputView: NewResultAndInputView(app),
headerView: NewHeaderView(),
footerView: NewFooterView(app, conn),
}
mv.commandsListView.GetCommands(app, mv.resultAndInputView, conn)
mv.mainGrid.
SetRows(8, 0, 5).
SetColumns(40, 0).
AddItem(mv.headerView.GetContent(), 0, 0, 1, 2, 0, 0, false).
AddItem(mv.footerView.GetContent(), 2, 0, 1, 2, 0, 0, false)
mv.mainGrid.AddItem(mv.commandsListView.GetContent(), 1, 0, 1, 1, 0, 0, true).
AddItem(mv.resultAndInputView.GetContent(), 1, 1, 1, 1, 0, 0, false)
mv.pages.AddPage("main", mv.mainGrid, true, true)
return mv
}
func (mv *MainView) GetContent() tview.Primitive {
return mv.pages
}
package views
import (
"code.fbi.h-da.de/cocsn/gosdn/cmd/gosdn-tview/app"
"github.com/rivo/tview"
)
type ResultAndInputView struct {
pages *tview.Pages
resultView *tview.TextView
pndInputView *AddPNDView
}
func NewResultAndInputView(app *app.App) *ResultAndInputView {
rv := &ResultAndInputView{
pages: tview.NewPages(),
pndInputView: NewAddPNDView(app),
resultView: tview.NewTextView(),
}
rv.resultView.
SetDynamicColors(true).
SetRegions(true).
SetScrollable(true).
SetTitle("Result").
SetBorder(true)
rv.pages.
AddPage("result", rv.resultView, true, true).
AddPage("addPND", rv.pndInputView.GetContent(), true, false)
return rv
}
func (rv *ResultAndInputView) GetContent() tview.Primitive {
return rv.pages
}
func (rv *ResultAndInputView) ChangeContentView(s string) {
rv.pages.SwitchToPage(s)
}
func (rv *ResultAndInputView) SetContent(s string) {
rv.resultView.Clear()
rv.resultView.SetText(s)
}
#example gosdn.toml
CliSocket = "localhost:55055"
DatabaseSocket = "bolt://141.100.70.170:7687"
ConfigPath = "./configs/gosdn.toml"
# example config
CliSocket = "localhost:55055"
DatabaseSocket = "bolt://141.100.70.170:7687"
DatabaseUser = ""
DatabasePassword = ""
DatabaseCrypto = false
ConfigPath = "./configs/gosdn.toml"
......@@ -11,8 +11,8 @@ require (
github.com/google/go-cmp v0.4.1 // indirect
github.com/neo4j/neo4j-go-driver v1.8.3
github.com/onsi/ginkgo v1.13.0 // indirect
github.com/rivo/tview v0.0.0-20201018122409-d551c850a743
golang.org/x/net v0.0.0-20200904194848-62affa334b73 // indirect
golang.org/x/sys v0.0.0-20200817155316-9781c653f443 // indirect
google.golang.org/genproto v0.0.0-20200519141106-08726f379972 // indirect
google.golang.org/grpc v1.29.1
google.golang.org/protobuf v1.23.0
......
......@@ -32,6 +32,10 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell/v2 v2.0.1-0.20201017141208-acf90d56d591 h1:0WWUDZ1oxq7NxVyGo8M3KI5jbkiwNAdZFFzAdC68up4=
github.com/gdamore/tcell/v2 v2.0.1-0.20201017141208-acf90d56d591/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
......@@ -171,6 +175,8 @@ github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
......@@ -179,6 +185,9 @@ github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8
github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg=
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
......@@ -208,6 +217,10 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rivo/tview v0.0.0-20201018122409-d551c850a743 h1:9BBjVJTRxuYBeCAv9DFH2hSzY0ujLx5sxMg5D3K/Xeg=
github.com/rivo/tview v0.0.0-20201018122409-d551c850a743/go.mod h1:t7mcA3nlK9dxD1DMoz/DQRMWFMkGBUj6rJBM5VNfLFA=
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
......@@ -282,18 +295,21 @@ golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200817155316-9781c653f443 h1:X18bCaipMcoJGm27Nv7zr4XYPKGUy92GtqboKC2Hxaw=
golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201017003518-b09fb700fbb7 h1:XtNJkfEjb4zR3q20BBBcYUykVOEMgZeIUOpBPfNYgxg=
golang.org/x/sys v0.0.0-20201017003518-b09fb700fbb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment