From 7f6c2eda328f00f392d465d6ca064654f00013ac Mon Sep 17 00:00:00 2001 From: Shalom Ben Zvi Kazaz Date: Thu, 26 Dec 2024 02:55:21 +0200 Subject: [PATCH] persist docker compose migration --- .../plugin/settings/SettingsComponent.java | 6 +- .../plugin/analytics/BackendInfoHolder.kt | 7 - .../plugin/docker/ComposeFileProvider.kt | 7 + .../docker/DockerComposeFileMigration.kt | 81 +++++++++ .../DockerComposePersistenceFeatureService.kt | 168 ------------------ .../intellij/plugin/docker/DockerService.kt | 7 +- .../plugin/docker/DockerServiceStarter.kt | 3 - .../plugin/docker/LocalInstallationFacade.kt | 24 --- .../plugin/ui/common/UpdateBackendAction.kt | 29 +++ .../common/statuspanels/NoConnectionPanel.kt | 10 -- 10 files changed, 128 insertions(+), 214 deletions(-) create mode 100644 ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/DockerComposeFileMigration.kt delete mode 100644 ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/DockerComposePersistenceFeatureService.kt diff --git a/ide-common/src/main/java/org/digma/intellij/plugin/settings/SettingsComponent.java b/ide-common/src/main/java/org/digma/intellij/plugin/settings/SettingsComponent.java index ef3f44ed7..08b677634 100644 --- a/ide-common/src/main/java/org/digma/intellij/plugin/settings/SettingsComponent.java +++ b/ide-common/src/main/java/org/digma/intellij/plugin/settings/SettingsComponent.java @@ -383,7 +383,11 @@ private static JBLabel createBackendVersionLabel() { if (someProject != null) { var about = BackendInfoHolder.getInstance(someProject).getAbout(); if (about != null) { - backendVersionLabel.setText(about.getApplicationVersion() + " (" + DockerService.getInstance().getComposeFilePath() + ")"); + if (LocalInstallationFacade.getInstance().isLocalEngineInstalled()) { + backendVersionLabel.setText(about.getApplicationVersion() + " (" + DockerService.getInstance().getComposeFilePath() + ")"); + }else{ + backendVersionLabel.setText(about.getApplicationVersion()); + } } } return backendVersionLabel; diff --git a/ide-common/src/main/kotlin/org/digma/intellij/plugin/analytics/BackendInfoHolder.kt b/ide-common/src/main/kotlin/org/digma/intellij/plugin/analytics/BackendInfoHolder.kt index e1f4993c0..11bc6b24b 100644 --- a/ide-common/src/main/kotlin/org/digma/intellij/plugin/analytics/BackendInfoHolder.kt +++ b/ide-common/src/main/kotlin/org/digma/intellij/plugin/analytics/BackendInfoHolder.kt @@ -114,13 +114,6 @@ class BackendInfoHolder(val project: Project) : DisposableAdaptor { } - //do not call this method unless it is really necessary to update on current thread. - //never call it on EDT. - fun updateOnCurrentThread() { - update() - } - - fun getAbout(): AboutResult? { if (aboutRef.get() == null) { return getAboutInBackgroundNow() diff --git a/ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/ComposeFileProvider.kt b/ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/ComposeFileProvider.kt index 85674ff8a..73aeaa639 100644 --- a/ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/ComposeFileProvider.kt +++ b/ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/ComposeFileProvider.kt @@ -52,6 +52,12 @@ class ComposeFileProvider { } + fun getComposeFilePath(): String { + return composeFile.absolutePath + } + + + //this method should return a file, if the file does not exist, the docker operation will fail fun getComposeFile(): File { ensureComposeFileExists() @@ -234,4 +240,5 @@ class ComposeFileProvider { } + } \ No newline at end of file diff --git a/ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/DockerComposeFileMigration.kt b/ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/DockerComposeFileMigration.kt new file mode 100644 index 000000000..ed73c462c --- /dev/null +++ b/ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/DockerComposeFileMigration.kt @@ -0,0 +1,81 @@ +package org.digma.intellij.plugin.docker + +import com.intellij.openapi.diagnostic.Logger +import org.digma.intellij.plugin.common.findActiveProject +import org.digma.intellij.plugin.errorreporting.ErrorReporter +import org.digma.intellij.plugin.log.Log +import org.digma.intellij.plugin.persistence.PersistenceService +import org.digma.intellij.plugin.posthog.ActivityMonitor +import java.io.File + + +fun migrateDockerComposeFile(newDockerComposeFilePath: String, logger: Logger) { + + /* + in version 2.0.404 of the plugin we changed the location of the docker-conmpose.yml file. + from $TEMP/digma-docker/docker-compose.yml + to + ${DigmaPathManager.getLocalFilesDirectoryPath()}/digma-docker/docker-compose.yml + + when this plugin version is installed we try to find the old compose file and just copy it to the new location. + after that local engine will continue to work as usual. + + if the old file does not exist: + - if local engine is not installed, nothing to do. + + - if local engine is installed and not running: + the next time user starts the engine the new file that is bundled with this plugin version will be used. + this may be an update to the engine if the previous engine was older than the compose file that is bundled with this plugin version. + + - if local engine is installed and running: + the next time user will try to stop the engine the new file that is bundled with this plugin version will be used. + this may not succeed if the engine was installed with a much older version than what is bundled with this plugin version. + + if the steps above succeed local engine will continue to work as usual. + + */ + + // this is a one time operation, if it fails we don't want to try again. + // this code will run once after the installation of plugin version that contains this code. + + try { + //check if this is the first time this plugin version is running + val isFirstRunAfterPersistDockerCompose = PersistenceService.getInstance().isFirstRunAfterPersistDockerCompose() + if (isFirstRunAfterPersistDockerCompose) { + Log.log(logger::info, "first run after persist docker compose") + PersistenceService.getInstance().setIsFirstRunAfterPersistDockerComposeDone() + + if (!PersistenceService.getInstance().isLocalEngineInstalled()) { + Log.log(logger::info, "local engine not installed, nothing to do") + return + } + + val oldDockerComposeDir = File(System.getProperty("java.io.tmpdir"), COMPOSE_FILE_DIR_NAME) + val oldDockerComposeFile = File(oldDockerComposeDir, COMPOSE_FILE_NAME) + if (oldDockerComposeFile.exists()) { + val newDockerComposeFile = File(newDockerComposeFilePath) + Log.log(logger::info, "old compose file found, moving to new location {}", newDockerComposeFile) + oldDockerComposeFile.copyTo(newDockerComposeFile, overwrite = true) + //do not delete the old file, it may be used by other IDEs. worst case it will stay as zombie file in the user's temp directory + ////oldDockerComposeFile.delete() + Log.log(logger::info, "old compose file moved to new location {}", newDockerComposeFile) + } else { + Log.log(logger::info, "old compose file not found") + } + + findActiveProject()?.let { + ActivityMonitor.getInstance(it).registerCustomEvent( + "migrated docker compose file", mapOf( + "oldDockerComposeFileExists" to oldDockerComposeFile.exists() + ) + ) + } + + } + } catch (e: Throwable) { + Log.warnWithException(logger, e, "error migrating docker compose file") + ErrorReporter.getInstance().reportError("DockerComposeFileMigration.migrateDockerComposeFile", e) + } +} + + diff --git a/ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/DockerComposePersistenceFeatureService.kt b/ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/DockerComposePersistenceFeatureService.kt deleted file mode 100644 index f16bc41a2..000000000 --- a/ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/DockerComposePersistenceFeatureService.kt +++ /dev/null @@ -1,168 +0,0 @@ -package org.digma.intellij.plugin.docker - -import com.intellij.openapi.components.Service -import com.intellij.openapi.diagnostic.Logger -import com.intellij.openapi.observable.properties.AtomicBooleanProperty -import com.intellij.openapi.project.Project -import org.digma.intellij.plugin.analytics.AnalyticsService -import org.digma.intellij.plugin.analytics.BackendConnectionMonitor -import org.digma.intellij.plugin.analytics.BackendInfoHolder -import org.digma.intellij.plugin.common.Backgroundable -import org.digma.intellij.plugin.log.Log -import org.digma.intellij.plugin.persistence.PersistenceService -import org.digma.intellij.plugin.posthog.ActivityMonitor -import java.io.File -import java.nio.file.Files -import java.util.concurrent.locks.ReentrantLock -import kotlin.concurrent.withLock - - -@Service(Service.Level.APP) -class DockerComposePersistenceFeatureService { - - private val logger = Logger.getInstance(this::class.java) - private val myLock = ReentrantLock(true) - - var updateInProgress = AtomicBooleanProperty(false) - - - fun migrateToNewComposeFileLocation(project: Project) { - - myLock.withLock { - - val isFirstRunAfterPersistDockerCompose = PersistenceService.getInstance().isFirstRunAfterPersistDockerCompose() - if (isFirstRunAfterPersistDockerCompose) { - Log.log(logger::info, "first run after persist docker compose") - PersistenceService.getInstance().setIsFirstRunAfterPersistDockerComposeDone() - - //nothing to do if local engine is not installed - if (!PersistenceService.getInstance().isLocalEngineInstalled()) { - Log.log(logger::info, "local engine not installed, nothing to do") - return - } - - - //run with background task so user will see a status notification when that happens - Backgroundable.runInNewBackgroundThread(project, "updating digma engine") { - - try { - updateInProgress.set(true) - - val eventProperties = mutableMapOf() - - val runningEngine = discoverActualRunningEngine(project) - eventProperties["local engine installed"] = runningEngine.runningDigmaInstances.contains(DigmaInstallationType.localEngine) - Log.log(logger::info, "discovered running engine: {}", runningEngine) - val dockerComposeCmd = getDockerComposeCommand() ?: return@runInNewBackgroundThread - var oldComposeFileFound = false - val engine = Engine() - - val oldDockerComposeDir = File(System.getProperty("java.io.tmpdir"), COMPOSE_FILE_DIR_NAME) - val oldDockerComposeFile = File(oldDockerComposeDir, COMPOSE_FILE_NAME) - if (oldDockerComposeFile.exists()) { - oldComposeFileFound = true - eventProperties["old compose file found"] = true - Log.log(logger::info, "old docker compose found") - Log.log(logger::info, "removing engine with old compose file") - engine.remove(project, oldDockerComposeFile, dockerComposeCmd, false) - Log.log(logger::info, "deleting old compose file") - Files.deleteIfExists(oldDockerComposeFile.toPath()) - Files.deleteIfExists(oldDockerComposeDir.toPath()) - waitForNoConnection(project) - } else { - eventProperties["old compose file found"] = false - Log.log(logger::info, "old docker compose not found") - } - - - - if (runningEngine.runningDigmaInstances.contains(DigmaInstallationType.localEngine)) { - Log.log(logger::info, "local engine was running, starting it again with new compose file") - - val newComposeFile = ComposeFileProvider().getComposeFile() - - if (oldComposeFileFound) { - Log.log(logger::info, "running engine.up with new compose file") - engine.up(project, newComposeFile, dockerComposeCmd) - waitForConnection(project) - } else { - Log.log(logger::info, "running engine.down with new compose file") - engine.down(project, newComposeFile, dockerComposeCmd) - waitForNoConnection(project) - Log.log(logger::info, "running engine.up with new compose file") - engine.up(project, newComposeFile, dockerComposeCmd) - waitForConnection(project) - } - } - - BackendInfoHolder.getInstance(project).updateOnCurrentThread() - ActivityMonitor.getInstance(project) - .registerCustomEvent("digma engine updated after docker compose persist feature", eventProperties) - }finally { - updateInProgress.set(false) - } - } - } - } - } - - private fun waitForNoConnection(project: Project) { - - repeat(24) { count -> - if (BackendConnectionMonitor.getInstance(project).isConnectionError()) { - return@repeat - } - try { - Log.log(logger::warn, "waiting for no-connection {}", count) - Thread.sleep(100) - } catch (e: InterruptedException) { - //ignore - } - - try { - AnalyticsService.getInstance(project).environments - } catch (e: Throwable) { - //Log.warnWithException(logger, e, "error in waitForConnection") - //ignore - } - } - - if (BackendConnectionMonitor.getInstance(project).isConnectionOk()) { - Log.log(logger::warn, "connection status still ok") - } else { - Log.log(logger::warn, "status is no connection") - } - } - - - //run this to try and update the connection status as soon as possible, call any api that will succeed and update the status - private fun waitForConnection(project: Project) { - - repeat(24) { count -> - if (BackendConnectionMonitor.getInstance(project).isConnectionOk()) { - return@repeat - } - - try { - Log.log(logger::warn, "waiting for connection {}", count) - Thread.sleep(1000) - } catch (e: InterruptedException) { - //ignore - } - - try { - AnalyticsService.getInstance(project).environments - } catch (e: Throwable) { - //ignore - } - } - - - if (BackendConnectionMonitor.getInstance(project).isConnectionOk()) { - Log.log(logger::warn, "status changed to connection ok") - } else { - Log.log(logger::warn, "still no connection status") - } - } - -} \ No newline at end of file diff --git a/ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/DockerService.kt b/ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/DockerService.kt index dd24b500c..e164689ab 100644 --- a/ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/DockerService.kt +++ b/ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/DockerService.kt @@ -49,8 +49,13 @@ class DockerService { } + init { + migrateDockerComposeFile(composeFileProvider.getComposeFilePath(), logger) + } + + fun getComposeFilePath(): String { - return composeFileProvider.getComposeFile().absolutePath + return composeFileProvider.getComposeFilePath() } diff --git a/ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/DockerServiceStarter.kt b/ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/DockerServiceStarter.kt index 2f7f037b7..b538a2e23 100644 --- a/ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/DockerServiceStarter.kt +++ b/ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/DockerServiceStarter.kt @@ -10,10 +10,7 @@ class DockerServiceStarter : DigmaProjectActivity() { override fun executeProjectStartup(project: Project) { //initialize the docker service as early as possible - //todo: probably not necessary because it does nothing in its init block DockerService.getInstance() - - service().migrateToNewComposeFileLocation(project) } } \ No newline at end of file diff --git a/ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/LocalInstallationFacade.kt b/ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/LocalInstallationFacade.kt index 797c6e651..268517613 100644 --- a/ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/LocalInstallationFacade.kt +++ b/ide-common/src/main/kotlin/org/digma/intellij/plugin/docker/LocalInstallationFacade.kt @@ -49,12 +49,6 @@ class LocalInstallationFacade { fun installEngine(project: Project, resultTask: Consumer) { - if (service().updateInProgress.get()) { - Log.log(logger::trace, "installEngine rejected because DockerComposePersistenceFeature update in progress") - resultTask.accept("install engine rejected because DockerComposePersistenceFeature update in progress") - return - } - Log.log(logger::trace, "installEngine requested") if (isLocalEngineInstalled()) { Log.log(logger::trace, "installEngine rejected because already installed") @@ -77,12 +71,6 @@ class LocalInstallationFacade { fun stopEngine(project: Project, resultTask: Consumer) { - if (service().updateInProgress.get()) { - Log.log(logger::trace, "stopEngine rejected because DockerComposePersistenceFeature update in progress") - resultTask.accept("stop engine rejected because DockerComposePersistenceFeature update in progress") - return - } - Log.log(logger::trace, "stopEngine requested") doOperation(OP.STOP, resultTask) { DockerService.getInstance().stopEngine(project, myResultTask) @@ -91,12 +79,6 @@ class LocalInstallationFacade { fun startEngine(project: Project, resultTask: Consumer) { - if (service().updateInProgress.get()) { - Log.log(logger::trace, "startEngine rejected because DockerComposePersistenceFeature update in progress") - resultTask.accept("start engine rejected because DockerComposePersistenceFeature update in progress") - return - } - Log.log(logger::trace, "startEngine requested") if (isLocalEngineRunning(project)) { Log.log(logger::trace, "startEngine rejected because already running") @@ -111,12 +93,6 @@ class LocalInstallationFacade { fun removeEngine(project: Project, resultTask: Consumer) { - if (service().updateInProgress.get()) { - Log.log(logger::trace, "removeEngine rejected because DockerComposePersistenceFeature update in progress") - resultTask.accept("remove engine rejected because DockerComposePersistenceFeature update in progress") - return - } - Log.log(logger::trace, "removeEngine requested") doOperation(OP.REMOVE, resultTask) { DockerService.getInstance().removeEngine(project, myResultTask) diff --git a/src/main/kotlin/org/digma/intellij/plugin/ui/common/UpdateBackendAction.kt b/src/main/kotlin/org/digma/intellij/plugin/ui/common/UpdateBackendAction.kt index 6ab883da5..3efd97b5b 100644 --- a/src/main/kotlin/org/digma/intellij/plugin/ui/common/UpdateBackendAction.kt +++ b/src/main/kotlin/org/digma/intellij/plugin/ui/common/UpdateBackendAction.kt @@ -3,11 +3,15 @@ package org.digma.intellij.plugin.ui.common import com.intellij.codeInsight.hint.HintManager import com.intellij.ide.BrowserUtil import com.intellij.openapi.components.service +import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project import com.intellij.ui.awt.RelativePoint import com.intellij.util.ui.JBUI.Borders.empty +import org.digma.intellij.plugin.analytics.AnalyticsService +import org.digma.intellij.plugin.analytics.BackendConnectionMonitor import org.digma.intellij.plugin.docker.LocalInstallationFacade import org.digma.intellij.plugin.errorreporting.ErrorReporter +import org.digma.intellij.plugin.log.Log import org.digma.intellij.plugin.model.rest.version.BackendDeploymentType import org.digma.intellij.plugin.service.EditorService import org.digma.intellij.plugin.ui.common.Links.DIGMA_DOCKER_APP_URL @@ -22,6 +26,7 @@ const val UPDATE_GUIDE_HELM_NAME = "upgrade_helm.md" class UpdateBackendAction { + private val logger = Logger.getInstance(this::class.java) fun updateBackend(project: Project, backendDeploymentType: BackendDeploymentType, sourceComponent: JComponent?) { @@ -53,6 +58,7 @@ class UpdateBackendAction { mapOf("exitValue" to exitValue) ) } + tryToUpdateConnectionStatusSoon(project) } } else { EditorService.getInstance(project) @@ -72,4 +78,27 @@ class UpdateBackendAction { } + //call some api to refresh the connection status as soon as possible + private fun tryToUpdateConnectionStatusSoon(project: Project){ + repeat(24) { count -> + if (BackendConnectionMonitor.getInstance(project).isConnectionOk()) { + return@repeat + } + + try { + Log.log(logger::warn, "waiting for connection {} [t:{}]", count,Thread.currentThread().name) + Thread.sleep(1000) + } catch (e: InterruptedException) { + //ignore + } + + try { + AnalyticsService.getInstance(project).environments + } catch (e: Throwable) { + //ignore + } + } + } + + } \ No newline at end of file diff --git a/src/main/kotlin/org/digma/intellij/plugin/ui/common/statuspanels/NoConnectionPanel.kt b/src/main/kotlin/org/digma/intellij/plugin/ui/common/statuspanels/NoConnectionPanel.kt index e277e6e42..20b1c242d 100644 --- a/src/main/kotlin/org/digma/intellij/plugin/ui/common/statuspanels/NoConnectionPanel.kt +++ b/src/main/kotlin/org/digma/intellij/plugin/ui/common/statuspanels/NoConnectionPanel.kt @@ -1,7 +1,6 @@ package org.digma.intellij.plugin.ui.common.statuspanels import com.intellij.openapi.Disposable -import com.intellij.openapi.components.service import com.intellij.openapi.options.ShowSettingsUtil import com.intellij.openapi.project.Project import com.intellij.ui.JBColor @@ -11,7 +10,6 @@ import org.digma.intellij.plugin.analytics.BackendInfoHolder import org.digma.intellij.plugin.analytics.refreshEnvironmentsNowOnBackground import org.digma.intellij.plugin.common.Backgroundable import org.digma.intellij.plugin.common.EDT -import org.digma.intellij.plugin.docker.DockerComposePersistenceFeatureService import org.digma.intellij.plugin.posthog.ActivityMonitor import org.digma.intellij.plugin.posthog.UserActionOrigin import org.digma.intellij.plugin.scheduling.oneShotTask @@ -95,14 +93,6 @@ fun createNoConnectionPanel(project: Project, parentDisposable: Disposable):JPan ToolWindowShower.getInstance(project).showToolWindow() } } - - service().updateInProgress.afterChange { - setupLink.isEnabled = !it - } - setupLink.isEnabled = service().updateInProgress.get() - - - buttonsPanel.add(setupLink, BorderLayout.EAST) mainPanel.add(buttonsPanel,constraints)