From 3081df26161cb603132b28888bd40820f43686f3 Mon Sep 17 00:00:00 2001 From: Arek Burdach Date: Tue, 18 Feb 2025 20:08:18 +0100 Subject: [PATCH] Removed errors field --- .../src/components/Process/ProcessErrors.tsx | 22 ----- .../components/Process/ProcessStateIcon.tsx | 2 - .../client/src/components/Process/types.ts | 3 +- .../src/reducers/graph/utils.fixtures.ts | 1 - .../engine/api/deployment/StatusDetails.scala | 3 +- .../SimpleProcessStateDefinitionManager.scala | 9 ++- .../deployment/simple/SimpleStateStatus.scala | 24 ++++-- .../scenariodetails/ScenarioStatusDto.scala | 1 - .../ui/api/AppApiHttpService.scala | 2 +- .../ui/api/ScenarioStatusPresenter.scala | 1 - .../InconsistentStateDetector.scala | 9 ++- .../InconsistentStateDetectorTest.scala | 4 +- .../management/FlinkDeploymentManager.scala | 2 +- .../FlinkStatusDetailsDeterminer.scala | 2 - ...reamingEmbeddedDeploymentManagerTest.scala | 2 +- .../k8s/manager/K8sDeploymentManager.scala | 3 +- .../manager/K8sDeploymentStatusMapper.scala | 46 ++++++----- .../K8sDeploymentStatusMapperSpec.scala | 81 ++++++++++++------- 18 files changed, 118 insertions(+), 99 deletions(-) delete mode 100644 designer/client/src/components/Process/ProcessErrors.tsx diff --git a/designer/client/src/components/Process/ProcessErrors.tsx b/designer/client/src/components/Process/ProcessErrors.tsx deleted file mode 100644 index 4089a5f5545..00000000000 --- a/designer/client/src/components/Process/ProcessErrors.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from "react"; -import { useTranslation } from "react-i18next"; -import { ProcessStateType } from "./types"; - -export function Errors({ state }: { state: ProcessStateType }) { - const { t } = useTranslation(); - - if (state.errors?.length < 1) { - return null; - } - - return ( -
- {t("stateIcon.errors", "Errors:")} - -
- ); -} diff --git a/designer/client/src/components/Process/ProcessStateIcon.tsx b/designer/client/src/components/Process/ProcessStateIcon.tsx index 89658ebe15e..55a813a2738 100644 --- a/designer/client/src/components/Process/ProcessStateIcon.tsx +++ b/designer/client/src/components/Process/ProcessStateIcon.tsx @@ -3,7 +3,6 @@ import { ProcessStateType, Scenario } from "./types"; import ProcessStateUtils from "./ProcessStateUtils"; import UrlIcon from "../UrlIcon"; import { Box, Divider, Popover, styled, Typography } from "@mui/material"; -import { Errors } from "./ProcessErrors"; const StyledUrlIcon = styled(UrlIcon)(({ theme }) => ({ width: theme.spacing(2.5), @@ -44,7 +43,6 @@ function ProcessStateIcon({ scenario, processState }: Props) { {tooltip} - diff --git a/designer/client/src/components/Process/types.ts b/designer/client/src/components/Process/types.ts index d22279e10ac..7ddcd24abff 100644 --- a/designer/client/src/components/Process/types.ts +++ b/designer/client/src/components/Process/types.ts @@ -1,5 +1,5 @@ /* eslint-disable i18next/no-literal-string */ -import { UnknownRecord, Instant } from "../../types/common"; +import { Instant } from "../../types/common"; import { ScenarioGraph, ValidationResult } from "../../types"; import { ProcessingMode } from "../../http/HttpService"; @@ -67,7 +67,6 @@ export type ProcessStateType = { icon: string; tooltip: string; description: string; - errors?: Array; }; export type StatusType = { diff --git a/designer/client/src/reducers/graph/utils.fixtures.ts b/designer/client/src/reducers/graph/utils.fixtures.ts index a9d869ee46f..dae28b139f3 100644 --- a/designer/client/src/reducers/graph/utils.fixtures.ts +++ b/designer/client/src/reducers/graph/utils.fixtures.ts @@ -171,7 +171,6 @@ export const state: GraphState = { icon: "/assets/states/not-deployed.svg", tooltip: "The scenario is not deployed.", description: "The scenario is not deployed.", - errors: [], }, validationResult: { errors: { diff --git a/designer/deployment-manager-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/StatusDetails.scala b/designer/deployment-manager-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/StatusDetails.scala index 965c9afe8d3..0e1dda21de5 100644 --- a/designer/deployment-manager-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/StatusDetails.scala +++ b/designer/deployment-manager-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/StatusDetails.scala @@ -5,13 +5,12 @@ import pl.touk.nussknacker.engine.deployment.{DeploymentId, ExternalDeploymentId case class StatusDetails( status: StateStatus, + // FIXME abr: non optional deploymentId: Option[DeploymentId], // TODO: remove it after periodic mechanism will use UUID for DeploymentId externalDeploymentId: Option[ExternalDeploymentId] = None, version: Option[ProcessVersion] = None, startTime: Option[Long] = None, - // FIXME abr: verify usage - errors: List[String] = List.empty ) { def deploymentIdUnsafe: DeploymentId = diff --git a/designer/deployment-manager-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/simple/SimpleProcessStateDefinitionManager.scala b/designer/deployment-manager-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/simple/SimpleProcessStateDefinitionManager.scala index 1698d0dfd01..53178e28055 100644 --- a/designer/deployment-manager-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/simple/SimpleProcessStateDefinitionManager.scala +++ b/designer/deployment-manager-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/simple/SimpleProcessStateDefinitionManager.scala @@ -26,15 +26,16 @@ object SimpleProcessStateDefinitionManager extends ProcessStateDefinitionManager ) private[nussknacker] def statusDescription(status: StateStatus): String = status match { - case _ @ProblemStateStatus(message, _) => message - case _ => SimpleStateStatus.definitions(status.name).description + case _ @ProblemStateStatus(message, _, _) => message + case _ => SimpleStateStatus.definitions(status.name).description } override def statusTooltip(input: ScenarioStatusWithScenarioContext): String = statusTooltip(input.scenarioStatus) private[nussknacker] def statusTooltip(status: StateStatus): String = status match { - case _ @ProblemStateStatus(message, _) => message - case _ => SimpleStateStatus.definitions(status.name).tooltip + case _ @ProblemStateStatus(message, _, Some(tooltip)) => tooltip + case _ @ProblemStateStatus(message, _, _) => message + case _ => SimpleStateStatus.definitions(status.name).tooltip } override def stateDefinitions: Map[StatusName, StateDefinitionDetails] = diff --git a/designer/deployment-manager-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/simple/SimpleStateStatus.scala b/designer/deployment-manager-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/simple/SimpleStateStatus.scala index a6c4e0c0649..c11aa52c1f7 100644 --- a/designer/deployment-manager-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/simple/SimpleStateStatus.scala +++ b/designer/deployment-manager-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/simple/SimpleStateStatus.scala @@ -4,6 +4,7 @@ import pl.touk.nussknacker.engine.api.deployment.StateStatus.StatusName import pl.touk.nussknacker.engine.api.deployment._ import pl.touk.nussknacker.engine.api.deployment.simple.SimpleStateStatus.ProblemStateStatus.defaultActions import pl.touk.nussknacker.engine.api.process.VersionId +import pl.touk.nussknacker.engine.deployment.DeploymentId import java.net.URI @@ -19,8 +20,11 @@ object SimpleStateStatus { } // Represents general problem. - final case class ProblemStateStatus(description: String, allowedActions: Set[ScenarioActionName] = defaultActions) - extends StateStatus { + final case class ProblemStateStatus( + description: String, + allowedActions: Set[ScenarioActionName] = defaultActions, + tooltip: Option[String] = None + ) extends StateStatus { override def name: StatusName = ProblemStateStatus.name } @@ -66,8 +70,18 @@ object SimpleStateStatus { def missingDeployedVersion(exceptedVersionId: VersionId, user: String): ProblemStateStatus = ProblemStateStatus(s"Scenario deployed without version by $user, expected version $exceptedVersionId.") - val MultipleJobsRunning: ProblemStateStatus = - ProblemStateStatus("More than one deployment is running.", Set(ScenarioActionName.Cancel)) + def multipleJobsRunning(nonFinalDeploymentIds: List[(DeploymentId, StateStatus)]): ProblemStateStatus = + ProblemStateStatus( + description = "More than one deployment is running.", + allowedActions = Set(ScenarioActionName.Cancel), + tooltip = Some( + nonFinalDeploymentIds + .map { case (deploymentId, deploymentStatus) => + deploymentId + " - " + deploymentStatus + } + .mkString("Expected one job, instead: ", ", ", "") + ) + ) } @@ -105,7 +119,7 @@ object SimpleStateStatus { Set(ScenarioActionName.Deploy, ScenarioActionName.Cancel) // When Failed - process is in terminal state in Flink and it doesn't require any cleanup in Flink, but in NK it does // - that's why Cancel action is available - case SimpleStateStatus.ProblemStateStatus(_, allowedActions) => allowedActions + case SimpleStateStatus.ProblemStateStatus(_, allowedActions, _) => allowedActions } val definitions: Map[StatusName, StateDefinitionDetails] = Map( diff --git a/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/scenariodetails/ScenarioStatusDto.scala b/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/scenariodetails/ScenarioStatusDto.scala index b4c3c486d64..6fd4dae508f 100644 --- a/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/scenariodetails/ScenarioStatusDto.scala +++ b/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/scenariodetails/ScenarioStatusDto.scala @@ -30,7 +30,6 @@ import java.net.URI icon: URI, tooltip: String, description: String, - errors: List[String] ) @JsonCodec case class LegacyScenarioStatusNameDto(name: String) diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/AppApiHttpService.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/AppApiHttpService.scala index f7b839081af..32969c478d4 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/AppApiHttpService.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/AppApiHttpService.scala @@ -173,7 +173,7 @@ class AppApiHttpService( ) statusMap = processes.flatMap(process => process.state.map(process.name -> _)).toMap withProblem = statusMap.collect { - case (name, processStatus @ ScenarioStatusDto(ProblemStateStatus.name, _, _, _, _, _, _, _, _)) => + case (name, processStatus @ ScenarioStatusDto(ProblemStateStatus.name, _, _, _, _, _, _, _)) => (name, processStatus) } } yield withProblem diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ScenarioStatusPresenter.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ScenarioStatusPresenter.scala index f9b7ea2829d..a6a3c5f4a02 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ScenarioStatusPresenter.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ScenarioStatusPresenter.scala @@ -35,7 +35,6 @@ class ScenarioStatusPresenter(dispatcher: DeploymentManagerDispatcher) { icon = presentation.icon, tooltip = presentation.tooltip, description = presentation.description, - errors = statusDetails.errors, ) } diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/deployment/scenariostatus/InconsistentStateDetector.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/deployment/scenariostatus/InconsistentStateDetector.scala index ebc5fbebc35..b6aefd1ec46 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/deployment/scenariostatus/InconsistentStateDetector.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/deployment/scenariostatus/InconsistentStateDetector.scala @@ -46,10 +46,11 @@ class InconsistentStateDetector extends LazyLogging { case (_, firstNotFinished :: _ :: _) => Left( firstNotFinished.copy( - status = ProblemStateStatus.MultipleJobsRunning, - errors = List(s"Expected one job, instead: ${notFinalStatuses - .map(details => details.deploymentId.map(_.value).getOrElse("missing") + " - " + details.status) - .mkString(", ")}") + status = ProblemStateStatus.multipleJobsRunning( + notFinalStatuses.map(deploymentStatus => + deploymentStatus.deploymentId.getOrElse(DeploymentId("missing")) -> deploymentStatus.status + ) + ) ) ) case (firstFinished :: _, Nil) => Right(Some(firstFinished)) diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/deployment/scenariostatus/InconsistentStateDetectorTest.scala b/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/deployment/scenariostatus/InconsistentStateDetectorTest.scala index 657818a798f..88e00ee38ef 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/deployment/scenariostatus/InconsistentStateDetectorTest.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/deployment/scenariostatus/InconsistentStateDetectorTest.scala @@ -18,7 +18,7 @@ class InconsistentStateDetectorTest extends AnyFunSuiteLike with Matchers { InconsistentStateDetector.extractAtMostOneStatus(List(firstDeploymentStatus, secondDeploymentStatus)) shouldBe Some( StatusDetails( - ProblemStateStatus.MultipleJobsRunning, + ProblemStateStatus.multipleJobsRunning, firstDeploymentStatus.deploymentId, errors = List( s"Expected one job, instead: ${firstDeploymentStatus.deploymentIdUnsafe} - RUNNING, ${secondDeploymentStatus.deploymentIdUnsafe} - RUNNING" @@ -34,7 +34,7 @@ class InconsistentStateDetectorTest extends AnyFunSuiteLike with Matchers { InconsistentStateDetector.extractAtMostOneStatus(List(firstDeploymentStatus, secondDeploymentStatus)) shouldBe Some( StatusDetails( - ProblemStateStatus.MultipleJobsRunning, + ProblemStateStatus.multipleJobsRunning, firstDeploymentStatus.deploymentId, errors = List( s"Expected one job, instead: ${firstDeploymentStatus.deploymentIdUnsafe} - RUNNING, ${secondDeploymentStatus.deploymentIdUnsafe} - RESTARTING" diff --git a/engine/flink/management/src/main/scala/pl/touk/nussknacker/engine/management/FlinkDeploymentManager.scala b/engine/flink/management/src/main/scala/pl/touk/nussknacker/engine/management/FlinkDeploymentManager.scala index e2fea6db31f..2aecaef16ab 100644 --- a/engine/flink/management/src/main/scala/pl/touk/nussknacker/engine/management/FlinkDeploymentManager.scala +++ b/engine/flink/management/src/main/scala/pl/touk/nussknacker/engine/management/FlinkDeploymentManager.scala @@ -204,7 +204,7 @@ class FlinkDeploymentManager( implicit val freshnessPolicy: DataFreshnessPolicy = DataFreshnessPolicy.Fresh getScenarioDeploymentsStatuses(processName).flatMap { statuses => val runningDeploymentIds = statuses.value.filter(statusDetailsPredicate).collect { - case StatusDetails(SimpleStateStatus.Running, _, Some(deploymentId), _, _, _) => deploymentId + case StatusDetails(SimpleStateStatus.Running, _, Some(deploymentId), _, _) => deploymentId } runningDeploymentIds match { case Nil => diff --git a/engine/flink/management/src/main/scala/pl/touk/nussknacker/engine/management/FlinkStatusDetailsDeterminer.scala b/engine/flink/management/src/main/scala/pl/touk/nussknacker/engine/management/FlinkStatusDetailsDeterminer.scala index 896e537eefa..570ec80ec52 100644 --- a/engine/flink/management/src/main/scala/pl/touk/nussknacker/engine/management/FlinkStatusDetailsDeterminer.scala +++ b/engine/flink/management/src/main/scala/pl/touk/nussknacker/engine/management/FlinkStatusDetailsDeterminer.scala @@ -35,7 +35,6 @@ class FlinkStatusDetailsDeterminer( Some(ExternalDeploymentId(job.jid)), version = Some(jobConfig.version), startTime = Some(job.`start-time`), - errors = List.empty ) } getOrElse { logger.debug( @@ -48,7 +47,6 @@ class FlinkStatusDetailsDeterminer( Some(ExternalDeploymentId(job.jid)), version = None, startTime = Some(job.`start-time`), - errors = List.empty ) } name -> details diff --git a/engine/lite/embeddedDeploymentManager/src/test/scala/pl/touk/nussknacker/streaming/embedded/StreamingEmbeddedDeploymentManagerTest.scala b/engine/lite/embeddedDeploymentManager/src/test/scala/pl/touk/nussknacker/streaming/embedded/StreamingEmbeddedDeploymentManagerTest.scala index 0d56ec5017a..5def40bad13 100644 --- a/engine/lite/embeddedDeploymentManager/src/test/scala/pl/touk/nussknacker/streaming/embedded/StreamingEmbeddedDeploymentManagerTest.scala +++ b/engine/lite/embeddedDeploymentManager/src/test/scala/pl/touk/nussknacker/streaming/embedded/StreamingEmbeddedDeploymentManagerTest.scala @@ -146,7 +146,7 @@ class StreamingEmbeddedDeploymentManagerTest val FixtureParam(manager, _, _, _) = prepareFixture(inputTopic, outputTopic, List(deployedScenarioData)) manager.getScenarioDeploymentsStatuses(name).futureValue.value.map(_.status) should matchPattern { - case ProblemStateStatus("Scenario compilation errors", _) :: Nil => + case ProblemStateStatus("Scenario compilation errors", _, _) :: Nil => } } diff --git a/engine/lite/k8sDeploymentManager/src/main/scala/pl/touk/nussknacker/k8s/manager/K8sDeploymentManager.scala b/engine/lite/k8sDeploymentManager/src/main/scala/pl/touk/nussknacker/k8s/manager/K8sDeploymentManager.scala index 668d9cb3b8d..ed9a50014e8 100644 --- a/engine/lite/k8sDeploymentManager/src/main/scala/pl/touk/nussknacker/k8s/manager/K8sDeploymentManager.scala +++ b/engine/lite/k8sDeploymentManager/src/main/scala/pl/touk/nussknacker/k8s/manager/K8sDeploymentManager.scala @@ -318,14 +318,13 @@ class K8sDeploymentManager( override def getScenarioDeploymentsStatuses( scenarioName: ProcessName )(implicit freshnessPolicy: DataFreshnessPolicy): Future[WithDataFreshnessStatus[List[StatusDetails]]] = { - val mapper = new K8sDeploymentStatusMapper(processStateDefinitionManager) for { deployments <- scenarioStateK8sClient .listSelected[ListResource[Deployment]](requirementForName(scenarioName)) .map(_.items) pods <- scenarioStateK8sClient.listSelected[ListResource[Pod]](requirementForName(scenarioName)).map(_.items) } yield { - WithDataFreshnessStatus.fresh(deployments.map(mapper.status(_, pods))) + WithDataFreshnessStatus.fresh(deployments.map(K8sDeploymentStatusMapper.status(_, pods))) } } diff --git a/engine/lite/k8sDeploymentManager/src/main/scala/pl/touk/nussknacker/k8s/manager/K8sDeploymentStatusMapper.scala b/engine/lite/k8sDeploymentManager/src/main/scala/pl/touk/nussknacker/k8s/manager/K8sDeploymentStatusMapper.scala index 84f9a15bd35..d7b1d15f909 100644 --- a/engine/lite/k8sDeploymentManager/src/main/scala/pl/touk/nussknacker/k8s/manager/K8sDeploymentStatusMapper.scala +++ b/engine/lite/k8sDeploymentManager/src/main/scala/pl/touk/nussknacker/k8s/manager/K8sDeploymentStatusMapper.scala @@ -1,16 +1,16 @@ package pl.touk.nussknacker.k8s.manager +import cats.data.NonEmptyList import com.typesafe.scalalogging.LazyLogging -import io.circe.Json import pl.touk.nussknacker.engine.api.deployment.simple.SimpleStateStatus import pl.touk.nussknacker.engine.api.deployment.simple.SimpleStateStatus.ProblemStateStatus -import pl.touk.nussknacker.engine.api.deployment.{ProcessStateDefinitionManager, StateStatus, StatusDetails} +import pl.touk.nussknacker.engine.api.deployment.{ScenarioActionName, StateStatus, StatusDetails} import pl.touk.nussknacker.k8s.manager.K8sDeploymentManager.parseVersionAnnotation -import pl.touk.nussknacker.k8s.manager.K8sDeploymentStatusMapper._ import skuber.apps.v1.Deployment import skuber.{Container, Pod} -object K8sDeploymentStatusMapper { +//Based on https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#deployment-status +object K8sDeploymentStatusMapper extends LazyLogging { private val availableCondition = "Available" @@ -23,10 +23,6 @@ object K8sDeploymentStatusMapper { private val crashLoopBackOffReason = "CrashLoopBackOff" private val newReplicaSetAvailable = "NewReplicaSetAvailable" -} - -//Based on https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#deployment-status -class K8sDeploymentStatusMapper(definitionManager: ProcessStateDefinitionManager) extends LazyLogging { private[manager] def findStatusForDeploymentsAndPods( deployments: List[Deployment], @@ -38,17 +34,20 @@ class K8sDeploymentStatusMapper(definitionManager: ProcessStateDefinitionManager case duplicates => Some( StatusDetails( - ProblemStateStatus.MultipleJobsRunning, + ProblemStateStatus( + description = "More than one deployment is running.", + allowedActions = Set(ScenarioActionName.Cancel), + tooltip = Some(s"Expected one deployment, instead: ${duplicates.map(_.metadata.name).mkString(", ")}") + ), None, - errors = List(s"Expected one deployment, instead: ${duplicates.map(_.metadata.name).mkString(", ")}") ) ) } } private[manager] def status(deployment: Deployment, pods: List[Pod]): StatusDetails = { - val (status, attrs, errors) = deployment.status match { - case None => (SimpleStateStatus.DuringDeploy, None, Nil) + val status = deployment.status match { + case None => SimpleStateStatus.DuringDeploy case Some(status) => mapStatusWithPods(status, pods) } val startTime = deployment.metadata.creationTimestamp.map(_.toInstant.toEpochMilli) @@ -59,7 +58,6 @@ class K8sDeploymentStatusMapper(definitionManager: ProcessStateDefinitionManager None, parseVersionAnnotation(deployment), startTime, - errors ) } @@ -67,24 +65,34 @@ class K8sDeploymentStatusMapper(definitionManager: ProcessStateDefinitionManager private[manager] def mapStatusWithPods( status: Deployment.Status, pods: List[Pod] - ): (StateStatus, Option[Json], List[String]) = { + ): StateStatus = { def condition(name: String): Option[Deployment.Condition] = status.conditions.find(cd => cd.`type` == name) def anyContainerInState(state: Container.State) = pods.flatMap(_.status.toList).flatMap(_.containerStatuses).exists(_.state.exists(_ == state)) (condition(availableCondition), condition(progressingCondition), condition(replicaFailureCondition)) match { case (Some(available), None | ProgressingNewReplicaSetAvailable(), _) if isTrue(available) => - (SimpleStateStatus.Running, None, Nil) + SimpleStateStatus.Running case (_, Some(progressing), _) if isTrue(progressing) && anyContainerInState(Container.Waiting(Some(crashLoopBackOffReason))) => logger.debug( s"Some containers are in waiting state with CrashLoopBackOff reason - returning Restarting status. Pods: $pods" ) - (SimpleStateStatus.Restarting, None, Nil) - case (_, Some(progressing), _) if isTrue(progressing) => (SimpleStateStatus.DuringDeploy, None, Nil) + SimpleStateStatus.Restarting + case (_, Some(progressing), _) if isTrue(progressing) => + SimpleStateStatus.DuringDeploy case (_, _, Some(replicaFailure)) if isTrue(replicaFailure) => - (ProblemStateStatus.Failed, None, replicaFailure.message.toList) - case (a, b, _) => (ProblemStateStatus.Failed, None, a.flatMap(_.message).toList ++ b.flatMap(_.message).toList) + ProblemStateStatus( + "There are some problems with scenario.", + tooltip = replicaFailure.message.map("Error: " + _) + ) + case (a, b, _) => + ProblemStateStatus( + "There are some problems with scenario.", + tooltip = NonEmptyList + .fromList(a.flatMap(_.message).toList ++ b.flatMap(_.message).toList) + .map(_.toList.mkString("Errors: ", ", ", "")) + ) } } diff --git a/engine/lite/k8sDeploymentManager/src/test/scala/pl/touk/nussknacker/k8s/manager/K8sDeploymentStatusMapperSpec.scala b/engine/lite/k8sDeploymentManager/src/test/scala/pl/touk/nussknacker/k8s/manager/K8sDeploymentStatusMapperSpec.scala index e5ca95c37be..175ceeec463 100644 --- a/engine/lite/k8sDeploymentManager/src/test/scala/pl/touk/nussknacker/k8s/manager/K8sDeploymentStatusMapperSpec.scala +++ b/engine/lite/k8sDeploymentManager/src/test/scala/pl/touk/nussknacker/k8s/manager/K8sDeploymentStatusMapperSpec.scala @@ -3,22 +3,20 @@ package pl.touk.nussknacker.k8s.manager import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers import pl.touk.nussknacker.engine.api.ProcessVersion -import pl.touk.nussknacker.engine.api.deployment.StatusDetails import pl.touk.nussknacker.engine.api.deployment.simple.SimpleStateStatus import pl.touk.nussknacker.engine.api.deployment.simple.SimpleStateStatus.ProblemStateStatus +import pl.touk.nussknacker.engine.api.deployment.{ScenarioActionName, StatusDetails} import pl.touk.nussknacker.engine.api.process.{ProcessId, ProcessName, VersionId} import pl.touk.nussknacker.engine.util.ResourceLoader import play.api.libs.json.{Format, Json} -import skuber.{ListResource, Pod} import skuber.apps.v1.Deployment import skuber.json.format._ +import skuber.{ListResource, Pod} //It's no so easy to move deployment in unstable state reliably, so //for now we have unit tests based on real responses - generated manually, using kubectl -v=9 describe deployment [...] class K8sDeploymentStatusMapperSpec extends AnyFunSuite with Matchers { - private val mapper = new K8sDeploymentStatusMapper(K8sProcessStateDefinitionManager) - private val timestamp = 1640769008000L private val version = ProcessVersion(VersionId(4), ProcessName("AAAAA"), ProcessId(7), List.empty, "admin", Some(2)) @@ -29,60 +27,89 @@ class K8sDeploymentStatusMapperSpec extends AnyFunSuite with Matchers { } test("detects running scenario") { - val state = mapper.findStatusForDeploymentsAndPods(parseResource[Deployment]("running.json") :: Nil, Nil) + val state = + K8sDeploymentStatusMapper.findStatusForDeploymentsAndPods(parseResource[Deployment]("running.json") :: Nil, Nil) state shouldBe Some( - StatusDetails(SimpleStateStatus.Running, None, None, Some(version), Some(timestamp), Nil) + StatusDetails( + status = SimpleStateStatus.Running, + deploymentId = None, + externalDeploymentId = None, + version = Some(version), + startTime = Some(timestamp) + ) ) } test("detects scenario in deployment") { - val state = mapper.findStatusForDeploymentsAndPods(parseResource[Deployment]("inProgress.json") :: Nil, Nil) + val state = K8sDeploymentStatusMapper.findStatusForDeploymentsAndPods( + parseResource[Deployment]("inProgress.json") :: Nil, + Nil + ) state shouldBe Some( - StatusDetails(SimpleStateStatus.DuringDeploy, None, None, Some(version), Some(timestamp), Nil) + StatusDetails( + status = SimpleStateStatus.DuringDeploy, + deploymentId = None, + externalDeploymentId = None, + version = Some(version), + startTime = Some(timestamp) + ) ) } test("detects scenario without progress") { - val state = mapper.findStatusForDeploymentsAndPods(parseResource[Deployment]("progressFailed.json") :: Nil, Nil) + val state = K8sDeploymentStatusMapper.findStatusForDeploymentsAndPods( + parseResource[Deployment]("progressFailed.json") :: Nil, + Nil + ) state shouldBe Some( StatusDetails( - ProblemStateStatus.Failed, - None, - None, - Some(version), - Some(timestamp), - List( - "Deployment does not have minimum availability.", - "ReplicaSet \"scenario-7-processname-aaaaa-x-5c799f64b8\" has timed out progressing." - ) + status = ProblemStateStatus( + "There are some problems with scenario.", + tooltip = Some( + "Errors: Deployment does not have minimum availability., ReplicaSet \"scenario-7-processname-aaaaa-x-5c799f64b8\" has timed out progressing." + ) + ), + deploymentId = None, + externalDeploymentId = None, + version = Some(version), + startTime = Some(timestamp), ) ) } test("detects restarting (crashing) scenario") { - val state = mapper.findStatusForDeploymentsAndPods( + val state = K8sDeploymentStatusMapper.findStatusForDeploymentsAndPods( parseResource[Deployment]("inProgress.json") :: Nil, parseResource[ListResource[Pod]]("podsCrashLoopBackOff.json").items ) state shouldBe Some( - StatusDetails(SimpleStateStatus.Restarting, None, None, Some(version), Some(timestamp), Nil) + StatusDetails( + status = SimpleStateStatus.Restarting, + deploymentId = None, + externalDeploymentId = None, + version = Some(version), + startTime = Some(timestamp) + ) ) } test("detects multiple deployments") { val deployment = parseResource[Deployment]("running.json") val deployment2 = deployment.copy(metadata = deployment.metadata.copy(name = "otherName")) - val state = mapper.findStatusForDeploymentsAndPods(deployment :: deployment2 :: Nil, Nil) + val state = K8sDeploymentStatusMapper.findStatusForDeploymentsAndPods(deployment :: deployment2 :: Nil, Nil) state shouldBe Some( StatusDetails( - ProblemStateStatus.MultipleJobsRunning, - None, - None, - None, - None, - "Expected one deployment, instead: scenario-7-processname-aaaaa-x, otherName" :: Nil + status = ProblemStateStatus( + description = "More than one deployment is running.", + allowedActions = Set(ScenarioActionName.Cancel), + tooltip = Some("Expected one deployment, instead: scenario-7-processname-aaaaa-x, otherName") + ), + deploymentId = None, + externalDeploymentId = None, + version = None, + startTime = None, ) ) }