Skip to content

Commit

Permalink
Add a job to heal projects at startup (#5221)
Browse files Browse the repository at this point in the history
* Add a job to heal projects at startup

---------

Co-authored-by: Simon Dumas <[email protected]>
  • Loading branch information
imsdu and Simon Dumas authored Nov 7, 2024
1 parent e5993bf commit 21021a4
Show file tree
Hide file tree
Showing 10 changed files with 76 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import ch.epfl.bluebrain.nexus.delta.sdk.identities.Identities
import ch.epfl.bluebrain.nexus.delta.sdk.marshalling.{HttpResponseFields, RdfMarshalling}
import ch.epfl.bluebrain.nexus.delta.sdk.model.BaseUri
import ch.epfl.bluebrain.nexus.delta.sdk.permissions.Permissions.{projects, supervision}
import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.{ProjectHealer, ProjectRejection, ProjectsHealth}
import ch.epfl.bluebrain.nexus.delta.sdk.projects.{ProjectHealer, ProjectsHealth}
import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.ProjectRejection
import ch.epfl.bluebrain.nexus.delta.sourcing.model.ProjectRef
import ch.epfl.bluebrain.nexus.delta.sourcing.stream.{ProjectActivitySignals, SupervisedDescription}
import io.circe.generic.semiauto.deriveEncoder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import ch.epfl.bluebrain.nexus.delta.sdk.model._
import ch.epfl.bluebrain.nexus.delta.sdk.model.metrics.ScopedEventMetricEncoder
import ch.epfl.bluebrain.nexus.delta.sdk.organizations.FetchActiveOrganization
import ch.epfl.bluebrain.nexus.delta.sdk.projects._
import ch.epfl.bluebrain.nexus.delta.sdk.projects.job.ProjectHealthJob
import ch.epfl.bluebrain.nexus.delta.sdk.projects.model._
import ch.epfl.bluebrain.nexus.delta.sdk.provisioning.ProjectProvisioning
import ch.epfl.bluebrain.nexus.delta.sdk.quotas.Quotas
Expand Down Expand Up @@ -76,6 +77,10 @@ object ProjectsModule extends ModuleDef {
ProjectHealer(errorStore, scopeInitializer, serviceAccount)
)

make[ProjectHealthJob].fromEffect { (projects: Projects, projectHealer: ProjectHealer) =>
ProjectHealthJob(projects, projectHealer)
}

make[ProjectsStatistics].fromEffect { (xas: Transactors) =>
ProjectsStatistics(xas)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import ch.epfl.bluebrain.nexus.delta.sdk.PriorityRoute
import ch.epfl.bluebrain.nexus.delta.sdk.acls.AclCheck
import ch.epfl.bluebrain.nexus.delta.sdk.identities.Identities
import ch.epfl.bluebrain.nexus.delta.sdk.model.BaseUri
import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.{ProjectHealer, ProjectsHealth}
import ch.epfl.bluebrain.nexus.delta.sdk.projects.{ProjectHealer, ProjectsHealth}
import ch.epfl.bluebrain.nexus.delta.sourcing.stream.{ProjectActivitySignals, Supervisor}
import izumi.distage.model.definition.{Id, ModuleDef}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import ch.epfl.bluebrain.nexus.delta.sdk.identities.IdentitiesDummy
import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller
import ch.epfl.bluebrain.nexus.delta.sdk.permissions.Permissions.{projects, supervision}
import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.ProjectRejection.ProjectInitializationFailed
import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.{ProjectHealer, ProjectsHealth}
import ch.epfl.bluebrain.nexus.delta.sdk.projects.{ProjectHealer, ProjectsHealth}
import ch.epfl.bluebrain.nexus.delta.sdk.utils.BaseRouteSpec
import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.{Anonymous, Authenticated, Group, User}
import ch.epfl.bluebrain.nexus.delta.sourcing.model.{Label, ProjectRef}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ch.epfl.bluebrain.nexus.delta.sdk.projects.model
package ch.epfl.bluebrain.nexus.delta.sdk.projects

import cats.effect.IO
import ch.epfl.bluebrain.nexus.delta.sdk.ScopeInitializer
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package ch.epfl.bluebrain.nexus.delta.sdk.projects.model
package ch.epfl.bluebrain.nexus.delta.sdk.projects

import cats.effect.IO
import ch.epfl.bluebrain.nexus.delta.sdk.projects.ScopeInitializationErrorStore
import ch.epfl.bluebrain.nexus.delta.sourcing.model.ProjectRef

trait ProjectsHealth {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package ch.epfl.bluebrain.nexus.delta.sdk.projects.job

import cats.effect.IO
import cats.effect.std.Env
import ch.epfl.bluebrain.nexus.delta.kernel.Logger
import ch.epfl.bluebrain.nexus.delta.sdk.projects.{ProjectHealer, Projects}
import ch.epfl.bluebrain.nexus.delta.sourcing.model.ProjectRef
import fs2.Stream

trait ProjectHealthJob

object ProjectHealthJob extends ProjectHealthJob {
private val logger = Logger[ProjectHealthJob]

def healTrigger: IO[Boolean] =
Env[IO].get("HEAL_PROJECTS").map(_.getOrElse("false").toBoolean)

private[job] def run(currentProjects: Stream[IO, ProjectRef], projectHealer: ProjectHealer): IO[Unit] =
currentProjects
.evalMap { projectRef =>
projectHealer.heal(projectRef).recoverWith { err =>
logger.error(err)(s"Project '$projectRef' could not be heal because of : ${err.getMessage}.")
}
}
.compile
.drain

def apply(projects: Projects, projectHealer: ProjectHealer): IO[ProjectHealthJob.type] =
healTrigger
.flatMap {
case true => {
logger.info("Starting Nexus automatic project healing.") >>
run(projects.currentRefs, projectHealer) >>
logger.info("Nexus automatic healing has completed.")
}
case false => logger.info("Nexus automatic project healingi is disabled.")
}
.as(ProjectHealthJob)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import cats.effect.{IO, Ref}
import ch.epfl.bluebrain.nexus.delta.sdk.error.ServiceError.ScopeInitializationFailed
import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.ServiceAccount
import ch.epfl.bluebrain.nexus.delta.sdk.projects.ScopeInitializationErrorStore.{noopStore, ScopeInitErrorRow}
import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.ProjectHealer
import ch.epfl.bluebrain.nexus.delta.sdk.{OrganizationResource, ScopeInitializer}
import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.User
import ch.epfl.bluebrain.nexus.delta.sourcing.model.{EntityType, Identity, Label, ProjectRef}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package ch.epfl.bluebrain.nexus.delta.sdk.projects
import cats.effect.IO
import ch.epfl.bluebrain.nexus.delta.sdk.error.ServiceError
import ch.epfl.bluebrain.nexus.delta.sdk.projects.ScopeInitializationErrorStore.ScopeInitErrorRow
import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.ProjectsHealth
import ch.epfl.bluebrain.nexus.delta.sourcing.model.{EntityType, Label, ProjectRef}
import ch.epfl.bluebrain.nexus.testkit.mu.NexusSuite

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ch.epfl.bluebrain.nexus.delta.sdk.projects.job

import cats.effect.IO
import cats.effect.Ref
import ch.epfl.bluebrain.nexus.delta.sdk.projects.ProjectHealer
import ch.epfl.bluebrain.nexus.delta.sourcing.model.ProjectRef
import ch.epfl.bluebrain.nexus.testkit.mu.NexusSuite
import fs2.Stream

class ProjectHealthJobSuite extends NexusSuite {

test("Healing should be called for the different projects") {
val projects = Set(ProjectRef.unsafe("org", "proj"), ProjectRef.unsafe("org2", "proj2"))
for {
ref <- Ref.of[IO, Set[ProjectRef]](Set.empty)
projectHealer = new ProjectHealer {
override def heal(project: ProjectRef): IO[Unit] = ref.update(_ + project)
}
stream = Stream.iterable(projects)
_ <- ProjectHealthJob.run(stream, projectHealer)
_ <- ref.get.assertEquals(projects)
} yield ()

}

}

0 comments on commit 21021a4

Please sign in to comment.