Skip to content

Commit

Permalink
Better updater
Browse files Browse the repository at this point in the history
Some fixes
  • Loading branch information
jendakol committed Dec 14, 2018
1 parent db8ee6e commit 108ce27
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 30 deletions.
5 changes: 3 additions & 2 deletions app/AppModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import net.codingwell.scalaguice.ScalaModule
import org.apache.commons.lang3.SystemUtils
import org.http4s.Uri
import org.http4s.client.blaze.Http1Client
import org.http4s.client.middleware.FollowRedirect
import play.api.{Configuration, Environment}
import scalikejdbc._
import scalikejdbc.config.DBs
Expand Down Expand Up @@ -78,14 +79,14 @@ class AppModule(environment: Environment, configuration: Configuration)

bind[GithubConnector].toInstance {
new GithubConnector(
Http1Client[Task]().runSyncUnsafe(Duration.Inf),
FollowRedirect(5)(Http1Client[Task]().runSyncUnsafe(Duration.Inf)),
Uri.unsafeFromString(config.getString("updater.releasesUrl")),
App.version,
blockingScheduler
)
}

bind[Duration].annotatedWithName("updaterCheckPeriod").toInstance(config.getDuration("updater.checkPeriod").toMillis.millis)
bind[FiniteDuration].annotatedWithName("updaterCheckPeriod").toInstance(config.getDuration("updater.checkPeriod").toMillis.millis)

bind[Monitor].annotatedWithName("FilesHandler").toInstance(rootMonitor.named("fileshandler"))

Expand Down
2 changes: 1 addition & 1 deletion app/lib/App.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class App @Inject()(backupSetsExecutor: BackupSetsExecutor, updater: Updater)(li
}

object App {
final val versionStr: String = "0.1.0"
final val versionStr: String = "0.1.2"
final val version: AppVersion = AppVersion(versionStr).getOrElse(throw new IllegalArgumentException("Could not parse versionStr"))

type Result[A] = EitherT[Task, AppException, A]
Expand Down
5 changes: 1 addition & 4 deletions app/lib/FilesHandler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,7 @@ class FilesHandler @Inject()(cloudConnector: CloudConnector,
private def shouldUpload(file: File): Result[Boolean] = {
checkFileUpdated(file)
.flatMap {
case false =>
logger.debug(s"File $file not updated, is 'upload_same_content' turned on?")
settings.uploadSameContent

case false => settings.uploadSameContent
case true => pureResult(true)
}
}
Expand Down
78 changes: 61 additions & 17 deletions app/updater/GithubConnector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,32 @@ import better.files.File
import cats.data.EitherT
import cats.effect.Effect
import cats.syntax.all._
import com.typesafe.scalalogging.StrictLogging
import io.circe.Decoder
import lib.App._
import lib.AppException
import lib.AppException._
import monix.eval.Task
import monix.execution.Scheduler
import org.http4s.client.Client
import org.http4s.{Status, Uri}
import org.http4s.headers.`Content-Length`
import org.http4s.{Response, Status, Uri}
import updater.GithubConnector._

import scala.concurrent.ExecutionContext

class GithubConnector(httpClient: Client[Task], uri: Uri, appVersion: AppVersion, blockingScheduler: Scheduler)(implicit F: Effect[Task]) {
class GithubConnector(httpClient: Client[Task], uri: Uri, appVersion: AppVersion, blockingScheduler: Scheduler)(implicit F: Effect[Task])
extends StrictLogging {
def checkUpdate: Result[Option[Release]] = {
EitherT {
logger.debug(s"Checking update at $uri")

httpClient.get(uri) { resp =>
resp.bodyAsText.compile.toList.map(_.mkString.trim).flatMap { strBody =>
resp.status match {
case Status.Ok => Task.now(parse(strBody))
case Status.Ok =>
logger.debug(s"Updater check response:\n$strBody")
Task.now(parse(strBody))
case s => Task.now(Left(InvalidResponseException(s.code, strBody, "Could not download releases"): AppException))
}
}
Expand All @@ -43,25 +50,62 @@ class GithubConnector(httpClient: Client[Task], uri: Uri, appVersion: AppVersion

def download(asset: Asset): Result[File] = {
EitherT {
logger.debug(s"Downloading update from ${asset.browserDownloadUrl}")

httpClient
.get(asset.browserDownloadUrl) { resp =>
val file = File.temporaryFile("rbackup", s"update-${asset.name}").get
implicit val ec: ExecutionContext = blockingScheduler

resp.body
.through(fs2.io.writeOutputStreamAsync(Task {
file.newOutputStream
}.executeOnScheduler(blockingScheduler)))
.compile
.drain
.map(_ => Right(file): Either[AppException, File])
.onErrorRecover {
case e: IOException => Left(FileException("Could not download update", e))
}
}
resp.status match {
case Status.Ok =>
`Content-Length`.from(resp.headers) match {
case Some(clh) =>
if (clh.length == asset.size) {
download(asset, resp)
} else {
resp.bodyAsText.compile.toList.map(_.mkString).map { body =>
logger.debug(s"Content-Length header doesn't match asset size - ${clh.length} != ${asset.size}\n$body")
Left(UpdateException("Content-Length header doesn't match asset size"))
}
}

case None =>
resp.bodyAsText.compile.toList.map(_.mkString).map { body =>
logger.debug(s"Missing Content-Length header, expected ${asset.size}\n$body")
Left(UpdateException("Missing Content-Length header"))
}
}

case s =>
resp.bodyAsText.compile.toList.map(_.mkString).map { body =>
logger.debug(s"Missing Content-Length header, expected ${asset.size}\n$body")
Left(InvalidResponseException(s.code, body, "Update failure"))
}
}

}
}
}

private def download(asset: Asset, resp: Response[Task]): Task[Either[AppException, File]] = {
val file = File.temporaryFile("rbackup", s"update-${asset.name}").get
implicit val ec: ExecutionContext = blockingScheduler

resp.body
.through(fs2.io.file.writeAllAsync(file.path))
.compile
.drain
.map { _ =>
if (file.size == asset.size) {
logger.debug(s"Update downloaded to $file")
Right(file): Either[AppException, File]
} else {
logger.debug(s"Size of downloaded file mismatch - ${file.size} != ${asset.size}")
Left(UpdateException("Size of downloaded file mismatch"))
}
}
.onErrorRecover {
case e: IOException => Left(FileException("Could not download update", e))
}
}
}

object GithubConnector {
Expand Down
22 changes: 18 additions & 4 deletions app/updater/Updater.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package updater
import java.util.concurrent.atomic.AtomicBoolean

import better.files.File
import cats.data.EitherT
import com.typesafe.scalalogging.StrictLogging
import javax.inject.{Inject, Named, Singleton}
import lib.App._
import lib.AppException.UpdateException
import monix.eval.Task
import monix.execution.{Cancelable, Scheduler}
import updater.GithubConnector.Release

Expand All @@ -21,7 +23,7 @@ class Updater @Inject()(connector: GithubConnector,
private val updateRunning = new AtomicBoolean(false)

def start: Cancelable = {
logger.info("Started updates checker")
logger.info(s"Started updates checker, will check every $checkPeriod")

scheduler.scheduleAtFixedRate(1.seconds, checkPeriod) {
connector.checkUpdate
Expand All @@ -36,13 +38,24 @@ class Updater @Inject()(connector: GithubConnector,
logger.debug("Didn't find update for current version")
pureResult(())
}
.recoverWith {
case ae =>
updateRunning.set(false)
logger.warn("Could not download update", ae)
EitherT.leftT[Task, Unit](ae)
}
.value
.onErrorRecover {
case e: java.net.ConnectException =>
updateRunning.set(false)
logger.warn("Could not check for update", e)
logger.warn("Could not download update", e)
Left(UpdateException("Could not update the app", e))

case e: UpdateException =>
updateRunning.set(false)
logger.warn("Could not download update", e)
Left(e)

case NonFatal(e) =>
updateRunning.set(false)
logger.warn("Unknown error while updating the app", e)
Expand All @@ -61,7 +74,7 @@ class Updater @Inject()(connector: GithubConnector,
val dirWithUpdate = file.unzipTo(File(s"update-${release.tagName}"))
// the data are in subfolder, move them up
dirWithUpdate.children.next().children.foreach { sub =>
logger.debug(s"Moving $sub to $dirWithUpdate")
logger.trace(s"Moving $sub to $dirWithUpdate")
sub.moveToDirectory(dirWithUpdate)
}

Expand All @@ -77,7 +90,8 @@ class Updater @Inject()(connector: GithubConnector,

private def downloadUpdate(release: Release): Result[File] = {
release.assets.find(_.name == s"rbackup-client-${release.tagName}.zip") match {
case Some(asset) => connector.download(asset)
case Some(asset) =>
connector.download(asset)
case None =>
logger.warn(s"Wanted to update, but didn't found proper asset in release '${release.name}'")
failedResult(UpdateException(s"Could not found proper asset in release '${release.name}'"))
Expand Down
4 changes: 2 additions & 2 deletions conf/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ fileHandler {
}

updater {
releasesUrl = "https://github.com/jendakol/rbackup-scala-client/releases"
checkPeriod = 1 minute
releasesUrl = "https://api.github.com/repos/jendakol/rbackup-scala-client/releases"
checkPeriod = 10 minutes
}

sentry {
Expand Down

0 comments on commit 108ce27

Please sign in to comment.