diff --git a/internal/integrations/ctl/airshipctl.go b/internal/integrations/ctl/airshipctl.go index 9d55dee..fca749c 100755 --- a/internal/integrations/ctl/airshipctl.go +++ b/internal/integrations/ctl/airshipctl.go @@ -19,8 +19,12 @@ import ( "opendev.org/airship/airshipctl/pkg/environment" "opendev.org/airship/airshipctl/pkg/version" + "opendev.org/airship/airshipui/internal/configs" ) +// maintain the state of a potentially long running process +var runningRequests map[configs.WsSubComponentType]bool = make(map[configs.WsSubComponentType]bool) + // ctlPage struct is used for templated HTML type ctlPage struct { ClusterRows string @@ -28,6 +32,8 @@ type ctlPage struct { CredentialRows string Title string Version string + Disabled string + ButtonText string } // client provides a library of functions that enable external programs (e.g. Airship UI) to perform airshipctl diff --git a/internal/integrations/ctl/baremetal.go b/internal/integrations/ctl/baremetal.go index 11bfdb6..8d92659 100755 --- a/internal/integrations/ctl/baremetal.go +++ b/internal/integrations/ctl/baremetal.go @@ -21,6 +21,7 @@ import ( ) // HandleBaremetalRequest will flop between requests so we don't have to have them all mapped as function calls +// This will wait for the sub component to complete before responding. The assumption is this is an async request func HandleBaremetalRequest(request configs.WsMessage) configs.WsMessage { response := configs.WsMessage{ Type: configs.AirshipCTL, @@ -30,13 +31,16 @@ func HandleBaremetalRequest(request configs.WsMessage) configs.WsMessage { var err error var message string - switch request.SubComponent { + subComponent := request.SubComponent + switch subComponent { case configs.GetDefaults: response.HTML, err = getBaremetalHTML() - case configs.DocPull: - message, err = c.docPull() case configs.GenerateISO: + // since this is long running cache it up + runningRequests[subComponent] = true message, err = c.generateIso() + // now that we're done forget we did anything + delete(runningRequests, subComponent) default: err = fmt.Errorf("Subcomponent %s not found", request.SubComponent) } @@ -61,8 +65,16 @@ func (c *client) generateIso() (string, error) { } func getBaremetalHTML() (string, error) { - return getHTML("./internal/integrations/ctl/templates/baremetal.html", ctlPage{ - Title: "Baremetal", - Version: getAirshipCTLVersion(), - }) + p := ctlPage{ + Title: "Baremetal", + Version: getAirshipCTLVersion(), + ButtonText: "Generate ISO", + } + + if _, ok := runningRequests[configs.GenerateISO]; ok { + p.Disabled = "disabled" + p.ButtonText = "In Progress" + } + + return getHTML("./internal/integrations/ctl/templates/baremetal.html", p) } diff --git a/internal/integrations/ctl/templates/baremetal.html b/internal/integrations/ctl/templates/baremetal.html index 897d184..71c4c33 100755 --- a/internal/integrations/ctl/templates/baremetal.html +++ b/internal/integrations/ctl/templates/baremetal.html @@ -2,4 +2,4 @@

Version: {{.Version}}

Generate ISO

- + diff --git a/internal/webservice/server.go b/internal/webservice/server.go index db4ee92..e5b94da 100755 --- a/internal/webservice/server.go +++ b/internal/webservice/server.go @@ -31,7 +31,7 @@ var upgrader = websocket.Upgrader{ } // this is a way to allow for arbitrary messages to be processed by the backend -// most likely we will need to have sub components register with the system +// 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{ configs.Electron: { diff --git a/web/js/airshipctl/airshipctl.js b/web/js/airshipctl/airshipctl.js index 59e742b..0d279bf 100755 --- a/web/js/airshipctl/airshipctl.js +++ b/web/js/airshipctl/airshipctl.js @@ -26,10 +26,11 @@ document.addEventListener("DOMContentLoaded", () => { // Displays the alerts from the backend function handleCTLResponse(json) { // eslint-disable-line no-unused-vars + let message = json["type"] + " " + json["component"] + " " + json["subComponent"] + " "; if (json.hasOwnProperty("error")) { - showDismissableAlert("danger", json["error"], false); + showDismissableAlert("danger", message + json["error"], false); } else { - showDismissableAlert("info", json["message"], false); + showDismissableAlert("info", message + json["message"], true); } } diff --git a/web/js/common.js b/web/js/common.js index 0f7816d..909db98 100755 --- a/web/js/common.js +++ b/web/js/common.js @@ -34,20 +34,22 @@ if (document.addEventListener) { // add dashboard links to Dropdown if present in $HOME/.airship/airshipui.json function addServiceDashboards(json) { // eslint-disable-line no-unused-vars - for (let i = 0; i < json.length; i++) { - let cluster = json[i]; - for (let j = 0; j < cluster.namespaces.length; j++) { - let namespace = cluster.namespaces[j]; - for (let k = 0; k < namespace.dashboards.length; k++) { - let dash = namespace.dashboards[k]; - let fqdn = ""; - if (dash.fqdn === undefined) { - fqdn = `${dash.hostname}.${cluster.namespaces[j].name}.${cluster.baseFqdn}` - } else { - ({ fqdn } = dash.fqdn); + if (json !== undefined) { + for (let i = 0; i < json.length; i++) { + let cluster = json[i]; + for (let j = 0; j < cluster.namespaces.length; j++) { + let namespace = cluster.namespaces[j]; + for (let k = 0; k < namespace.dashboards.length; k++) { + let dash = namespace.dashboards[k]; + let fqdn = ""; + if (dash.fqdn === undefined) { + fqdn = `${dash.hostname}.${cluster.namespaces[j].name}.${cluster.baseFqdn}` + } else { + ({ fqdn } = dash.fqdn); + } + let url = `${dash.protocol}://${fqdn}:${dash.port}/${dash.path || ""}`; + addDashboard("DashDropdown", dash.name, url) } - let url = `${dash.protocol}://${fqdn}:${dash.port}/${dash.path || ""}`; - addDashboard("DashDropdown", dash.name, url) } } } @@ -56,11 +58,13 @@ function addServiceDashboards(json) { // eslint-disable-line no-unused-vars // if any plugins (external executables) have a corresponding web dashboard defined, // add them to the dropdown function addPluginDashboards(json) { // eslint-disable-line no-unused-vars - for (let i = 0; i < json.length; i++) { - if (json[i].executable.autoStart && json[i].dashboard.fqdn !== undefined) { - let dash = json[i].dashboard; - let url = `${dash.protocol}://${dash.fqdn}:${dash.port}/${dash.path || ""}`; - addDashboard("PluginDropdown", json[i].name, url); + if (json !== undefined) { + for (let i = 0; i < json.length; i++) { + if (json[i].executable.autoStart && json[i].dashboard.fqdn !== undefined) { + let dash = json[i].dashboard; + let url = `${dash.protocol}://${dash.fqdn}:${dash.port}/${dash.path || ""}`; + addDashboard("PluginDropdown", json[i].name, url); + } } } } @@ -91,12 +95,6 @@ function authenticate(json) { // eslint-disable-line no-unused-vars view.src = json["url"]; } -function removeElement(id) { // eslint-disable-line no-unused-vars - if (document.contains(document.getElementById(id))) { - document.getElementById(id).remove(); - } -} - // show a dismissable alert in the UI // if 'fade' is set to true, the alert will fade out and disappear after 5s function showDismissableAlert(alertLevel, msg, fade) { // eslint-disable-line no-unused-vars diff --git a/web/js/websocket.js b/web/js/websocket.js index 805b0f4..9e581d1 100755 --- a/web/js/websocket.js +++ b/web/js/websocket.js @@ -75,11 +75,11 @@ function hanldleElectronMessages(json) { authenticate(json["authentication"]); } else { authComplete(); - } - if (json["plugins"] !== null) { + } + if (json.hasOwnProperty("plugins")) { addPluginDashboards(json["plugins"]); } - if (json["dashboards"] !== null) { + if (json.hasOwnProperty("dashboards")) { addServiceDashboards(json["dashboards"]); } } else if (json["component"] === "authcomplete") {