Commit a70cdde9 authored by Mohamed, Fawzi Roberto (fawzi)'s avatar Mohamed, Fawzi Roberto (fawzi)
Browse files

html templates

parent db06cd93
const config = require('config')
const stringify = require('json-stringify-safe');
const http = require('http');
const fs = require('fs');
......@@ -6,8 +7,6 @@ const yaml = require('js-yaml')
const k8 = require('./kubernetes');
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) {
fs.access(path, fs.constants.F_OK | fs.constants.R_OK, (err) => {
if(err){
......@@ -41,6 +40,7 @@ function getOrCreatePod(podName, repl, shouldCreate, next) {
k8.ns(config.k8component.namespace).pod.get(podName, function(err, result) {
if(err) {
if (shouldCreate) {
logger.debug(`creating ${podName}`)
components.templateForImage(repl, function(err, template, repl) {
if(err) {
logger.error(`Cannot start pod ${podName}, error in template generation: ${stringify(err)}`);
......@@ -50,7 +50,7 @@ function getOrCreatePod(podName, repl, shouldCreate, next) {
const templateValue = yaml.safeLoad(template, 'utf8')
k8.ns(config.k8component.namespace).pod.post({ body: templateValue}, function(err, res2){
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)
} else {
logger.info(`Created pod ${podName}: ${stringify(res2)}`)
......@@ -108,22 +108,28 @@ function resolvePod(repl, next) {
resolveCache.set(podName, res)
next(null, res)
} else {
let secondsSinceCreation = (Date.now() - Date.parse(pod.metadata.creationTimestamp))/ 1000.0
const err = {
error: "not ready",
msg: "pod not yet ready",
status: pod.status,
host: podIp,
port: portNr
port: portNr,
pod: pod,
secondsSinceCreation: secondsSinceCreation
}
next(err, null)
}
} else {
let secondsSinceCreation = (Date.now() - Date.parse(pod.metadata.creationTimestamp))/ 1000.0
const err = {
error: "no ip",
msg: "ip not yet available",
status: pod.status,
host: podIp,
port: portNr
port: portNr,
pod: pod,
secondsSinceCreation: secondsSinceCreation
}
next(err, null)
}
......@@ -147,22 +153,39 @@ ProxyRouter.prototype.lookup = function(req, res, userID, isWebsocket, path, nex
components.cachedReplacements(req, function(err, repl) {
//logger.debug(`replacements available after ${(Date.now()-start)/1000.0}s`)
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 {
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.error === 'no ip' && err.status && err.status.phase === 'Pending' ||
err.error === 'not ready') {
logger.warn(`pod ${repl.podName} ${err.error} ${stringify(err)}`)
res.send(reloadMsg)
if ((err.error === 'no ip' || err.error === 'not ready') &&
err.status && (err.status.phase === 'Pending' || err.status.phase === 'Running')) {
let error_detail = ''
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 {
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)}`)
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 {
// logger.debug(`Resolved to ${stringify(target)} after ${(Date.now()-start)/1000.0}s`)
next(target);
}
})
......
config = require('config')
const config = require('config')
const handlebars = require('handlebars');
const fs = require('fs');
const path = require('path');
......@@ -10,6 +10,7 @@ const url = require('url');
const compact_sha = require('./compact-sha')
const logger = require('./logger')
const stringify = require('json-stringify-safe')
const yaml = require('js-yaml')
var baseRepl = {
baseDir: baseDir,
......@@ -18,7 +19,15 @@ var baseRepl = {
};
const br = config.app.baseReplacements
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
function templatize(str) {
......@@ -110,7 +119,7 @@ function evalTemplate(templatePath, extraRepl, next) {
// Immediately returns an html describing the error
function getHtmlErrorTemplate(err, context = '') {
let error = '', error_msg = '', error_detail = ''
if (!err) {
if (err) {
try {
error_detail = stringify(err, null, 2)
error = err.error || ''
......@@ -134,25 +143,40 @@ function getHtmlErrorTemplate(err, context = '') {
</pre>
</body>
</html>`
} else {
return ''
}
}
// 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
function evalHtmlTemplate(htmlPath, repl, next, layout = null, context = '') {
const layout = repl.layout || "defaultTemplate.html"
evalTemplate("html/"+htmlPath, repl, function (err, template){
function evalHtmlTemplate(htmlPath, repl, next, { context = '' } = {} ) {
//logger.debug(`entering evalHtmlTemplate(${stringify(htmlPath)}, ${stringify(repl)},...)`)
evalTemplate('html/'+htmlPath, repl, function (err, template){
if (err) {
logger.warn(`evalTemplate ${htmlPath} returning error ${stringify(err)}`)
next(err, getHtmlErrorTemplate(err, context))
} else {
const repl2 = Object.assign({title: htmlPath, head: ''}, repl, { body: template })
evalHtmlTemplate("html/"+layout, repl2, function(err,res){
if (err) {
next(err, getHtmlErrorTemplate(err, context))
} else {
next(nil, res)
}
})
let extraRepl = {}
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) {
next(err, getHtmlErrorTemplate(err, context))
} else {
next(null, res)
}
})
} else {
next(null, templateBody)
}
}
})
}
......@@ -276,5 +300,7 @@ module.exports = {
cachedReplacements: cachedReplacements,
podNameForRepl: podNameForRepl,
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>
<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>
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment