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
No related branches found
No related tags found
No related merge requests found
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,
......@@ -20,6 +21,14 @@ const br = config.app.baseReplacements
for (k in br)
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) {
return handlebars.compile(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){
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(nil, res)
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>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment