Skip to content

Commit dcbfaa8

Browse files
author
Felix
committed
Minor bug fixing
1 parent 4f1a38b commit dcbfaa8

File tree

14 files changed

+137
-57
lines changed

14 files changed

+137
-57
lines changed

chromeapp/src/main/scala/util/ChromePlatformService.scala

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package util.chrome
33
import _root_.api._
44
import model.stats.ContainerStats
55
import model.{BasicWebSocket, Connection, DockerEvent}
6-
import org.scalajs.dom.ext.Ajax
6+
import org.scalajs.dom.ext.{Ajax, AjaxException}
77
import org.scalajs.dom.ext.Ajax.InputData
88
import org.scalajs.dom.raw._
99
import util.EventsCustomParser.DockerEventStream
@@ -101,15 +101,26 @@ class ChromeDockerConnection(val connection: Connection) extends DockerConnectio
101101
val url = connection.url + "/" + DockerVersion
102102
val urlOptional = connection.url + "/" + DockerOptionalVersion
103103

104-
def get(path: String, timeout: Int = HttpTimeOut): Future[Response] = Ajax.get(url = s"$url$path", timeout = timeout)
105-
.map(xhr => Response(xhr.responseText, xhr.status))
104+
def get(path: String, timeout: Int = HttpTimeOut): Future[Response] = {
105+
Ajax.get(url = s"$url$path", timeout = timeout)
106+
.map(xhr => Response(xhr.responseText, xhr.status))
107+
}.recover {
108+
case ex: AjaxException => throw ConnectionException(ex.xhr.responseText)
109+
}
106110

107-
def post(path: String, data: Option[InputData] = None, headers: Map[String, String] = Map.empty, timeout: Int = HttpTimeOut) =
111+
def post(path: String, data: Option[InputData] = None, headers: Map[String, String] = Map.empty, timeout: Int = HttpTimeOut) = {
108112
Ajax.post(url = s"$url$path", data = data.getOrElse(""), headers = headers, timeout = timeout)
109113
.map(xhr => Response(xhr.responseText, xhr.status))
114+
}.recover {
115+
case ex: AjaxException => throw ConnectionException(ex.xhr.responseText)
116+
}
110117

111-
def delete(path: String, timeout: Int = HttpTimeOut): Future[Response] = Ajax.delete(url = s"$url$path", timeout = timeout)
112-
.map(xhr => Response(xhr.responseText, xhr.status))
118+
def delete(path: String, timeout: Int = HttpTimeOut): Future[Response] = {
119+
Ajax.delete(url = s"$url$path", timeout = timeout)
120+
.map(xhr => Response(xhr.responseText, xhr.status))
121+
}.recover {
122+
case ex: AjaxException => throw ConnectionException(ex.xhr.responseText)
123+
}
113124

