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

refactor to have separate command execution

* function for pod resolution with error handling already done (guaranteeResolvePod)
* function for replacements with error handling already done (guaranteeCachedReplacements)
* separate comand-exec endpoint (currently a get, should be post?)
parent e2d0e415
......@@ -294,6 +294,23 @@ function cachedReplacements(req, next) {
}
}
// cached replacements replying with error if not available
function guaranteeCachedReplacements(req,res,next) {
cachedReplacements(req, function(err, repl) {
//logger.debug(`replacements available after ${(Date.now()-start)/1000.0}s`)
if (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 {
next(repl)
}
})
}
function templateForImage(repl, next) {
evalTemplate(repl['templatePath'], repl, next)
}
......@@ -307,6 +324,7 @@ module.exports = {
replacementsForUser: replacementsForUser,
selfUserName: selfUserName,
cachedReplacements: cachedReplacements,
guaranteeCachedReplacements: guaranteeCachedReplacements,
podNameForRepl: podNameForRepl,
infoForPodName: infoForPodName,
templateForImage: templateForImage,
......
......@@ -287,6 +287,43 @@ function resolvePod(repl, next) {
}
}
function guaranteeResolvePod(repl, res, next){
resolvePod(repl, function (err, target) {
if (err) {
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 if (err.error === 'too many containers') {
components.evalHtmlTemplate("maxContainers.html", {
pods: pods
}, function(err, errorHtml) {
repl.status(503).send(errorHtml)
})
} else {
logger.error(`error starting container ${repl.podName}: ${stringify(err)}`)
let errorHtml = components.getHtmlErrorTemplate(err, "Error starting container")
if (res && res.status && res.send)
res.status(500).send(errorHtml)
}
} else {
next(target);
}
})
}
function deletePod(podName, next) {
......@@ -346,6 +383,7 @@ module.exports = {
guaranteeDir: guaranteeDir,
guaranteeUserDir: guaranteeUserDir,
resolvePod: resolvePod,
guaranteeResolvePod: guaranteeResolvePod,
deletePod: deletePod,
getServiceInfo: getServiceInfo
}
......@@ -28,7 +28,7 @@ module.exports = function (app, redirect, config, proxyServer, proxyRouter, k8,
let cconf = config.k8component
let entryPath = components.templatize(cconf.entryPoint.path)(components.baseRepl)
logger.debug(`entryPoint: ${entryPath}`)
const commandsBase = components.templatize(cconf.commands.path)(components.baseRepl)
app.get(entryPath, ensureLoggedIn(loginUri), bodyParser.json(), bodyParser.urlencoded({extended:true}), function(req, res){
function isEmpty(obj) {
......@@ -61,7 +61,7 @@ module.exports = function (app, redirect, config, proxyServer, proxyRouter, k8,
req.session.replacements[cconf.image.imageType] = repl
const podName = components.podNameForRepl(repl)
k8D.getOrCreatePod(podName, repl, true, function (err, podInfo) {
let target = components.templatize(cconf.entryPoint.redirectTarget)(repl)
let target = `${commandsBase}/command-exec`
logger.info(`entryPoint got pod ${stringify(podInfo)}`)
if (err) {
if (err.error === 'too many containers') {
......@@ -107,29 +107,7 @@ module.exports = function (app, redirect, config, proxyServer, proxyRouter, k8,
}
}
if (podIp && ready && podInfo.status.phase == 'Running') { // we have a valid and running pod
if (cconf.execCommand) {
k8.api.v1.ns(cconf.namespace).pod(podName).exec.post({ qs: {
command: cconf.execCommand,
stdin: false,
stderr: true,
stdout: true,
tty: false
} }).then(function(value){
res.redirect(302, target);
}, function(err) {
logger.warn(`command ${stringify(execCommand)} on pod ${podName} failed ${stringify(err)}`)
components.evalHtmlTemplate("failedCommand.html", {
podName: podName,
command: stringify(cconf.execCommand),
error: err,
target: target
}, function(err, templateHtml) {
res.status(500).send(templateHtml)
})
})
} else {
res.redirect(302, target);
}
res.redirect(302, target);
} else if (podInfo.status.phase === 'Pending' || podInfo.status.phase === 'Running') {
let error_detail = ''
let secondsSinceCreation = (Date.now() - Date.parse(podInfo.metadata.creationTimestamp))/ 1000.0
......@@ -162,7 +140,53 @@ module.exports = function (app, redirect, config, proxyServer, proxyRouter, k8,
})
});
const commandsBase = components.templatize(cconf.commands.path)(components.baseRepl)
app.get(commandsBase + "/command-exec", ensureLoggedIn(loginUri), bodyParser.json(), function(req, res){
components.guaranteeCachedReplacements(req, res, function(repl) {
let targetTemplateStr = cconf.entryPoint.redirectTarget
let targetTemplate = components.templatize(targetTemplateStr)
k8D.guaranteeResolvePod(repl, res, function(podInfo){
if (cconf.entryPoint.execCommand) {
let cmd = cconf.entryPoint.execCommand.map(function (x) {
if (x.indexOf('{{') == -1)
return x
else
return components.templatize(x)(repl)
})
k8.api.v1.ns(cconf.namespace).pod(repl.podName).exec.post({ qs: {
command: cmd,
stdin: false,
stderr: true,
stdout: true,
tty: false
} }).then(function(value){
let newRepl = Object.create(repl)
newRepl.cmdOut = value.messages.filter(function(x){ return x.channel == "stdout" }).map(function(x){ return x.message }).join("")
newRepl.cmdOutTrimmed = newRepl.cmdOut.trim()
newRepl.cmdBody = value.body
newRepl.cmdBodyTrimmed = newRepl.cmdBody.trim()
targetTemplate(newRepl)
let target = targetTemplate(newRepl)
res.redirect(302, target);
}).catch(function(err) {
logger.warn(`command ${stringify(cmd)} on pod ${repl.podName} failed ${stringify(err)}`)
let target = targetTemplate(repl)
components.evalHtmlTemplate("failedCommand.html", {
podName: podName,
command: stringify(cmd),
error: err,
target: target
}, function(err, templateHtml) {
res.status(500).send(templateHtml)
})
})
} else {
let target = targetTemplate(repl)
res.redirect(302, target);
}
})
})
})
app.get(commandsBase + "/view-containers", ensureLoggedIn(loginUri), bodyParser.urlencoded({extended: true}), function(req, res){
let user = components.selfUserName(req)
......
......@@ -5,7 +5,7 @@ k8component: {
imageType: jupyter
imageSubtype: default1
subType: default1
image: "analytics-toolkit.nomad-coe.eu:5509/nomadlab/jupyter-image:0.999"
image: "analytics-toolkit.nomad-coe.eu:5509/nomadlab/jupyter-image:0.999.1"
port: 8888,
prefix: "/jupyter",
healthPath: "/jupyter"
......@@ -15,10 +15,9 @@ k8component: {
liveDelay: 15
livePeriod: 30
}
"entryPoint": {
"path": "/jupyter/entrypoint"
"uriRe": "/jupyter(/.*)",
"redirectTarget": "{{prefix}}/tree?",
"exclusiveStartPoint": false
},
entryPoint: {
redirectTarget: "{{prefix}}/notebooks/{{cmdOutTrimmed}}"
execCommand: ["/usr/src/localCopy.sh", "/data/private", "/data/private/external", "/data/shared", "{{path1}}" ]
exclusiveStartPoint: true
}
}
title: Login
---
<h1>Login</h1>
<form action="{{loginUri}}/callback" method="post">
<div>
<label>Username:</label>
<input type="text" name="username"/>
</div>
<div>
<label>Password:</label>
<input type="password" name="password"/>
</div>
<div>
<input type="submit" value="Log In"/>
</div>
</form>
......@@ -11,6 +11,7 @@ metadata:
spec:
imagePullSecrets:
- name: garching-kube
restartPolicy: Never
containers:
- image: "{{image}}"
name: "{{imageType}}"
......@@ -36,7 +37,7 @@ spec:
- mountPath: "/data/shared"
name: "shared-data-volume"
readOnly: true
- mountPath: "/data/shared/{{user}}"
- mountPath: "/data/private/myshared"
name: "my-shared-data-volume"
readinessProbe:
httpGet:
......
Markdown is supported
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