Skip to content
Snippets Groups Projects
Commit a70cdde9 authored by Mohamed, Fawzi Roberto (fawzi)'s avatar Mohamed, Fawzi Roberto (fawzi)
Browse files

html templates

parent db06cd93
Branches
Tags
No related merge requests found
const config = require('config')
const stringify = require('json-stringify-safe'); const stringify = require('json-stringify-safe');
const http = require('http'); const http = require('http');
const fs = require('fs'); const fs = require('fs');
...@@ -6,8 +7,6 @@ const yaml = require('js-yaml') ...@@ -6,8 +7,6 @@ const yaml = require('js-yaml')
const k8 = require('./kubernetes'); const k8 = require('./kubernetes');
const logger = require('./logger') const logger = require('./logger')
const reloadMsg = `<html><head><title>Starting up!</title><meta http-equiv="refresh" content="${config.app.pageReloadTime}" ></head><body><h3>Please wait while we start a container for you!</h3><p>You might need to refresh manually (F5)...</body></html>`;
function guaranteeDir(path, next) { function guaranteeDir(path, next) {
fs.access(path, fs.constants.F_OK | fs.constants.R_OK, (err) => { fs.access(path, fs.constants.F_OK | fs.constants.R_OK, (err) => {
if(err){ if(err){
...@@ -41,6 +40,7 @@ function getOrCreatePod(podName, repl, shouldCreate, next) { ...@@ -41,6 +40,7 @@ function getOrCreatePod(podName, repl, shouldCreate, next) {
k8.ns(config.k8component.namespace).pod.get(podName, function(err, result) { k8.ns(config.k8component.namespace).pod.get(podName, function(err, result) {
if(err) { if(err) {
if (shouldCreate) { if (shouldCreate) {
logger.debug(`creating ${podName}`)
components.templateForImage(repl, function(err, template, repl) { components.templateForImage(repl, function(err, template, repl) {
if(err) { if(err) {
logger.error(`Cannot start pod ${podName}, error in template generation: ${stringify(err)}`); logger.error(`Cannot start pod ${podName}, error in template generation: ${stringify(err)}`);
...@@ -50,7 +50,7 @@ function getOrCreatePod(podName, repl, shouldCreate, next) { ...@@ -50,7 +50,7 @@ function getOrCreatePod(podName, repl, shouldCreate, next) {
const templateValue = yaml.safeLoad(template, 'utf8') const templateValue = yaml.safeLoad(template, 'utf8')
k8.ns(config.k8component.namespace).pod.post({ body: templateValue}, function(err, res2){ k8.ns(config.k8component.namespace).pod.post({ body: templateValue}, function(err, res2){
if(err) { if(err) {
logger.error(`Cannot start pod ${podName}, error: ${stringify(err)}, \n====\ntemplate was ${template}\n====\nexpanded to\n${templateValue}\n====`); logger.error(`Cannot start pod ${podName}, error: ${stringify(err)}, \n====\ntemplate was ${template}\n====`);
next(err, null) next(err, null)
} else { } else {
logger.info(`Created pod ${podName}: ${stringify(res2)}`) logger.info(`Created pod ${podName}: ${stringify(res2)}`)
...@@ -108,22 +108,28 @@ function resolvePod(repl, next) { ...@@ -108,22 +108,28 @@ function resolvePod(repl, next) {
resolveCache.set(podName, res) resolveCache.set(podName, res)
next(null, res) next(null, res)
} else { } else {
let secondsSinceCreation = (Date.now() - Date.parse(pod.metadata.creationTimestamp))/ 1000.0
const err = { const err = {
error: "not ready", error: "not ready",
msg: "pod not yet ready", msg: "pod not yet ready",
status: pod.status, status: pod.status,
host: podIp, host: podIp,
port: portNr port: portNr,
pod: pod,
secondsSinceCreation: secondsSinceCreation
} }
next(err, null) next(err, null)
} }
} else { } else {
let secondsSinceCreation = (Date.now() - Date.parse(pod.metadata.creationTimestamp))/ 1000.0
const err = { const err = {
error: "no ip", error: "no ip",
msg: "ip not yet available", msg: "ip not yet available",
status: pod.status, status: pod.status,
host: podIp, host: podIp,
port: portNr port: portNr,
pod: pod,
secondsSinceCreation: secondsSinceCreation
} }
next(err, null) next(err, null)
} }
...@@ -147,22 +153,39 @@ ProxyRouter.prototype.lookup = function(req, res, userID, isWebsocket, path, nex ...@@ -147,22 +153,39 @@ ProxyRouter.prototype.lookup = function(req, res, userID, isWebsocket, path, nex
components.cachedReplacements(req, function(err, repl) { components.cachedReplacements(req, function(err, repl) {
//logger.debug(`replacements available after ${(Date.now()-start)/1000.0}s`) //logger.debug(`replacements available after ${(Date.now()-start)/1000.0}s`)
if (err) { if (err) {
logger.error(`No replacements: lookup without visiting the entry point ${config.k8component.entryPoint.path} (${stringify(err)})`) logger.error(`no replacements for ${userID} in %{path}`)
res.send(500, components.getHtmlErrorTemplate({
error:"No replacements",
msg: `lookup without visiting the entry point ${config.k8component.entryPoint.path} (${stringify(err)})`
}))
} else { } else {
resolvePod(repl, function (err, target) { resolvePod(repl, function (err, target) {
//logger.debug(`target available after ${(Date.now()-start)/1000.0}s`) //logger.debug(`target available after ${(Date.now()-start)/1000.0}s, err: ${stringify(err)} target: ${stringify(target)}`)
if (err) { if (err) {
if (err.error === 'no ip' && err.status && err.status.phase === 'Pending' || if ((err.error === 'no ip' || err.error === 'not ready') &&
err.error === 'not ready') { err.status && (err.status.phase === 'Pending' || err.status.phase === 'Running')) {
logger.warn(`pod ${repl.podName} ${err.error} ${stringify(err)}`) let error_detail = ''
res.send(reloadMsg) if (!err.secondsSinceCreation || err.secondsSinceCreation > 15)
error_detail = stringify(err, null, 2)
let repl = {
refreshEachS: config.app.pageReloadTime,
error_detail: error_detail
}
components.evalHtmlTemplate(
'reloadMsg.html', repl,
function(err, pageHtml) {
if (res && res.send)
res.send(pageHtml)
})
return;
} else { } else {
const errorMsg = `<html><head><title>Error starting Container!</title><meta http-equiv="refresh"<body><h3>Error ${err.error} while trying to start a container for you!</h3><p>${err.msg}</p><pre>${stringify(err, null, 2 )}</pre></body></html>`;
logger.error(`error starting container ${repl.podName}: ${stringify(err)}`) logger.error(`error starting container ${repl.podName}: ${stringify(err)}`)
res.send(500, errorMsg) let errorHtml = components.getHtmlErrorTemplate(err, "Error starting container")
logger.error(`errorHtml: ${stringify(errorHtml)}`)
if (res && res.status && res.send)
res.status(500).send(errorHtml)
} }
} else { } else {
// logger.debug(`Resolved to ${stringify(target)} after ${(Date.now()-start)/1000.0}s`)
next(target); next(target);
} }
}) })
... ...
......
config = require('config') const config = require('config')
const handlebars = require('handlebars'); const handlebars = require('handlebars');
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
...@@ -10,6 +10,7 @@ const url = require('url'); ...@@ -10,6 +10,7 @@ const url = require('url');
const compact_sha = require('./compact-sha') const compact_sha = require('./compact-sha')
const logger = require('./logger') const logger = require('./logger')
const stringify = require('json-stringify-safe') const stringify = require('json-stringify-safe')
const yaml = require('js-yaml')
var baseRepl = { var baseRepl = {
baseDir: baseDir, baseDir: baseDir,
...@@ -20,6 +21,14 @@ const br = config.app.baseReplacements ...@@ -20,6 +21,14 @@ const br = config.app.baseReplacements
for (k in br) for (k in br)
baseRepl[k] = br[k]; baseRepl[k] = br[k];
handlebars.registerHelper('json', function(object){
return new Handlebars.SafeString(stringify(object));
});
handlebars.registerHelper('prettyJson', function(object){
return stringify(object, null, 2);
});
// Create a template from the given string // Create a template from the given string
function templatize(str) { function templatize(str) {
return handlebars.compile(str) return handlebars.compile(str)
...@@ -110,7 +119,7 @@ function evalTemplate(templatePath, extraRepl, next) { ...@@ -110,7 +119,7 @@ function evalTemplate(templatePath, extraRepl, next) {
// Immediately returns an html describing the error // Immediately returns an html describing the error
function getHtmlErrorTemplate(err, context = '') { function getHtmlErrorTemplate(err, context = '') {
let error = '', error_msg = '', error_detail = '' let error = '', error_msg = '', error_detail = ''
if (!err) { if (err) {
try { try {
error_detail = stringify(err, null, 2) error_detail = stringify(err, null, 2)
error = err.error || '' error = err.error || ''
...@@ -134,25 +143,40 @@ function getHtmlErrorTemplate(err, context = '') { ...@@ -134,25 +143,40 @@ function getHtmlErrorTemplate(err, context = '') {
</pre> </pre>
</body> </body>
</html>` </html>`
} else {
return ''
} }
} }
// Helper to evaluate a web page template (layout + content) // Helper to evaluate a web page template (layout + content)
// will *always* give an html as result (it there was an error it describe the error // will *always* give an html as result (it there was an error it describe the error
function evalHtmlTemplate(htmlPath, repl, next, layout = null, context = '') { function evalHtmlTemplate(htmlPath, repl, next, { context = '' } = {} ) {
const layout = repl.layout || "defaultTemplate.html" //logger.debug(`entering evalHtmlTemplate(${stringify(htmlPath)}, ${stringify(repl)},...)`)
evalTemplate("html/"+htmlPath, repl, function (err, template){ evalTemplate('html/'+htmlPath, repl, function (err, template){
if (err) { if (err) {
logger.warn(`evalTemplate ${htmlPath} returning error ${stringify(err)}`)
next(err, getHtmlErrorTemplate(err, context)) next(err, getHtmlErrorTemplate(err, context))
} else { } else {
const repl2 = Object.assign({title: htmlPath, head: ''}, repl, { body: template }) let extraRepl = {}
evalHtmlTemplate("html/"+layout, repl2, function(err,res){ let templateBody = template
let m = /\B---\B/.exec(template)
if (m) {
templateBody = template.slice(m.index + 3)
extraRepl = yaml.safeLoad(template.slice(0, m.index), 'utf8')
}
const repl2 = Object.assign({title: htmlPath, head: '', layout: "defaultLayout.html"}, extraRepl, repl, { body: templateBody })
const layout = repl2.layout
if (layout) {
evalTemplate("htmlLayout/"+layout, repl2, function(err,res){
if (err) { if (err) {
next(err, getHtmlErrorTemplate(err, context)) next(err, getHtmlErrorTemplate(err, context))
} else { } else {
next(nil, res) next(null, res)
} }
}) })
} else {
next(null, templateBody)
}
} }
}) })
} }
...@@ -276,5 +300,7 @@ module.exports = { ...@@ -276,5 +300,7 @@ module.exports = {
cachedReplacements: cachedReplacements, cachedReplacements: cachedReplacements,
podNameForRepl: podNameForRepl, podNameForRepl: podNameForRepl,
infoForPodName: infoForPodName, infoForPodName: infoForPodName,
templateForImage: templateForImage templateForImage: templateForImage,
getHtmlErrorTemplate: getHtmlErrorTemplate,
evalHtmlTemplate: evalHtmlTemplate
} }
<!doctype html>
<html>
<head>
<title>{{title}}</title>
<meta charset="utf-8" />
{{head}}
</head>
<body>
{{body}}
</body>
</html>
title: "Starting up!"
head: "<meta http-equiv=\"refresh\" content=\"{{refreshEachS}}\" >"
---
<h3>Please wait while we start a container for you!</h3> <h3>Please wait while we start a container for you!</h3>
<p>You might need to refresh manually (F5)...</p>p> <p>You might need to refresh manually (F5)...</p>
<pre>
{{error_detail}}
</pre>
<!doctype html>
<html>
<head>
<title>{{{title}}}</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
{{{head}}}
</head>
<body>
{{{body}}}
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
</body>
</html>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment