Commit fa53aa78 authored by Mathieu's avatar Mathieu

Fix pod list

parent 77f145e0
......@@ -32,7 +32,12 @@ object AdminPanel {
def admin() = {
implicit def userDataSeqToRows(userData: Seq[UserData]): Seq[ExpandableRow] = userData.map { u =>
buildExpandable(u.name, u.email, u.password, u.role, u.omVersion, u.lastAccess, podInfos.now.filter{_.userEmail == u.email}.headOption)
buildExpandable(u.name, u.email, u.password, u.role, u.omVersion, u.lastAccess, podInfos.now.filter {
_.userEmail == Some(u.email)
}.headOption, open.now.map {
_ == u.email
}.getOrElse(false))
}
......@@ -41,6 +46,7 @@ object AdminPanel {
lazy val rows: Var[Seq[ExpandableRow]] = Var(Seq())
lazy val open: Var[Option[String]] = Var(None)
lazy val podInfos: Var[Seq[PodInfo]] = Var(Seq())
......@@ -68,17 +74,18 @@ object AdminPanel {
}
def updatePodInfos =
Post[AdminApi].podInfos().call().foreach {pi=>
Post[AdminApi].podInfos().call().foreach { pi =>
println("PI " + pi)
podInfos() = pi
updateRows
}
def updatePodInfoTimer:Unit = {
setTimeout(5000) {
updatePodInfos
updatePodInfoTimer
}
}
// def updatePodInfoTimer: Unit = {
// setTimeout(10000) {
// updatePodInfos
// updatePodInfoTimer
// }
// }
def closeAll(except: ExpandableRow) = rows.now.filterNot {
......@@ -121,6 +128,10 @@ object AdminPanel {
TriggerCell(a(userName, onclick := { () =>
closeAll(expandableRow)
aVar() = !aVar.now
open() = {
if (aVar.now) Some(userEmail)
else None
}
})),
LabelCell(podInfo.map {
_.status
......@@ -136,13 +147,17 @@ object AdminPanel {
updateRows
updatePodInfos
updatePodInfoTimer
//updatePodInfoTimer
val addUserButton = button(btn_primary, "Add", onclick := { () =>
val row = buildExpandable(userRole = user, userOMVersion = "LATEST", expanded = true)
rows.update(rows.now :+ row)
})
val refreshButton = button(btn_default, "Refresh", onclick := { () =>
updatePodInfos
})
val headerStyle: ModifierSeq = Seq(
height := 40.85
)
......@@ -151,7 +166,8 @@ object AdminPanel {
img(src := "img/logo.png", css.adminLogoStyle),
Utils.logoutItem(styles.display.flex, flexDirection.row, justifyContent.flexEnd),
div(styles.display.flex, flexDirection.row, justifyContent.flexStart, marginLeft := 50, marginBottom := 20, marginTop := 80)(
addUserButton(styles.display.flex, flexDirection.row, styles.justifyContent.flexEnd)
addUserButton(styles.display.flex, flexDirection.row, styles.justifyContent.flexEnd),
refreshButton(styles.display.flex, flexDirection.row, styles.justifyContent.flexEnd)
),
Rx {
div(styles.display.flex, flexDirection.row, styles.justifyContent.center)(
......
......@@ -12,18 +12,21 @@ object AdminApiImpl extends shared.AdminApi {
def upserted(userData: UserData): Seq[UserData] = {
val id = DB.uuid(Email(userData.email)).getOrElse(UUID(java.util.UUID.randomUUID.toString))
upsert(toUser(id, userData))
K8sService.deployIfNotDeployedYet(id)
users
}
def delete(userData: UserData): Seq[UserData] = {
val id = DB.uuid(Email(userData.email))
id.foreach {i=>
DB.delete(toUser(i, userData))
id.foreach { i =>
DB.delete(toUser(i, userData))
}
users
}
//PODS
def podInfos(): Seq[PodInfo] = K8sService.podInfos
def podInfos(): Seq[PodInfo] = {
K8sService.podInfos
}
}
\ No newline at end of file
......@@ -171,6 +171,7 @@ class ConnectServlet(arguments: ConnectServer.ServletArguments) extends Scalatra
Host(uuid, K8sService.hostIP(uuid))
}
}
println("HOST " + host)
buildAndAddCookieToHeader(TokenData.accessToken(host, DB.Email(email)))
buildAndAddCookieToHeader(TokenData.refreshToken(host, DB.Email(email)))
redirect("/")
......
......@@ -3,83 +3,156 @@ package org.openmoleconnect.server
import java.time.temporal.ChronoField
import org.openmoleconnect.server.DB.UUID
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import shared.Data
import skuber._
import skuber.Timestamp
import skuber.json.ext.format._
import skuber.json.format._
import akka.actor.ActorSystem
import skuber.json.format._
import akka.stream.ActorMaterializer
import shared.Data._
import skuber.api.client.KubernetesClient
import skuber.ext.Deployment
import org.openmoleconnect.server.Utils._
import scala.concurrent.duration._
import scala.concurrent.{Await, Future}
import scala.util.{Failure, Success, Try}
import scala.concurrent.ExecutionContext
object K8sService {
private def listPods = {
def listPods = {
implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
implicit val dispatcher = system.dispatcher
val k8s = k8sInit
// val podList: Future[PodList] = k8s list[PodList]() // list pod in default namespaces
// list pods in all namespaces
val allPodsMapFut: Future[Map[String, PodList]] = k8s listByNamespace[PodList]()
val podList: Future[List[Pod]] = allPodsMapFut map { allPodsMap =>
val allPodsFuture: Future[List[Pod]] = allPodsMapFut map { allPodsMap =>
allPodsMap.values.flatMap(_.items).toList
}
podList map {
_.flatMap {
pod: Pod =>
val name = pod.name
// val ns = pod.namespace
(for {
stat <- pod.status.toList
containerStat <- stat.containerStatuses
status <- containerStat.state
restarts <- stat.containerStatuses.headOption
createTime <- pod.metadata.creationTimestamp
podIP <- stat.podIP
} yield {
val st: Status = status match {
case Container.Waiting(reason)=> Data.Waiting(reason.getOrElse(""))
case _: Container.Running=> Data.Running()
case Container.Terminated(_,_,_,message,_,finishedAt, _)=> Data.Terminated(message.getOrElse(""), finishedAt.map{_.toLocalTime.getLong(ChronoField.INSTANT_SECONDS)}.getOrElse(0L))
}
PodInfo(name, st.value, restarts.restartCount, createTime.toLocalTime.getLong(ChronoField.INSTANT_SECONDS), podIP, DB.email(UUID(pod.metadata.name)).map{_.value})
def listPods0(pods: List[Pod]) = {
pods.flatMap { pod: Pod =>
val name = pod.name
val ns = pod.namespace
for {
stat <- pod.status.toList
containerStat <- stat.containerStatuses
status <- containerStat.state
restarts <- stat.containerStatuses.headOption
createTime <- pod.metadata.creationTimestamp
podIP <- stat.podIP
} yield {
val st: Status = status match {
case Container.Waiting(reason) => Data.Waiting(reason.getOrElse(""))
case _: Container.Running => Data.Running()
case Container.Terminated(_, _, _, message, _, finishedAt, _) => Data.Terminated(message.getOrElse(""), finishedAt.map {
_.toEpochSecond
}.getOrElse(0L))
}
PodInfo(pod.name, st.value, restarts.restartCount, createTime.toEpochSecond, podIP, DB.email(UUID(pod.metadata.name)).map {
_.value
})
}
}
}
val allPods = allPodsFuture map { pods => listPods0(pods) }
Await.result(allPods, Duration.Inf)
}
private def podInfo(uuid: UUID) = {
def withK8s[T](kubeAction: KubernetesClient => Future[T]) = {
import monix.execution.Scheduler.Implicits.global
Await.result(
listPods.map { list =>
list.find {
_.name.contains(uuid.value)
}
}, 1 minute)
implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
implicit val dispatcher = system.dispatcher
// val k8s = k8sInit(K8SConfiguration.useLocalProxyDefault)
val k8s = k8sInit(K8SConfiguration.useLocalProxyDefault.setCurrentNamespace("ingress-nginx"))
kubeAction(k8s)
}
def withK8sToResult(k8Action: String)(kubeAction: KubernetesClient => Future[_ <: ObjectResource]): K8ActionResult = {
implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
implicit val dispatcher = system.dispatcher
val k8s = k8sInit(K8SConfiguration.useLocalProxyDefault)
Try {
Await.result({
kubeAction(k8s)
}, Duration.Inf)
} match {
case Success(o: ObjectResource) => K8Success(s"$k8Action successfully completed " + o.name + " // " + o.metadata.generateName)
case Failure(t: Throwable) => K8Failure(t.getMessage, t.toStackTrace)
}
}
def deployOpenMOLE(uuid: UUID) = {
withK8sToResult("OpenMOLE depolyment for user " + DB.email(uuid).map {
_.value
}.getOrElse("")) {
k8s =>
val openmoleLabel = "app" -> "openmole"
val openmoleContainer = Container(name = "openmole", image = "openmole/openmole", command = List("bin/bash", "-c", "openmole --port 80 --password password --http --remote")).exposePort(80)
val openmoleTemplate = Pod.Template.Spec(ObjectMeta(name = uuid.value, namespace = "ingress-nginx"))
.addContainer(openmoleContainer)
.addLabel(openmoleLabel)
//.named("openmole")
val desiredCount = 1
val openmoleDeployment = Deployment(uuid.value)
.withReplicas(desiredCount)
.withTemplate(openmoleTemplate)
println("\nCreating openmole deployment")
k8s create openmoleDeployment
}
}
def deployIfNotDeployedYet(uuid: UUID) = {
if (!isDeploymentExists(uuid))
deployOpenMOLE(uuid)
}
private def podInfo(uuid: UUID) =
// import monix.execution.Scheduler.Implicits.global
listPods.find {
_.name.contains(uuid.value)
}
def isServiceUp(uuid: UUID): Boolean = {
podInfo(uuid).map{_.status} == Some(Running)
podInfo(uuid).map {
_.status
} == Some(Running)
}
def isDeploymentExists(uuid: UUID) = podInfo(uuid).isDefined
def podInfos: Seq[PodInfo] = {
for {
uuid <- DB.uuids
podInfo <- podInfo(uuid)
} yield (podInfo)
}
def hostIP(uuid:UUID) = podInfo(uuid).map{_.podIP}
def hostIP(uuid: UUID) = {
podInfo(uuid).map {
_.podIP
}
}
}
package org.openmoleconnect.server
import java.io.{PrintWriter, StringWriter}
import java.text.SimpleDateFormat
import java.util.Locale
......@@ -12,4 +13,12 @@ object Utils {
def developpement = DB.Version("FIXME DEV")
}
implicit class ST(throwable: Throwable) {
def toStackTrace = {
val sw = new StringWriter()
throwable.printStackTrace(new PrintWriter(sw))
sw.toString
}
}
}
......@@ -27,4 +27,9 @@ object Data {
case class UserData(name: String, email: String, password: String, role: Role, omVersion: String, lastAccess: Long)
trait K8ActionResult
case class K8Success(message: String) extends K8ActionResult
case class K8Failure(message: String, stackTrace: String) extends K8ActionResult
}
\ No newline at end of file
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