Restructure of the webservice components
This allows for a more class based approach to the types of things that the webservice will be doing going forward. It shunts common tasks to their respective classes to help make it clearer who's doing what where Also removes electron as a type from the ws messages and replaces it with the airshipui type Change-Id: I72786821385244a4c0d93c2cfad73c0d740ef942
This commit is contained in:
parent
2ff942f766
commit
5224e65492
@ -39,16 +39,13 @@ var rootCmd = &cobra.Command{
|
|||||||
Version: Version(),
|
Version: Version(),
|
||||||
}
|
}
|
||||||
|
|
||||||
var headless bool
|
|
||||||
var remote bool
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// Add a 'version' command, in addition to the '--version' option that is auto created
|
// Add a 'version' command, in addition to the '--version' option that is auto created
|
||||||
rootCmd.AddCommand(newVersionCmd())
|
rootCmd.AddCommand(newVersionCmd())
|
||||||
|
|
||||||
// add the remote & headless options in case people want to run a split setup
|
// add the remote & headless options in case people want to run a split setup
|
||||||
rootCmd.Flags().BoolVar(&headless, "headless", false, "start the system in headless webserver only, no ui.")
|
rootCmd.Flags().BoolVar(&configs.Headless, "headless", false, "start the system in headless webserver only, no ui.")
|
||||||
rootCmd.Flags().BoolVar(&remote, "remote", false, "start the system in remote ui only, no webserver.")
|
rootCmd.Flags().BoolVar(&configs.Remote, "remote", false, "start the system in remote ui only, no webserver.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func launch(cmd *cobra.Command, args []string) {
|
func launch(cmd *cobra.Command, args []string) {
|
||||||
@ -87,6 +84,7 @@ func launch(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// just a little ditty to see if we should open the ui or the webservice or both
|
// just a little ditty to see if we should open the ui or the webservice or both
|
||||||
|
// this is done as a switch insted of an if else because our linter prefers switches to if elses
|
||||||
switch handleStartType() {
|
switch handleStartType() {
|
||||||
case "headless":
|
case "headless":
|
||||||
// start webservice and listen for the the ctl + c to exit
|
// start webservice and listen for the the ctl + c to exit
|
||||||
@ -122,13 +120,13 @@ func startElectron() {
|
|||||||
// TODO: determine if cobra can make flags exclusive without the extra logic
|
// TODO: determine if cobra can make flags exclusive without the extra logic
|
||||||
func handleStartType() string {
|
func handleStartType() string {
|
||||||
st := "default"
|
st := "default"
|
||||||
if remote && headless {
|
if configs.Remote && configs.Headless {
|
||||||
log.Fatalf("Cannot set both --remote and --headless flags")
|
log.Fatalf("Cannot set both --remote and --headless flags")
|
||||||
}
|
}
|
||||||
|
|
||||||
if remote {
|
if configs.Remote {
|
||||||
st = "remote"
|
st = "remote"
|
||||||
} else if headless {
|
} else if configs.Headless {
|
||||||
st = "headless"
|
st = "headless"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,9 @@ import (
|
|||||||
// variables related to UI config
|
// variables related to UI config
|
||||||
var (
|
var (
|
||||||
UIConfig Config
|
UIConfig Config
|
||||||
|
|
||||||
|
Headless bool
|
||||||
|
Remote bool
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config basic structure to hold configuration params for Airship UI
|
// Config basic structure to hold configuration params for Airship UI
|
||||||
@ -98,7 +101,7 @@ type WsSubComponentType string
|
|||||||
// constants related to specific request/component/subcomponent types for WsRequests
|
// constants related to specific request/component/subcomponent types for WsRequests
|
||||||
const (
|
const (
|
||||||
AirshipCTL WsRequestType = "airshipctl"
|
AirshipCTL WsRequestType = "airshipctl"
|
||||||
Electron WsRequestType = "electron"
|
AirshipUI WsRequestType = "airshipui"
|
||||||
Alert WsRequestType = "alert"
|
Alert WsRequestType = "alert"
|
||||||
|
|
||||||
Authcomplete WsComponentType = "authcomplete"
|
Authcomplete WsComponentType = "authcomplete"
|
||||||
|
@ -32,6 +32,13 @@ var (
|
|||||||
basepath = filepath.Dir(b)
|
basepath = filepath.Dir(b)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CTLFunctionMap is a function map for the CTL functions that is referenced in the webservice
|
||||||
|
var CTLFunctionMap = map[configs.WsComponentType]func(configs.WsMessage) configs.WsMessage{
|
||||||
|
configs.CTLConfig: HandleConfigRequest,
|
||||||
|
configs.Baremetal: HandleBaremetalRequest,
|
||||||
|
configs.Document: HandleDocumentRequest,
|
||||||
|
}
|
||||||
|
|
||||||
// maintain the state of a potentially long running process
|
// maintain the state of a potentially long running process
|
||||||
var runningRequests map[configs.WsSubComponentType]bool = make(map[configs.WsSubComponentType]bool)
|
var runningRequests map[configs.WsSubComponentType]bool = make(map[configs.WsSubComponentType]bool)
|
||||||
|
|
||||||
|
@ -15,147 +15,34 @@
|
|||||||
package webservice
|
package webservice
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
"opendev.org/airship/airshipui/internal/configs"
|
"opendev.org/airship/airshipui/internal/configs"
|
||||||
"opendev.org/airship/airshipui/internal/integrations/ctl"
|
"opendev.org/airship/airshipui/internal/integrations/ctl"
|
||||||
)
|
)
|
||||||
|
|
||||||
// gorilla ws specific HTTP upgrade to WebSockets
|
|
||||||
var upgrader = websocket.Upgrader{
|
|
||||||
ReadBufferSize: 1024,
|
|
||||||
WriteBufferSize: 1024,
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is a way to allow for arbitrary messages to be processed by the backend
|
// this is a way to allow for arbitrary messages to be processed by the backend
|
||||||
// the message of a specifc component is shunted to that subsystem for further processing
|
// the message of a specifc component is shunted to that subsystem for further processing
|
||||||
// TODO: make this a dynamic registration of components
|
|
||||||
var functionMap = map[configs.WsRequestType]map[configs.WsComponentType]func(configs.WsMessage) configs.WsMessage{
|
var functionMap = map[configs.WsRequestType]map[configs.WsComponentType]func(configs.WsMessage) configs.WsMessage{
|
||||||
configs.Electron: {
|
configs.AirshipUI: {
|
||||||
configs.Keepalive: keepaliveReply,
|
configs.Keepalive: keepaliveReply,
|
||||||
configs.Initialize: clientInit,
|
configs.Initialize: clientInit,
|
||||||
},
|
},
|
||||||
configs.AirshipCTL: {
|
configs.AirshipCTL: ctl.CTLFunctionMap,
|
||||||
configs.CTLConfig: ctl.HandleConfigRequest,
|
|
||||||
configs.Baremetal: ctl.HandleBaremetalRequest,
|
|
||||||
configs.Document: ctl.HandleDocumentRequest,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// websocket that'll be reused by several places
|
|
||||||
var ws *websocket.Conn
|
|
||||||
|
|
||||||
// semaphore to signal the UI to authenticate
|
// semaphore to signal the UI to authenticate
|
||||||
var isAuthenticated bool
|
var isAuthenticated bool
|
||||||
|
|
||||||
// handle the origin request & upgrade to websocket
|
|
||||||
func onOpen(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// gorilla ws will give a 403 on a cross origin request, so we silence its complaints
|
|
||||||
// This happens with electron because it's sending an origin of 'file://' instead of 'localhost:8080'
|
|
||||||
upgrader.CheckOrigin = func(r *http.Request) bool { return true }
|
|
||||||
|
|
||||||
// upgrade to websocket protocol over http
|
|
||||||
log.Printf("Establishing the websocket")
|
|
||||||
wsConn, err := upgrader.Upgrade(w, r, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Could not open websocket connection from: %s\n", r.Host)
|
|
||||||
http.Error(w, "Could not open websocket connection", http.StatusBadRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
ws = wsConn
|
|
||||||
log.Printf("WebSocket established with %s\n", ws.RemoteAddr().String())
|
|
||||||
|
|
||||||
// send any initialization alerts to UI and clear the queue
|
|
||||||
for len(Alerts) > 0 {
|
|
||||||
sendAlertMessage(Alerts[0])
|
|
||||||
Alerts[0] = configs.WsMessage{}
|
|
||||||
Alerts = Alerts[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
go onMessage()
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle messaging to the client
|
|
||||||
func onMessage() {
|
|
||||||
// just in case clean up the websocket
|
|
||||||
defer onClose()
|
|
||||||
|
|
||||||
for {
|
|
||||||
var request configs.WsMessage
|
|
||||||
err := ws.ReadJSON(&request)
|
|
||||||
if err != nil {
|
|
||||||
onError(err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// look through the function map to find the type to handle the request
|
|
||||||
if reqType, ok := functionMap[request.Type]; ok {
|
|
||||||
// the function map may have a component (function) to process the request
|
|
||||||
if component, ok := reqType[request.Component]; ok {
|
|
||||||
// get the response and tag the timestamp so it's not repeated across all functions
|
|
||||||
response := component(request)
|
|
||||||
response.Timestamp = time.Now().UnixNano() / 1000000
|
|
||||||
if err = ws.WriteJSON(response); err != nil {
|
|
||||||
onError(err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err = ws.WriteJSON(requestErrorHelper(fmt.Sprintf("Requested component: %s, not found",
|
|
||||||
request.Component), request)); err != nil {
|
|
||||||
onError(err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
log.Printf("Requested component: %s, not found\n", request.Component)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err = ws.WriteJSON(requestErrorHelper(fmt.Sprintf("Requested type: %s, not found",
|
|
||||||
request.Type), request)); err != nil {
|
|
||||||
onError(err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
log.Printf("Requested type: %s, not found\n", request.Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func requestErrorHelper(err string, request configs.WsMessage) configs.WsMessage {
|
|
||||||
return configs.WsMessage{
|
|
||||||
Type: request.Type,
|
|
||||||
Component: request.Component,
|
|
||||||
Timestamp: time.Now().UnixNano() / 1000000,
|
|
||||||
Error: err,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The keepalive response including a timestamp from the server
|
|
||||||
// The electron / web app will occasionally ping the server due to the websocket default timeout
|
|
||||||
func keepaliveReply(configs.WsMessage) configs.WsMessage {
|
|
||||||
return configs.WsMessage{
|
|
||||||
Type: configs.Electron,
|
|
||||||
Component: configs.Keepalive,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// common websocket close with logging
|
|
||||||
func onClose() {
|
|
||||||
log.Printf("Closing websocket")
|
|
||||||
// ws.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// common websocket error handling with logging
|
|
||||||
func onError(err error) {
|
|
||||||
log.Printf("Error receiving / sending message: %s\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle an auth complete attempt
|
// handle an auth complete attempt
|
||||||
func handleAuth(w http.ResponseWriter, r *http.Request) {
|
func handleAuth(response http.ResponseWriter, request *http.Request) {
|
||||||
// TODO: handle the response body to capture the credentials
|
// TODO: handle the response body to capture the credentials
|
||||||
err := ws.WriteJSON(configs.WsMessage{
|
err := ws.WriteJSON(configs.WsMessage{
|
||||||
Type: configs.Electron,
|
Type: configs.AirshipUI,
|
||||||
Component: configs.Authcomplete,
|
Component: configs.Authcomplete,
|
||||||
Timestamp: time.Now().UnixNano() / 1000000,
|
Timestamp: time.Now().UnixNano() / 1000000,
|
||||||
})
|
})
|
||||||
@ -169,34 +56,34 @@ func handleAuth(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WebServer will run the handler functions for WebSockets
|
// WebServer will run the handler functions for WebSockets
|
||||||
// TODO: potentially add in the ability to serve static content
|
|
||||||
func WebServer() {
|
func WebServer() {
|
||||||
|
webServerMux := http.NewServeMux()
|
||||||
|
|
||||||
// some things may need a redirect so we'll give them a url to do that with
|
// some things may need a redirect so we'll give them a url to do that with
|
||||||
http.HandleFunc("/auth", handleAuth)
|
webServerMux.HandleFunc("/auth", handleAuth)
|
||||||
|
|
||||||
// hand off the websocket upgrade over http
|
// hand off the websocket upgrade over http
|
||||||
http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
|
webServerMux.HandleFunc("/ws", func(response http.ResponseWriter, request *http.Request) {
|
||||||
onOpen(w, r)
|
onOpen(response, request)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// We can serve up static content if it's flagged as headless on command line
|
||||||
|
if configs.Headless {
|
||||||
|
// static file server
|
||||||
|
path, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
staticContent := filepath.Join(path + string(os.PathSeparator) + "web")
|
||||||
|
log.Println("Attempting to serve static content from ", staticContent)
|
||||||
|
fs := http.FileServer(http.Dir(staticContent))
|
||||||
|
webServerMux.Handle("/", fs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: pull ports out into conf files
|
||||||
log.Println("Attempting to start webservice on localhost:8080")
|
log.Println("Attempting to start webservice on localhost:8080")
|
||||||
if err := http.ListenAndServe(":8080", nil); err != nil {
|
if err := http.ListenAndServe(":8080", webServerMux); err != nil {
|
||||||
log.Fatal("ListenAndServe:", err)
|
log.Fatal("ListenAndServe:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func clientInit(configs.WsMessage) configs.WsMessage {
|
|
||||||
// if no auth method is supplied start with minimal functionality
|
|
||||||
if configs.UIConfig.AuthMethod == nil {
|
|
||||||
isAuthenticated = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return configs.WsMessage{
|
|
||||||
Type: configs.Electron,
|
|
||||||
Component: configs.Initialize,
|
|
||||||
IsAuthenticated: isAuthenticated,
|
|
||||||
Dashboards: configs.UIConfig.Clusters,
|
|
||||||
Plugins: configs.UIConfig.Plugins,
|
|
||||||
Authentication: configs.UIConfig.AuthMethod,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -15,15 +15,12 @@
|
|||||||
package webservice
|
package webservice
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"opendev.org/airship/airshipui/internal/configs"
|
"opendev.org/airship/airshipui/internal/configs"
|
||||||
"opendev.org/airship/airshipui/internal/integrations/ctl"
|
|
||||||
"opendev.org/airship/airshipui/testutil"
|
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -34,10 +31,10 @@ const (
|
|||||||
serverAddr string = "localhost:8080"
|
serverAddr string = "localhost:8080"
|
||||||
|
|
||||||
// client messages
|
// client messages
|
||||||
initialize string = `{"type":"electron","component":"initialize"}`
|
initialize string = `{"type":"airshipui","component":"initialize"}`
|
||||||
keepalive string = `{"type":"electron","component":"keepalive"}`
|
keepalive string = `{"type":"airshipui","component":"keepalive"}`
|
||||||
unknownType string = `{"type":"fake_type","component":"initialize"}`
|
unknownType string = `{"type":"fake_type","component":"initialize"}`
|
||||||
unknownComponent string = `{"type":"electron","component":"fake_component"}`
|
unknownComponent string = `{"type":"airshipui","component":"fake_component"}`
|
||||||
document string = `{"type":"airshipctl","component":"document","subcomponent":"getDefaults"}`
|
document string = `{"type":"airshipctl","component":"document","subcomponent":"getDefaults"}`
|
||||||
baremetal string = `{"type":"airshipctl","component":"baremetal","subcomponent":"getDefaults"}`
|
baremetal string = `{"type":"airshipctl","component":"baremetal","subcomponent":"getDefaults"}`
|
||||||
config string = `{"type":"airshipctl","component":"config","subcomponent":"getDefaults"}`
|
config string = `{"type":"airshipctl","component":"config","subcomponent":"getDefaults"}`
|
||||||
@ -47,126 +44,6 @@ func init() {
|
|||||||
go WebServer()
|
go WebServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClientInit(t *testing.T) {
|
|
||||||
client, err := NewTestClient()
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer client.Close()
|
|
||||||
|
|
||||||
// simulate config provided by airshipui.json
|
|
||||||
configs.UIConfig = testutil.DummyCompleteConfig()
|
|
||||||
|
|
||||||
// get server response to "initialize" message from client
|
|
||||||
response, err := getResponse(client, initialize)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
expected := configs.WsMessage{
|
|
||||||
Type: configs.Electron,
|
|
||||||
Component: configs.Initialize,
|
|
||||||
IsAuthenticated: false,
|
|
||||||
Dashboards: []configs.Cluster{
|
|
||||||
testutil.DummyClusterConfig(),
|
|
||||||
},
|
|
||||||
Plugins: []configs.Plugin{
|
|
||||||
testutil.DummyPluginWithDashboardConfig(),
|
|
||||||
testutil.DummyPluginNoDashboard(),
|
|
||||||
},
|
|
||||||
Authentication: testutil.DummyAuthMethodConfig(),
|
|
||||||
// don't fail on timestamp diff
|
|
||||||
Timestamp: response.Timestamp,
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, expected, response)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestClientInitNoAuth(t *testing.T) {
|
|
||||||
client, err := NewTestClient()
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer client.Close()
|
|
||||||
|
|
||||||
// simulate config provided by airshipui.json
|
|
||||||
configs.UIConfig = testutil.DummyConfigNoAuth()
|
|
||||||
|
|
||||||
isAuthenticated = false
|
|
||||||
|
|
||||||
response, err := getResponse(client, initialize)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
expected := configs.WsMessage{
|
|
||||||
Type: configs.Electron,
|
|
||||||
Component: configs.Initialize,
|
|
||||||
// isAuthenticated should now be true in response
|
|
||||||
IsAuthenticated: true,
|
|
||||||
Dashboards: []configs.Cluster{
|
|
||||||
testutil.DummyClusterConfig(),
|
|
||||||
},
|
|
||||||
Plugins: []configs.Plugin{
|
|
||||||
testutil.DummyPluginWithDashboardConfig(),
|
|
||||||
testutil.DummyPluginNoDashboard(),
|
|
||||||
},
|
|
||||||
// don't fail on timestamp diff
|
|
||||||
Timestamp: response.Timestamp,
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, expected, response)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKeepalive(t *testing.T) {
|
|
||||||
client, err := NewTestClient()
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer client.Close()
|
|
||||||
|
|
||||||
// get server response to "keepalive" message from client
|
|
||||||
response, err := getResponse(client, keepalive)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
expected := configs.WsMessage{
|
|
||||||
Type: configs.Electron,
|
|
||||||
Component: configs.Keepalive,
|
|
||||||
// don't fail on timestamp diff
|
|
||||||
Timestamp: response.Timestamp,
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, expected, response)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnknownType(t *testing.T) {
|
|
||||||
client, err := NewTestClient()
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer client.Close()
|
|
||||||
|
|
||||||
response, err := getResponse(client, unknownType)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
expected := configs.WsMessage{
|
|
||||||
Type: "fake_type",
|
|
||||||
Component: configs.Initialize,
|
|
||||||
// don't fail on timestamp diff
|
|
||||||
Timestamp: response.Timestamp,
|
|
||||||
Error: "Requested type: fake_type, not found",
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, expected, response)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnknownComponent(t *testing.T) {
|
|
||||||
client, err := NewTestClient()
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer client.Close()
|
|
||||||
|
|
||||||
response, err := getResponse(client, unknownComponent)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
expected := configs.WsMessage{
|
|
||||||
Type: configs.Electron,
|
|
||||||
Component: "fake_component",
|
|
||||||
// don't fail on timestamp diff
|
|
||||||
Timestamp: response.Timestamp,
|
|
||||||
Error: "Requested component: fake_component, not found",
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, expected, response)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHandleAuth(t *testing.T) {
|
func TestHandleAuth(t *testing.T) {
|
||||||
client, err := NewTestClient()
|
client, err := NewTestClient()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -183,7 +60,7 @@ func TestHandleAuth(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
expected := configs.WsMessage{
|
expected := configs.WsMessage{
|
||||||
Type: configs.Electron,
|
Type: configs.AirshipUI,
|
||||||
Component: configs.Authcomplete,
|
Component: configs.Authcomplete,
|
||||||
// don't fail on timestamp diff
|
// don't fail on timestamp diff
|
||||||
Timestamp: response.Timestamp,
|
Timestamp: response.Timestamp,
|
||||||
@ -194,101 +71,6 @@ func TestHandleAuth(t *testing.T) {
|
|||||||
assert.Equal(t, expected, response)
|
assert.Equal(t, expected, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleDocumentRequest(t *testing.T) {
|
|
||||||
client, err := NewTestClient()
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer client.Close()
|
|
||||||
|
|
||||||
expectedHTML, err := ctl.GetDocumentHTML()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
response, err := getResponse(client, document)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
expected := configs.WsMessage{
|
|
||||||
Type: configs.AirshipCTL,
|
|
||||||
Component: configs.Document,
|
|
||||||
SubComponent: configs.GetDefaults,
|
|
||||||
HTML: expectedHTML,
|
|
||||||
// don't fail on timestamp diff
|
|
||||||
Timestamp: response.Timestamp,
|
|
||||||
}
|
|
||||||
|
|
||||||
// the non typed interface requires us to break up the checking otherwise the 2 will never be equal
|
|
||||||
assert.Equal(t, expected.Type, response.Type)
|
|
||||||
assert.Equal(t, expected.Component, response.Component)
|
|
||||||
assert.Equal(t, expected.SubComponent, response.SubComponent)
|
|
||||||
assert.Equal(t, expected.HTML, response.HTML)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHandleBaremetalRequest(t *testing.T) {
|
|
||||||
client, err := NewTestClient()
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer client.Close()
|
|
||||||
|
|
||||||
expectedHTML, err := ctl.GetBaremetalHTML()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
response, err := getResponse(client, baremetal)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
expected := configs.WsMessage{
|
|
||||||
Type: configs.AirshipCTL,
|
|
||||||
Component: configs.Baremetal,
|
|
||||||
SubComponent: configs.GetDefaults,
|
|
||||||
HTML: expectedHTML,
|
|
||||||
// don't fail on timestamp diff
|
|
||||||
Timestamp: response.Timestamp,
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, expected, response)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHandleConfigRequest(t *testing.T) {
|
|
||||||
client, err := NewTestClient()
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer client.Close()
|
|
||||||
|
|
||||||
response, err := getResponse(client, config)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
expected := configs.WsMessage{
|
|
||||||
Type: configs.AirshipCTL,
|
|
||||||
Component: configs.CTLConfig,
|
|
||||||
SubComponent: configs.GetDefaults,
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, expected.Type, response.Type)
|
|
||||||
assert.Equal(t, expected.Component, response.Component)
|
|
||||||
assert.Equal(t, expected.SubComponent, response.SubComponent)
|
|
||||||
|
|
||||||
// NOTE(mfuller): integrations/ctl 'client' gets initialized
|
|
||||||
// *before* any env vars can be set here in tests, so client
|
|
||||||
// will always be initialized with default config file locations.
|
|
||||||
// Client is not exported, so we can't set it directly here. We'll
|
|
||||||
// simply make sure there's no Error value and that HTML has
|
|
||||||
// len > 0. Full testing of this response is covered in the
|
|
||||||
// integrations/ctl tests.
|
|
||||||
|
|
||||||
assert.Len(t, response.Error, 0)
|
|
||||||
assert.Greater(t, len(response.HTML), 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getResponse(client *websocket.Conn, message string) (configs.WsMessage, error) {
|
|
||||||
err := client.WriteJSON(json.RawMessage(message))
|
|
||||||
if err != nil {
|
|
||||||
return configs.WsMessage{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var response configs.WsMessage
|
|
||||||
err = client.ReadJSON(&response)
|
|
||||||
if err != nil {
|
|
||||||
return configs.WsMessage{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return response, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTestClient() (*websocket.Conn, error) {
|
func NewTestClient() (*websocket.Conn, error) {
|
||||||
var err error
|
var err error
|
||||||
var client *websocket.Conn
|
var client *websocket.Conn
|
||||||
|
151
internal/webservice/websocket.go
Executable file
151
internal/webservice/websocket.go
Executable file
@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package webservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"opendev.org/airship/airshipui/internal/configs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// gorilla ws specific HTTP upgrade to WebSockets
|
||||||
|
var upgrader = websocket.Upgrader{
|
||||||
|
ReadBufferSize: 1024,
|
||||||
|
WriteBufferSize: 1024,
|
||||||
|
}
|
||||||
|
|
||||||
|
// websocket that'll be reused by several places
|
||||||
|
var ws *websocket.Conn
|
||||||
|
|
||||||
|
// handle the origin request & upgrade to websocket
|
||||||
|
func onOpen(response http.ResponseWriter, request *http.Request) {
|
||||||
|
// gorilla ws will give a 403 on a cross origin request, so we silence its complaints
|
||||||
|
// This happens with electron because it's sending an origin of 'file://' instead of 'localhost:8080'
|
||||||
|
upgrader.CheckOrigin = func(r *http.Request) bool { return true }
|
||||||
|
|
||||||
|
// upgrade to websocket protocol over http
|
||||||
|
log.Printf("Establishing the websocket")
|
||||||
|
wsConn, err := upgrader.Upgrade(response, request, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Could not open websocket connection from: %s\n", request.Host)
|
||||||
|
http.Error(response, "Could not open websocket connection", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
ws = wsConn
|
||||||
|
log.Printf("WebSocket established with %s\n", ws.RemoteAddr().String())
|
||||||
|
|
||||||
|
// send any initialization alerts to UI and clear the queue
|
||||||
|
for len(Alerts) > 0 {
|
||||||
|
sendAlertMessage(Alerts[0])
|
||||||
|
Alerts[0] = configs.WsMessage{}
|
||||||
|
Alerts = Alerts[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
go onMessage()
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle messaging to the client
|
||||||
|
func onMessage() {
|
||||||
|
// just in case clean up the websocket
|
||||||
|
defer onClose()
|
||||||
|
|
||||||
|
for {
|
||||||
|
var request configs.WsMessage
|
||||||
|
err := ws.ReadJSON(&request)
|
||||||
|
if err != nil {
|
||||||
|
onError(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// look through the function map to find the type to handle the request
|
||||||
|
if reqType, ok := functionMap[request.Type]; ok {
|
||||||
|
// the function map may have a component (function) to process the request
|
||||||
|
if component, ok := reqType[request.Component]; ok {
|
||||||
|
// get the response and tag the timestamp so it's not repeated across all functions
|
||||||
|
response := component(request)
|
||||||
|
response.Timestamp = time.Now().UnixNano() / 1000000
|
||||||
|
if err = ws.WriteJSON(response); err != nil {
|
||||||
|
onError(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err = ws.WriteJSON(requestErrorHelper(fmt.Sprintf("Requested component: %s, not found",
|
||||||
|
request.Component), request)); err != nil {
|
||||||
|
onError(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
log.Printf("Requested component: %s, not found\n", request.Component)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err = ws.WriteJSON(requestErrorHelper(fmt.Sprintf("Requested type: %s, not found",
|
||||||
|
request.Type), request)); err != nil {
|
||||||
|
onError(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
log.Printf("Requested type: %s, not found\n", request.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// common websocket close with logging
|
||||||
|
func onClose() {
|
||||||
|
log.Printf("Closing websocket")
|
||||||
|
// ws.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// common websocket error handling with logging
|
||||||
|
func onError(err error) {
|
||||||
|
log.Printf("Error receiving / sending message: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The keepalive response including a timestamp from the server
|
||||||
|
// The UI will occasionally ping the server due to the websocket default timeout
|
||||||
|
func keepaliveReply(configs.WsMessage) configs.WsMessage {
|
||||||
|
return configs.WsMessage{
|
||||||
|
Type: configs.AirshipUI,
|
||||||
|
Component: configs.Keepalive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// formats an error response in the way that we're expecting on the UI
|
||||||
|
func requestErrorHelper(err string, request configs.WsMessage) configs.WsMessage {
|
||||||
|
return configs.WsMessage{
|
||||||
|
Type: request.Type,
|
||||||
|
Component: request.Component,
|
||||||
|
Timestamp: time.Now().UnixNano() / 1000000,
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is generated on the onOpen event and sends the information the UI needs to startup
|
||||||
|
func clientInit(configs.WsMessage) configs.WsMessage {
|
||||||
|
// if no auth method is supplied start with minimal functionality
|
||||||
|
if configs.UIConfig.AuthMethod == nil {
|
||||||
|
isAuthenticated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return configs.WsMessage{
|
||||||
|
Type: configs.AirshipUI,
|
||||||
|
Component: configs.Initialize,
|
||||||
|
IsAuthenticated: isAuthenticated,
|
||||||
|
Dashboards: configs.UIConfig.Clusters,
|
||||||
|
Plugins: configs.UIConfig.Plugins,
|
||||||
|
Authentication: configs.UIConfig.AuthMethod,
|
||||||
|
}
|
||||||
|
}
|
242
internal/webservice/websocket_test.go
Executable file
242
internal/webservice/websocket_test.go
Executable file
@ -0,0 +1,242 @@
|
|||||||
|
/*
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package webservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"opendev.org/airship/airshipui/internal/configs"
|
||||||
|
"opendev.org/airship/airshipui/internal/integrations/ctl"
|
||||||
|
"opendev.org/airship/airshipui/testutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestClientInit(t *testing.T) {
|
||||||
|
client, err := NewTestClient()
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
// simulate config provided by airshipui.json
|
||||||
|
configs.UIConfig = testutil.DummyCompleteConfig()
|
||||||
|
|
||||||
|
// get server response to "initialize" message from client
|
||||||
|
response, err := getResponse(client, initialize)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := configs.WsMessage{
|
||||||
|
Type: configs.AirshipUI,
|
||||||
|
Component: configs.Initialize,
|
||||||
|
IsAuthenticated: true,
|
||||||
|
Dashboards: []configs.Cluster{
|
||||||
|
testutil.DummyClusterConfig(),
|
||||||
|
},
|
||||||
|
Plugins: []configs.Plugin{
|
||||||
|
testutil.DummyPluginWithDashboardConfig(),
|
||||||
|
testutil.DummyPluginNoDashboard(),
|
||||||
|
},
|
||||||
|
Authentication: testutil.DummyAuthMethodConfig(),
|
||||||
|
// don't fail on timestamp diff
|
||||||
|
Timestamp: response.Timestamp,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, expected, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClientInitNoAuth(t *testing.T) {
|
||||||
|
client, err := NewTestClient()
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
// simulate config provided by airshipui.json
|
||||||
|
configs.UIConfig = testutil.DummyConfigNoAuth()
|
||||||
|
|
||||||
|
isAuthenticated = false
|
||||||
|
|
||||||
|
response, err := getResponse(client, initialize)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := configs.WsMessage{
|
||||||
|
Type: configs.AirshipUI,
|
||||||
|
Component: configs.Initialize,
|
||||||
|
// isAuthenticated should now be true in response
|
||||||
|
IsAuthenticated: true,
|
||||||
|
Dashboards: []configs.Cluster{
|
||||||
|
testutil.DummyClusterConfig(),
|
||||||
|
},
|
||||||
|
Plugins: []configs.Plugin{
|
||||||
|
testutil.DummyPluginWithDashboardConfig(),
|
||||||
|
testutil.DummyPluginNoDashboard(),
|
||||||
|
},
|
||||||
|
// don't fail on timestamp diff
|
||||||
|
Timestamp: response.Timestamp,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, expected, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeepalive(t *testing.T) {
|
||||||
|
client, err := NewTestClient()
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
// get server response to "keepalive" message from client
|
||||||
|
response, err := getResponse(client, keepalive)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := configs.WsMessage{
|
||||||
|
Type: configs.AirshipUI,
|
||||||
|
Component: configs.Keepalive,
|
||||||
|
// don't fail on timestamp diff
|
||||||
|
Timestamp: response.Timestamp,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, expected, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnknownType(t *testing.T) {
|
||||||
|
client, err := NewTestClient()
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
response, err := getResponse(client, unknownType)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := configs.WsMessage{
|
||||||
|
Type: "fake_type",
|
||||||
|
Component: configs.Initialize,
|
||||||
|
// don't fail on timestamp diff
|
||||||
|
Timestamp: response.Timestamp,
|
||||||
|
Error: "Requested type: fake_type, not found",
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, expected, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnknownComponent(t *testing.T) {
|
||||||
|
client, err := NewTestClient()
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
response, err := getResponse(client, unknownComponent)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := configs.WsMessage{
|
||||||
|
Type: configs.AirshipUI,
|
||||||
|
Component: "fake_component",
|
||||||
|
// don't fail on timestamp diff
|
||||||
|
Timestamp: response.Timestamp,
|
||||||
|
Error: "Requested component: fake_component, not found",
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, expected, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleDocumentRequest(t *testing.T) {
|
||||||
|
client, err := NewTestClient()
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
expectedHTML, err := ctl.GetDocumentHTML()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
response, err := getResponse(client, document)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := configs.WsMessage{
|
||||||
|
Type: configs.AirshipCTL,
|
||||||
|
Component: configs.Document,
|
||||||
|
SubComponent: configs.GetDefaults,
|
||||||
|
HTML: expectedHTML,
|
||||||
|
// don't fail on timestamp diff
|
||||||
|
Timestamp: response.Timestamp,
|
||||||
|
}
|
||||||
|
|
||||||
|
// the non typed interface requires us to break up the checking otherwise the 2 will never be equal
|
||||||
|
assert.Equal(t, expected.Type, response.Type)
|
||||||
|
assert.Equal(t, expected.Component, response.Component)
|
||||||
|
assert.Equal(t, expected.SubComponent, response.SubComponent)
|
||||||
|
assert.Equal(t, expected.HTML, response.HTML)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleBaremetalRequest(t *testing.T) {
|
||||||
|
client, err := NewTestClient()
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
expectedHTML, err := ctl.GetBaremetalHTML()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
response, err := getResponse(client, baremetal)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := configs.WsMessage{
|
||||||
|
Type: configs.AirshipCTL,
|
||||||
|
Component: configs.Baremetal,
|
||||||
|
SubComponent: configs.GetDefaults,
|
||||||
|
HTML: expectedHTML,
|
||||||
|
// don't fail on timestamp diff
|
||||||
|
Timestamp: response.Timestamp,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, expected, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleConfigRequest(t *testing.T) {
|
||||||
|
client, err := NewTestClient()
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
response, err := getResponse(client, config)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := configs.WsMessage{
|
||||||
|
Type: configs.AirshipCTL,
|
||||||
|
Component: configs.CTLConfig,
|
||||||
|
SubComponent: configs.GetDefaults,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, expected.Type, response.Type)
|
||||||
|
assert.Equal(t, expected.Component, response.Component)
|
||||||
|
assert.Equal(t, expected.SubComponent, response.SubComponent)
|
||||||
|
|
||||||
|
// NOTE(mfuller): integrations/ctl 'client' gets initialized
|
||||||
|
// *before* any env vars can be set here in tests, so client
|
||||||
|
// will always be initialized with default config file locations.
|
||||||
|
// Client is not exported, so we can't set it directly here. We'll
|
||||||
|
// simply make sure there's no Error value and that HTML has
|
||||||
|
// len > 0. Full testing of this response is covered in the
|
||||||
|
// integrations/ctl tests.
|
||||||
|
|
||||||
|
assert.Len(t, response.Error, 0)
|
||||||
|
assert.Greater(t, len(response.HTML), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getResponse(client *websocket.Conn, message string) (configs.WsMessage, error) {
|
||||||
|
err := client.WriteJSON(json.RawMessage(message))
|
||||||
|
if err != nil {
|
||||||
|
return configs.WsMessage{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var response configs.WsMessage
|
||||||
|
err = client.ReadJSON(&response)
|
||||||
|
if err != nil {
|
||||||
|
return configs.WsMessage{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
@ -63,13 +63,13 @@ function handleMessages(message) {
|
|||||||
switch(json["type"]) {
|
switch(json["type"]) {
|
||||||
case "alert": showDismissableAlert(json["component"], json["message"], json["fade"]); break;
|
case "alert": showDismissableAlert(json["component"], json["message"], json["fade"]); break;
|
||||||
case "airshipctl": handleCTLMessages(json); break;
|
case "airshipctl": handleCTLMessages(json); break;
|
||||||
case "electron": hanldleElectronMessages(json); break;
|
case "airshipui": hanldleAirshipUIMessages(json); break;
|
||||||
default: console.log("Received message: " + json["type"]); break;
|
default: console.log("Received message: " + json["type"]); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is a helper function for electron / base app specific messages
|
// this is a helper function for airshipui / base app specific messages
|
||||||
function hanldleElectronMessages(json) {
|
function hanldleAirshipUIMessages(json) {
|
||||||
if (json["component"] === "initialize") {
|
if (json["component"] === "initialize") {
|
||||||
if (!json["isAuthenticated"]) {
|
if (!json["isAuthenticated"]) {
|
||||||
authenticate(json["authentication"]);
|
authenticate(json["authentication"]);
|
||||||
@ -99,7 +99,7 @@ function handleCTLMessages(json) {
|
|||||||
|
|
||||||
function open() {
|
function open() {
|
||||||
console.log("Websocket established");
|
console.log("Websocket established");
|
||||||
var json = { "type": "electron", "component": "initialize" };
|
var json = { "type": "airshipui", "component": "initialize" };
|
||||||
ws.send(JSON.stringify(json));
|
ws.send(JSON.stringify(json));
|
||||||
// start up the keepalive so the websocket stays open
|
// start up the keepalive so the websocket stays open
|
||||||
keepAlive();
|
keepAlive();
|
||||||
@ -137,7 +137,7 @@ function keepAlive() {
|
|||||||
// clear the previously set timeout
|
// clear the previously set timeout
|
||||||
window.clearTimeout(timeout);
|
window.clearTimeout(timeout);
|
||||||
window.clearInterval(timeout);
|
window.clearInterval(timeout);
|
||||||
var json = { "type": "electron", "component": "keepalive" };
|
var json = { "type": "airshipui", "component": "keepalive" };
|
||||||
ws.send(JSON.stringify(json));
|
ws.send(JSON.stringify(json));
|
||||||
timeout = window.setTimeout(keepAlive, 60000);
|
timeout = window.setTimeout(keepAlive, 60000);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user