@@ -12,24 +12,31 @@ import com.intellij.openapi.project.Project
1212import com.intellij.openapi.ui.MessageConstants
1313import com.intellij.openapi.ui.Messages
1414import com.intellij.openapi.util.SystemInfo
15- import org.digma.intellij.plugin.analytics.BackendConnectionMonitor
1615import org.digma.intellij.plugin.common.Backgroundable
1716import org.digma.intellij.plugin.errorreporting.ErrorReporter
1817import org.digma.intellij.plugin.log.Log
1918import org.digma.intellij.plugin.persistence.PersistenceService
2019import org.digma.intellij.plugin.posthog.ActivityMonitor
20+ import org.jetbrains.annotations.ApiStatus.Internal
2121import java.util.function.Consumer
2222import java.util.function.Supplier
2323
24-
24+ /* *
25+ * this service is a front end to docker, it can install,upgrade,stop,start,remove and also provide
26+ * information about docker installation and running instances.
27+ * this service is a kind of CRUD service, the core operation of install,upgrade,stop,start,remove
28+ * should never be called directly because it does not manage single access and locking, meaning if install is called
29+ * multiple times concurrently it may cause failures. please never call these operations directly, always call
30+ * LocalInstallationFacade for these CRUD operations,LocalInstallationFacade manages single access and logical flows.
31+ * information services may be called directly
32+ */
2533@Service(Service .Level .APP )
2634class DockerService {
2735
2836 private val logger = Logger .getInstance(this ::class .java)
2937
3038 private val engine = Engine ()
3139 private val downloader = Downloader ()
32- private var installationInProgress: Boolean = false
3340
3441 companion object {
3542
@@ -44,58 +51,24 @@ class DockerService {
4451
4552 init {
4653
47- if (isEngineInstalled ()) {
54+ if (PersistenceService .getInstance().isLocalEngineInstalled ()) {
4855 // this will happen on IDE start,
4956 // DockerService is an application service so Downloader will be singleton per application
5057 downloader.downloadComposeFile()
5158 }
5259 }
5360
5461
55- fun getActualRunningEngine (project : Project ): DigmaInstallationStatus {
56- return discoverActualRunningEngine(project)
57- }
58-
59-
60- fun getCurrentDigmaInstallationStatusOnConnectionLost (): DigmaInstallationStatus {
61- return discoverActualRunningEngine(false )
62- }
63-
64-
65- fun getCurrentDigmaInstallationStatusOnConnectionGained (): DigmaInstallationStatus {
66- return discoverActualRunningEngine(true )
67- }
68-
69-
70- fun isInstallationInProgress (): Boolean {
71- return installationInProgress
72- }
7362
7463 fun isDockerInstalled (): Boolean {
75- return isInstalled(DOCKER_COMMAND ) || isInstalled( DOCKER_COMPOSE_COMMAND )
64+ return isInstalled(DOCKER_COMMAND )
7665 }
7766
78-
79- fun isEngineInstalled (): Boolean {
80- return PersistenceService .getInstance().isLocalEngineInstalled()
81- }
82-
83-
84- fun isEngineRunning (project : Project ): Boolean {
85- return isEngineInstalled() && BackendConnectionMonitor .getInstance(project).isConnectionOk()
67+ fun isDockerComposeInstalled (): Boolean {
68+ return isInstalled(DOCKER_COMPOSE_COMMAND )
8669 }
8770
8871
89- private fun isDockerDaemonDownExitValue (exitValue : String ): Boolean {
90- return exitValue.contains(" Cannot connect to the Docker daemon" , true ) || // mac, linux
91- exitValue.contains(" docker daemon is not running" , true ) || // win
92- // this is an error on windows with docker desktop that will be solved by starting docker desktop
93- (exitValue.contains(" error during connect" , true ) && exitValue.contains(" The system cannot find the file specified" , true )) || // win
94- exitValue.contains(" Error while fetching server API version" , true )
95- }
96-
97-
98-
9972 fun collectDigmaContainerLog (): String {
10073 try {
10174
@@ -113,7 +86,7 @@ class DockerService {
11386 val getLogCommand = GeneralCommandLine (dockerCmd, " logs" , " --tail" , " 1000" , " $containerId " )
11487 .withParentEnvironmentType(GeneralCommandLine .ParentEnvironmentType .CONSOLE )
11588
116- val processOutput: ProcessOutput = ExecUtil .execAndGetOutput(getLogCommand)
89+ val processOutput: ProcessOutput = ExecUtil .execAndGetOutput(getLogCommand)
11790 return processOutput.toString()
11891
11992 } catch (ex: Exception ) {
@@ -124,14 +97,8 @@ class DockerService {
12497 }
12598
12699
100+ @Internal
127101 fun installEngine (project : Project , resultTask : Consumer <String >) {
128- installationInProgress = true
129-
130- PersistenceService .getInstance().setLocalEngineInstalled(true )
131-
132- val onCompleted = Consumer { _: String ->
133- installationInProgress = false
134- }.andThen(resultTask)
135102
136103 ActivityMonitor .getInstance(project).registerDigmaEngineEventStart(" installEngine" , mapOf ())
137104
@@ -155,65 +122,85 @@ class DockerService {
155122 }
156123 }
157124
158- notifyResult(exitValue, onCompleted )
125+ notifyResult(exitValue, resultTask )
159126 } else {
160127 ActivityMonitor .getInstance(project).registerDigmaEngineEventError(" installEngine" , " could not find docker compose command" )
161128 Log .log(logger::warn, " could not find docker compose command" )
162- notifyResult(NO_DOCKER_COMPOSE_COMMAND , onCompleted )
129+ notifyResult(NO_DOCKER_COMPOSE_COMMAND , resultTask )
163130 }
164131 } else {
165132 ActivityMonitor .getInstance(project).registerDigmaEngineEventError(" installEngine" , " Failed to download compose file" )
166133 Log .log(logger::warn, " Failed to download compose file" )
167- notifyResult(" Failed to download compose file" , onCompleted )
134+ notifyResult(" Failed to download compose file" , resultTask )
168135 }
169136
170137 } catch (e: Throwable ) {
171138 ErrorReporter .getInstance().reportError(project, " DockerService.installEngine" , e)
172139 ActivityMonitor .getInstance(project).registerDigmaEngineEventError(" installEngine" , " Failed in installEngine $e " )
173140 Log .warnWithException(logger, e, " Failed install docker engine {}" , e)
174- notifyResult(" Failed to install docker engine: $e " , onCompleted )
141+ notifyResult(" Failed to install docker engine: $e " , resultTask )
175142 } finally {
176143 ActivityMonitor .getInstance(project).registerDigmaEngineEventEnd(" installEngine" , mapOf ())
177144 }
178145 }
179146 }
180147
181-
182- fun upgradeEngine (project : Project ) {
148+ @Internal
149+ fun upgradeEngine (project : Project , resultTask : Consumer < String > ) {
183150
184151 ActivityMonitor .getInstance(project).registerDigmaEngineEventStart(" upgradeEngine" , mapOf ())
185152
186153 Backgroundable .runInNewBackgroundThread(project, " upgrading digma engine" ) {
187154
155+ // stop the engine before upgrade using the current compose file, before downloading a new file.
156+ // the engine should be up otherwise the upgrade would not be triggered.
157+ // ignore errors, we'll try to upgrade anyway after that.
158+ try {
159+ val dockerComposeCmd = getDockerComposeCommand()
160+ dockerComposeCmd?.let {
161+ engine.down(project, downloader.composeFile, it, false )
162+ }
163+ } catch (e: Throwable ) {
164+ ErrorReporter .getInstance().reportError(project, " DockerService.upgradeEngine" , e)
165+ Log .warnWithException(logger, e, " Failed to stop docker engine {}" , e)
166+ }
167+
168+
188169 try {
189170 if (downloader.downloadComposeFile(true )) {
190171 val dockerComposeCmd = getDockerComposeCommand()
191172
192173 if (dockerComposeCmd != null ) {
193174 val exitValue = engine.up(project, downloader.composeFile, dockerComposeCmd)
175+ // in upgrade there is no need to check if daemon is down because upgrade will not be triggered if the engine is not running.
194176 if (exitValue != " 0" ) {
195177 ActivityMonitor .getInstance(project).registerDigmaEngineEventError(" upgradeEngine" , exitValue)
196178 Log .log(logger::warn, " error upgrading engine {}" , exitValue)
197179 }
180+
181+ notifyResult(exitValue, resultTask)
198182 } else {
199183 ActivityMonitor .getInstance(project).registerDigmaEngineEventError(" upgradeEngine" , " could not find docker compose command" )
200184 Log .log(logger::warn, " could not find docker compose command" )
185+ notifyResult(NO_DOCKER_COMPOSE_COMMAND , resultTask)
201186 }
202187 } else {
203188 ActivityMonitor .getInstance(project).registerDigmaEngineEventError(" upgradeEngine" , " Failed to download compose file" )
204189 Log .log(logger::warn, " Failed to download compose file" )
190+ notifyResult(" Failed to download compose file" , resultTask)
205191 }
206192 } catch (e: Exception ) {
207193 ErrorReporter .getInstance().reportError(project, " DockerService.upgradeEngine" , e)
208194 ActivityMonitor .getInstance(project).registerDigmaEngineEventError(" upgradeEngine" , " Failed in upgradeEngine $e " )
209195 Log .warnWithException(logger, e, " Failed install docker engine {}" , e)
196+ notifyResult(" Failed to upgrade docker engine: $e " , resultTask)
210197 } finally {
211198 ActivityMonitor .getInstance(project).registerDigmaEngineEventEnd(" upgradeEngine" , mapOf ())
212199 }
213200 }
214201 }
215202
216-
203+ @Internal
217204 fun stopEngine (project : Project , resultTask : Consumer <String >) {
218205
219206 ActivityMonitor .getInstance(project).registerDigmaEngineEventStart(" stopEngine" , mapOf ())
@@ -254,6 +241,7 @@ class DockerService {
254241 }
255242 }
256243
244+ @Internal
257245 fun startEngine (project : Project , resultTask : Consumer <String >) {
258246
259247 ActivityMonitor .getInstance(project).registerDigmaEngineEventStart(" startEngine" , mapOf ())
@@ -265,15 +253,19 @@ class DockerService {
265253 val dockerComposeCmd = getDockerComposeCommand()
266254
267255 if (dockerComposeCmd != null ) {
268- // we try to detect errors when running the docker command. engine.start executes docker-compose up,
269- // if executing docker-compose up while containers exist it will print many errors that are ok but
270- // that interferes with our attempt to detect errors.
271- // so running down and then up solves it
272- engine.down(project, downloader.composeFile, dockerComposeCmd, false )
256+
273257 try {
258+ // we try to detect errors when running the docker command. engine.start executes docker-compose up,
259+ // if executing docker-compose up while containers exist it will print many errors that are ok but
260+ // that interferes with our attempt to detect errors.
261+ // so running down and then up solves it.
262+ // in any case it's better to stop before start because we don't know the state of the engine, maybe its
263+ // partially up and start will fail.
264+ engine.down(project, downloader.composeFile, dockerComposeCmd, false )
274265 Thread .sleep(2000 )
275266 } catch (e: Exception ) {
276- // ignore
267+ ErrorReporter .getInstance().reportError(project, " DockerService.startEngine" , e)
268+ Log .warnWithException(logger, e, " Failed to stop docker engine {}" , e)
277269 }
278270
279271 var exitValue = engine.start(project, downloader.composeFile, dockerComposeCmd)
@@ -310,7 +302,7 @@ class DockerService {
310302
311303 }
312304
313-
305+ @Internal
314306 fun removeEngine (project : Project , resultTask : Consumer <String >) {
315307
316308 ActivityMonitor .getInstance(project).registerDigmaEngineEventStart(" removeEngine" , mapOf ())
@@ -363,6 +355,15 @@ class DockerService {
363355 }
364356
365357
358+ private fun isDockerDaemonDownExitValue (exitValue : String ): Boolean {
359+ return exitValue.contains(" Cannot connect to the Docker daemon" , true ) || // mac, linux
360+ exitValue.contains(" docker daemon is not running" , true ) || // win
361+ // this is an error on windows with docker desktop that will be solved by starting docker desktop
362+ (exitValue.contains(" error during connect" , true ) && exitValue.contains(" The system cannot find the file specified" , true )) || // win
363+ exitValue.contains(" Error while fetching server API version" , true )
364+ }
365+
366+
366367 private fun doRetryFlowWhenDockerDaemonIsDown (project : Project , prevExitValue : String , runCommand : Supplier <String >): String {
367368
368369 val eventName = " docker-daemon-is-down"
@@ -413,6 +414,7 @@ class DockerService {
413414 return exitValue
414415 }
415416
417+
416418 private fun tryStartDockerDaemon (project : Project ) {
417419
418420 Log .log(logger::info, " Trying to start docker daemon" )
@@ -468,5 +470,4 @@ class DockerService {
468470 }
469471
470472
471-
472473}
0 commit comments