Skip to content

Commit

Permalink
adding user interface scaffolding (#16)
Browse files Browse the repository at this point in the history
* adding user interface scaffolding

* adding dist directory

* linter

* moar linter

* fixing test
  • Loading branch information
autodidaddict authored Jan 2, 2024
1 parent 2d87466 commit 8a11ac4
Show file tree
Hide file tree
Showing 46 changed files with 2,330 additions and 26 deletions.
26 changes: 25 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,25 @@
.task
.task

# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

# Go workspace file
go.work

.vscode
31 changes: 16 additions & 15 deletions control-api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
// $NEX.RUN.{namespace}.{node}
// $NEX.STOP.{namespace}.{node}

type apiClient struct {
type Client struct {
nc *nats.Conn
timeout time.Duration
namespace string
Expand All @@ -28,20 +28,20 @@ type apiClient struct {

// Creates a new client to communicate with a group of NEX nodes, using the
// namespace of 'default' for applicable requests
func NewApiClient(nc *nats.Conn, timeout time.Duration, log *logrus.Logger) *apiClient {
func NewApiClient(nc *nats.Conn, timeout time.Duration, log *logrus.Logger) *Client {
return NewApiClientWithNamespace(nc, timeout, "default", log)
}

// Creates a new client to communicate with a group of NEX nodes all within a given namespace. Note that
// this namespace is used for requests where it is mandatory
func NewApiClientWithNamespace(nc *nats.Conn, timeout time.Duration, namespace string, log *logrus.Logger) *apiClient {
return &apiClient{nc: nc, timeout: timeout, namespace: namespace, log: log}
func NewApiClientWithNamespace(nc *nats.Conn, timeout time.Duration, namespace string, log *logrus.Logger) *Client {
return &Client{nc: nc, timeout: timeout, namespace: namespace, log: log}
}

// Attempts to stop a running workload. This can fail for a wide variety of reasons, the most common
// is likely to be security validation that prevents one issuer from issuing a stop request for
// another issuer's workload
func (api *apiClient) StopWorkload(stopRequest *StopRequest) (*StopResponse, error) {
func (api *Client) StopWorkload(stopRequest *StopRequest) (*StopResponse, error) {
subject := fmt.Sprintf("%s.STOP.%s.%s", APIPrefix, api.namespace, stopRequest.TargetNode)
bytes, err := api.performRequest(subject, stopRequest)
if err != nil {
Expand All @@ -59,7 +59,7 @@ func (api *apiClient) StopWorkload(stopRequest *StopRequest) (*StopResponse, err

// Attempts to start a workload. The workload URI, at the moment, must always point to a NATS object store
// bucket in the form of `nats://{bucket}/{key}`
func (api *apiClient) StartWorkload(request *RunRequest) (*RunResponse, error) {
func (api *Client) StartWorkload(request *RunRequest) (*RunResponse, error) {
subject := fmt.Sprintf("%s.RUN.%s.%s", APIPrefix, api.namespace, request.TargetNode)
bytes, err := api.performRequest(subject, request)
if err != nil {
Expand All @@ -75,7 +75,7 @@ func (api *apiClient) StartWorkload(request *RunRequest) (*RunResponse, error) {
}

// Requests information for a given node within the client's namespace
func (api *apiClient) NodeInfo(nodeId string) (*InfoResponse, error) {
func (api *Client) NodeInfo(nodeId string) (*InfoResponse, error) {
subject := fmt.Sprintf("%s.INFO.%s.%s", APIPrefix, api.namespace, nodeId)
bytes, err := api.performRequest(subject, nil)
if err != nil {
Expand All @@ -92,7 +92,7 @@ func (api *apiClient) NodeInfo(nodeId string) (*InfoResponse, error) {

// Attempts to list all nodes. Note that this operation returns all visible nodes regardless of
// namespace
func (api *apiClient) ListNodes() ([]PingResponse, error) {
func (api *Client) ListNodes() ([]PingResponse, error) {
ctx, cancel := context.WithTimeout(context.Background(), api.timeout)
defer cancel()

Expand Down Expand Up @@ -130,14 +130,14 @@ func (api *apiClient) ListNodes() ([]PingResponse, error) {

// A convenience function that subscribes to all available logs and uses
// an unbuffered, blocking channel
func (api *apiClient) MonitorAllLogs() (chan EmittedLog, error) {
func (api *Client) MonitorAllLogs() (chan EmittedLog, error) {
return api.MonitorLogs("*", "*", "*", "*", 0)
}

// Creates a NATS subscription to the appropriate log subject. If you do not want to limit
// the monitor by any of the filters, supply a '*', not an empty string. Bufferlength refers
// to the size of the channel buffer, where 0 is unbuffered (aka blocking)
func (api *apiClient) MonitorLogs(
func (api *Client) MonitorLogs(
namespaceFilter string,
nodeFilter string,
workloadFilter string,
Expand All @@ -161,14 +161,14 @@ func (api *apiClient) MonitorLogs(

// A convenience function that monitors all available events without filter, and
// uses an unbuffered (blocking) channel for the results
func (api *apiClient) MonitorAllEvents() (chan EmittedEvent, error) {
func (api *Client) MonitorAllEvents() (chan EmittedEvent, error) {
return api.MonitorEvents("*", "*", 0)
}

// Creates a NATS subscription to the appropriate event subject. If you don't want to limit
// the monitor to a specific namespace or event type, then supply '*' for both values, not
// an empty string. Buffer length is the size of the channel buffer, where 0 is unbuffered (blocking)
func (api *apiClient) MonitorEvents(
func (api *Client) MonitorEvents(
namespaceFilter string,
eventTypeFilter string,
bufferLength int) (chan EmittedEvent, error) {
Expand All @@ -195,7 +195,7 @@ func (api *apiClient) MonitorEvents(
return eventChannel, nil
}

func handleEventEntry(api *apiClient, ch chan EmittedEvent) func(m *nats.Msg) {
func handleEventEntry(api *Client, ch chan EmittedEvent) func(m *nats.Msg) {
return func(m *nats.Msg) {
tokens := strings.Split(m.Subject, ".")
if len(tokens) != 4 {
Expand All @@ -219,7 +219,7 @@ func handleEventEntry(api *apiClient, ch chan EmittedEvent) func(m *nats.Msg) {
}
}

func handleLogEntry(api *apiClient, ch chan EmittedLog) func(m *nats.Msg) {
func handleLogEntry(api *Client, ch chan EmittedLog) func(m *nats.Msg) {
return func(m *nats.Msg) {
/*
$NEX.logs.{namespace}.{node}.{workload}.{vm}
Expand All @@ -242,6 +242,7 @@ func handleLogEntry(api *apiClient, ch chan EmittedLog) func(m *nats.Msg) {
Namespace: tokens[2],
NodeId: tokens[3],
Workload: tokens[4],
Timestamp: time.Now().UTC().Format(time.RFC3339),
rawLog: logEntry,
}
}
Expand All @@ -250,7 +251,7 @@ func handleLogEntry(api *apiClient, ch chan EmittedLog) func(m *nats.Msg) {

// Helper that submits data, gets a standard envelope back, and returns the inner data
// payload as JSON
func (api *apiClient) performRequest(subject string, raw interface{}) ([]byte, error) {
func (api *Client) performRequest(subject string, raw interface{}) ([]byte, error) {
var bytes []byte
var err error
if raw == nil {
Expand Down
4 changes: 2 additions & 2 deletions control-api/monitor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
var _ = Describe("event monitor", func() {
var nc *nats.Conn
var log *logrus.Logger
var client *apiClient
var client *Client
var ch chan EmittedEvent
var subject EmittedEvent

Expand Down Expand Up @@ -79,7 +79,7 @@ var _ = Describe("event monitor", func() {
var _ = Describe("log monitor", func() {
var nc *nats.Conn
var log *logrus.Logger
var client *apiClient
var client *Client
var ch chan EmittedLog
var subject EmittedLog

Expand Down
14 changes: 7 additions & 7 deletions control-api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,10 @@ type Envelope struct {

// Wrapper for what goes across the wire
type EmittedLog struct {
Namespace string
NodeId string
Workload string
Namespace string `json:"namespace"`
NodeId string `json:"node_id"`
Workload string `json:"workload_id"`
Timestamp string `json:"timestamp"`
rawLog
}

Expand All @@ -84,12 +85,11 @@ type rawLog struct {
MachineId string `json:"machine_id"`
}

// Note this a wrapper to add context to a cloud event, and is not
// intended to be sent on the wire as-is
// Note this a wrapper to add context to a cloud event
type EmittedEvent struct {
cloudevents.Event
Namespace string
EventType string
Namespace string `json:"namespace"`
EventType string `json:"event_type"`
}

func NewEnvelope(dataType string, data interface{}, err *string) Envelope {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ require (
github.com/go-stack/stack v1.8.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,8 @@ github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
Expand Down
File renamed without changes.
5 changes: 5 additions & 0 deletions nex-cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,17 @@ import (

var (
Opts = &Options{}
GuiOpts = &UiOptions{}
RunOpts = &RunOptions{Env: make(map[string]string)}
DevRunOpts = &DevRunOptions{}
StopOpts = &StopOptions{}
WatchOpts = &WatchOptions{}
)

type UiOptions struct {
Port int
}

type DevRunOptions struct {
Filename string
// Stop a workload with the same name on a target
Expand Down
4 changes: 4 additions & 0 deletions nex-cli/cmd/nex/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,9 @@ func main() {
evts := ncli.Command("events", "Live monitor events from nex nodes")
evts.Action(cli.WatchEvents)

ui := ncli.Command("ui", "Starts a web server for interacting with Nex")
ui.Flag("port", "Port on which to run the UI").Default("8080").IntVar(&cli.GuiOpts.Port)
ui.Action(cli.RunUI)

ncli.MustParseWithUsage(os.Args[1:])
}
2 changes: 1 addition & 1 deletion nex-cli/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func ListNodes(ctx *fisk.ParseContext) error {
if err != nil {
return err
}
// TODO

renderNodeList(nodes)
return nil

Expand Down
12 changes: 12 additions & 0 deletions nex-cli/ui.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package nexcli

import (
nexui "github.com/ConnectEverything/nex/nex-ui"
"github.com/choria-io/fisk"
)

func RunUI(ctx *fisk.ParseContext) error {
nexui.ServeUI(GuiOpts.Port)

return nil
}
2 changes: 2 additions & 0 deletions nex-ui/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# exclude the binary
cmd/nexui/nexui
Loading

0 comments on commit 8a11ac4

Please sign in to comment.