114125
// TODO refactor
115126
def attachToContainer(containerId: String): Future[BasicWebSocket] = Future {
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
const createWindowsInstaller = require('electron-winstaller').createWindowsInstaller
2+
const path = require('path')
3+
4+
getInstallerConfig()
5+
.then(createWindowsInstaller)
6+
.catch((error) => {
7+
console.error(error.message || error)
8+
process.exit(1)
9+
})
10+
11+
function getInstallerConfig () {
12+
console.log('creating windows installer')
13+
const rootPath = path.join('./')
14+
const outPath = path.join(rootPath, 'dist')
15+
16+
return Promise.resolve({
17+
appDirectory: path.join(outPath, 'SimpleDockerUI-win32-x64/'),
18+
authors: 'Felix',
19+
name: 'SimpleDockerUI',
20+
title: 'SimpleDockerUI',
21+
noMsi: true,
22+
outputDirectory: path.join(outPath, 'windows-installer'),
23+
exe: 'SimpleDockerUI.exe',
24+
setupExe: 'SimpleDockerUI-setup.exe',
25+
setupIcon: path.join(rootPath, 'img', 'logo_small.ico')
26+
})
27+
}

electron/main.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
1+
//handle setupevents as quickly as possible
2+
const setupEvents = require('./installers/setupEvents')
3+
if (setupEvents.handleSquirrelEvent()) {
4+
// squirrel event handled and app will exit in 1000ms, so don't do anything else
5+
return;
6+
}
7+
8+
19
const electron = require('electron')
210
// Module to control application life.
311
const app = electron.app
412
// Module to create native browser window.
513
const BrowserWindow = electron.BrowserWindow
614

15+
16+
717
// Keep a global reference of the window object, if you don't, the window will
818
// be closed automatically when the JavaScript object is garbage collected.
919
let mainWindow
@@ -16,8 +26,11 @@ function createWindow () {
1626
// Create the browser window.
1727
mainWindow = new BrowserWindow({width: 1100, height: 770})
1828

29+
mainWindow.setMenu(null)
30+
1931
// Open the DevTools.
20-
mainWindow.webContents.openDevTools()
32+
33+
//mainWindow.webContents.openDevTools()
2134

2235
// and load the index.html of the app.
2336
mainWindow.loadURL(`file://${__dirname}/index.html`)

electron/src/main/scala/util/DockerModem.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,7 @@ object DockerModem {
5555
log.debug(s"Url: protocol: $protocol, host: $host")
5656
val port = connection.url.drop(protocol.size).drop("://".size).dropWhile(_ != ':').drop(1)
5757
log.debug(s"Url: port: $port")
58-
59-
val pathOpt = Option(g.process.env.DOCKER_CERT_PATH.asInstanceOf[String])
58+
val pathOpt: js.UndefOr[String] = g.process.env.DOCKER_CERT_PATH.asInstanceOf[js.UndefOr[String]]
6059
val (envCa, envCert, envKey) =
6160
if (protocol == "https" && pathOpt.isDefined) {
6261
val path = pathOpt.get

electron/src/main/scala/util/ElectronPlatformService.scala

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,28 @@ object ElectronPlatformService extends PlatformService {
2020
override def appVersion: String = {
2121
import js.Dynamic.{global => g}
2222
val electron = g.require("electron")
23-
electron.remote.app.getVersion().asInstanceOf[String]
23+
val version = electron.remote.app.getVersion().asInstanceOf[String]
24+
s"$version"
2425
}
2526

26-
val keyStoragePrefix = "v1_"
27+
val keyStoragePrefix = "v2_"
2728

28-
override def osName: Future[String] = Future.successful("Electron Platform")
29+
def os(): String = {
30+
import js.Dynamic.{global => g}
31+
val os = g.require("os")
32+
// https://nodejs.org/api/os.html#os_os_release
33+
// 'darwin', 'freebsd', 'linux', 'sunos' or 'win32'
34+
os.platform().asInstanceOf[String]
35+
}
36+
37+
override def osName: Future[String] = Future.successful(os())
2938

3039
override def save(key: String, value: String): Future[Unit] = Future.successful {
3140
dom.window.localStorage.setItem(keyStoragePrefix + key, value)
3241
}
3342

3443
override def get(key: String): Future[String] = Future {
3544
val value = Option(dom.window.localStorage.getItem(keyStoragePrefix + key))
36-
log.debug(s"Get $key=$value")
3745
value.getOrElse(throw new Exception(s"Key $key not found"))
3846
}
3947

@@ -62,11 +70,22 @@ object ElectronPlatformService extends PlatformService {
6270
throw ex
6371
}
6472

65-
val DefaultDockerURL = "unix:///var/run/docker.sock"
73+
val DefaultMacLinuxDockerURL = "unix:///var/run/docker.sock"
74+
val DefaultWindows = "http://localhost:2375"
75+
76+
def defaultUrl: Future[String] = {
77+
osName.map {
78+
case "win32" =>
79+
log.debug(s"Default docker for Windows url $DefaultWindows")
80+
DefaultWindows
81+
case "darwin" =>
82+
log.debug(s"Default docker for Mac url $DefaultMacLinuxDockerURL")
83+
DefaultMacLinuxDockerURL
84+
case other =>
85+
log.debug(s"Default docker for '$other' url $DefaultMacLinuxDockerURL")
86+
DefaultMacLinuxDockerURL
87+
}
6688

67-
def defaultUrl: Future[String] = Future.successful {
68-
log.debug(s"Default docker url $DefaultDockerURL")
69-
DefaultDockerURL
7089
}
7190

7291
override def checkIsLatestVersion(callback: (String) => Unit): Unit = CheckIsLatestVersion.check(callback)
@@ -91,19 +110,18 @@ class ElectronDockerConnection(val connection: Connection) extends DockerConnect
91110
val callback: js.Function2[js.Any, js.Dynamic, Unit] =
92111
(msg: js.Any, response: js.Dynamic) => {
93112
if (msg == null) {
94-
//log.debug(s"Processing dail response...")
95113
if (dialOptions.hijack) {
96114
processHijackResponse(onWebSocketCreated, response)
97115
} else if (dialOptions.isStream) {
98-
processStreamingResponse(onStreamingData, shouldAbort, response)
116+
def onComplete { p.success(Response("", 200)) }
117+
processStreamingResponse(onStreamingData, shouldAbort, onComplete, response)
99118
} else {
100119
val responseText = js.Dynamic.global.JSON.stringify(response).asInstanceOf[String]
101-
//log.debug(s"dial response: ${responseText.take(1000)}...")
102120
p.success(Response(responseText, 200))
103121
}
104122
} else {
105123
log.debug(s"dial fail: $msg")
106-
p.failure(new Exception(msg.toString))
124+
p.failure(new ConnectionException(msg.toString))
107125
}
108126
()
109127
}
@@ -113,7 +131,7 @@ class ElectronDockerConnection(val connection: Connection) extends DockerConnect
113131
} catch {
114132
case ex: Throwable =>
115133
log.debug(s"dial error: $ex")
116-
p.failure(ex)
134+
p.failure(ConnectionException(ex.getMessage))
117135
}
118136
p.future
119137
}
@@ -125,18 +143,20 @@ class ElectronDockerConnection(val connection: Connection) extends DockerConnect
125143
onWebSocketCreated(ws)
126144
}
127145

128-
def processStreamingResponse(onStreamingData: (String) => Unit, shouldAbort: Unit => Boolean, response: Dynamic): Unit = {
146+
def processStreamingResponse(onStreamingData: (String) => Unit, shouldAbort: Unit => Boolean, onComplete: => Unit,response: Dynamic): Unit = {
129147
log.debug(s"processing stream $response")
130148
val eventEmiter = response.asInstanceOf[EventEmitter]
131149
eventEmiter.on("data") { chuck: Dynamic =>
132150
val data = chuck.toString
133-
log.debug(s"dial stream data: ${data.take(1000)}...")
134151
onStreamingData(data)
135152
if (shouldAbort()) {
136153
log.info("Stream aborted")
137154
response.req.abort()
138155
}
139156
}
157+
eventEmiter.on("end") { _: Dynamic =>
158+
onComplete
159+
}
140160
}
141161

142162
def get(path: String, timeout: Int = HttpTimeOut): Future[Response] = {
@@ -179,8 +199,7 @@ class ElectronDockerConnection(val connection: Connection) extends DockerConnect
179199
)
180200
dial(options, onWebSocketCreated = onWebSocketCreated)
181201
.onFailure { case ex: Exception =>
182-
log.debug(s"Unable to connect WS")
183-
log.debug(s"${ex.toString}")
202+
log.debug(s"Unable to connect WS - ${ex.toString}")
184203
p.failure(ex)
185204
}
186205

@@ -194,7 +213,6 @@ class ElectronDockerConnection(val connection: Connection) extends DockerConnect
194213
val currentStream = EventStream()
195214

196215
def onStreamingData(data: String): Unit = {
197-
log.debug(s"[dockerClient.pullImage] data: $data")
198216
currentStream.data = data
199217
}
200218
val sanitizedTerm = URIUtils.encodeURIComponent(term)
@@ -203,8 +221,7 @@ class ElectronDockerConnection(val connection: Connection) extends DockerConnect
203221
log.info(s"[dockerClient.pullImage] start")
204222

205223
dial(options, onStreamingData).onComplete { result =>
206-
log.debug(s"Pull finished $result")
207-
currentStream.done
224+
currentStream.done = true
208225
}
209226

210227
currentStream
@@ -249,7 +266,6 @@ class ElectronDockerConnection(val connection: Connection) extends DockerConnect
249266
}
250267

251268
def onStreamingData(data: String): Unit = {
252-
log.debug(s"container stats update ${data.take(100)}...")
253269
val stats = StatsCustomParser.parse(data)
254270
updateUI(stats, stream)
255271
}

src/main/scala/api/DockerClient.scala

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ object DockerClientConfig {
3737
val KeepInGarbageCollection = 10
3838
}
3939

40+
case class ConnectionException(message: String) extends Exception(message)
41+
4042
trait DockerConnection {
4143
val connection: Connection
4244

@@ -110,27 +112,25 @@ case class DockerClient(con: DockerConnection) {
110112

111113
def startContainer(containerId: String): Future[ContainerInfo] = {
112114
con.post(path = s"/containers/$containerId/start").flatMap(_ => containerInfo(containerId))
113-
}.recover {
114-
case ex: AjaxException =>
115-
throw new Exception(ex.xhr.responseText)
116115
}
117116

118117
def removeContainer(containerId: String): Future[Unit] = {
119118
con.delete(path = s"/containers/$containerId").map { xhr =>
120119
log.info("[dockerClient.removeContainer] return: " + xhr.statusCode)
121120
}.recover {
122-
case ex: AjaxException =>
123-
log.info(s"Unable to delete $containerId} ${ex.xhr.responseText}")
121+
case ex: ConnectionException =>
122+
log.info(s"Unable to delete $containerId} ${ex.getMessage}")
124123
}
125124
}
126125

127126
private def removeImage(imageId: String): Future[Unit] = {
128-
con.delete(path = s"/images/$imageId").map { xhr =>
127+
val id = imageId.replace("sha256:","")
128+
con.delete(path = s"/images/$id").map { xhr =>
129129
log.info("[dockerClient.removeImage] return: " + xhr.statusCode)
130130
}.transform(identity, ex => ex match {
131-
case ex: AjaxException =>
132-
log.info(s"Unable to delete $imageId} ${ex.xhr.responseText}")
133-
new Exception(ex.xhr.responseText)
131+
case ex: ConnectionException =>
132+
log.info(s"Unable to delete $imageId} ${ex.getMessage}")
133+
ex
134134
})
135135
}
136136

@@ -196,7 +196,7 @@ case class DockerClient(con: DockerConnection) {
196196
log.info(s"Ordering images before GC...")
197197
// First need to order from top to button
198198

199-
val imagesOrdered = unorderedImages.sortWith(compareImages).map(_.image)
199+
val imagesOrdered = unorderedImages.sortWith(compareImages).map(_.image).distinct
200200

201201
status(s"Images Ordered and ready to GC: ${unorderedImages.size}")
202202
log.info("images ordered:")
@@ -210,7 +210,7 @@ case class DockerClient(con: DockerConnection) {
210210
}
211211

212212
}
213-
FutureUtils.sequenceWithDelay(tasks, FutureUtils.LongDelay, ignoreErrors = false)
213+
FutureUtils.sequenceWithDelay(tasks, FutureUtils.LongDelay, ignoreErrors = true)
214214
}.flatMap(identity)
215215

216216
tasksFut.flatMap(_ => images())

src/main/scala/model/model.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ case class DockerMetadata(connectionInfo: String,
2323
version: Version,
2424
containers: Seq[Container]) {
2525

26-
def totalContainersSize = bytesToSize(containers.map(c => c.SizeRootFs + c.SizeRw).sum)
26+
def totalContainersSize = bytesToSize(containers.map(c => c.SizeRootFs.toLong + c.SizeRw.toLong).sum)
2727
}
2828

2929
case class Info(Containers: Int,

src/main/scala/ui/pages/HomePage.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ case object HomePage extends Page {
4646
}
4747

4848
def loadContainersSize(info: DockerMetadata) =
49-
client.containersRunningWithExtraInfo.map { containers =>
49+
client.containersRunningWithExtraInfo().map { containers =>
5050
t.modState { s =>
5151
s.copy(info = Some(info.copy(containers = containers)))
5252
}

src/main/scala/ui/pages/SettingsPage.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,11 +168,11 @@ object SettingsPageRender {
168168
"To connect this app with Docker you need to enable the Docker Remote API:"),
169169
<.ul(
170170
<.li(
171-
<.a(^.href := "https://github.com/felixgborrego/docker-ui-chrome-app/wiki/Mac-OS-X", ^.target := "_blank",
171+
<.a(^.href := "https://github.com/felixgborrego/docker-ui-chrome-app/wiki/OS-X-Installable-Desktop-version", ^.target := "_blank",
172172
<.i(^.className := "fa fa-apple"), "Mac OS X config")
173173
),
174174
<.li(
175-
<.a(^.href := "https://github.com/felixgborrego/docker-ui-chrome-app/wiki/windows", ^.target := "_blank",
175+
<.a(^.href := "https://github.com/felixgborrego/docker-ui-chrome-app/wiki/OS-X-Installable-Desktop-version", ^.target := "_blank",
176176
<.i(^.className := "fa fa-windows"), "Windows config")
177177
),
178178
<.li(

0 commit comments

Comments
 (0)