Skip to content

Commit

Permalink
chore: UI improvements (#37)
Browse files Browse the repository at this point in the history
* chore: UI improvements

Signed-off-by: Anatolii Bazko <[email protected]>

* chore: Done't allow to connect if connection has been already established

Signed-off-by: Anatolii Bazko <[email protected]>

---------

Signed-off-by: Anatolii Bazko <[email protected]>
  • Loading branch information
tolusha authored Mar 21, 2024
1 parent fa77a25 commit 433c4d0
Show file tree
Hide file tree
Showing 16 changed files with 679 additions and 301 deletions.
98 changes: 71 additions & 27 deletions src/main/kotlin/com/github/devspaces/gateway/DevSpacesConnection.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,54 +13,98 @@ package com.github.devspaces.gateway

import com.github.devspaces.gateway.openshift.DevWorkspaces
import com.github.devspaces.gateway.openshift.Pods
import com.github.devspaces.gateway.openshift.Utils
import com.github.devspaces.gateway.server.RemoteServer
import com.jetbrains.gateway.thinClientLink.LinkedClientManager
import com.jetbrains.gateway.thinClientLink.ThinClientHandle
import com.jetbrains.rd.util.lifetime.Lifetime
import org.bouncycastle.util.Arrays
import java.io.Closeable
import io.kubernetes.client.openapi.ApiException
import java.io.IOException
import java.net.URI

class DevSpacesConnection(private val devSpacesContext: DevSpacesContext) {
@Throws(Exception::class)
@Suppress("UnstableApiUsage")
fun connect(): ThinClientHandle {
val isStarted = Utils.getValue(devSpacesContext.devWorkspace, arrayOf("spec", "started")) as Boolean
val dwName = Utils.getValue(devSpacesContext.devWorkspace, arrayOf("metadata", "name")) as String
val dwNamespace = Utils.getValue(devSpacesContext.devWorkspace, arrayOf("metadata", "namespace")) as String
fun connect(
onConnected: () -> Unit,
onDisconnected: () -> Unit,
onDevWorkspaceStopped: () -> Unit,
): ThinClientHandle {
if (devSpacesContext.isConnected)
throw IOException(String.format("Already connected to %s", devSpacesContext.devWorkspace.metadata.name))

if (!isStarted) {
DevWorkspaces(devSpacesContext.client).start(dwNamespace, dwName)
devSpacesContext.isConnected = true
try {
return doConnection(onConnected, onDevWorkspaceStopped, onDisconnected)
} catch (e: Exception) {
devSpacesContext.isConnected = false
throw e
}
DevWorkspaces(devSpacesContext.client).waitRunning(dwNamespace, dwName)
}

@Throws(Exception::class)
@Suppress("UnstableApiUsage")
private fun doConnection(
onConnected: () -> Unit,
onDevWorkspaceStopped: () -> Unit,
onDisconnected: () -> Unit
): ThinClientHandle {
startAndWaitDevWorkspace()

val remoteServer = RemoteServer(devSpacesContext)
remoteServer.waitProjects()
val remoteServer = RemoteServer(devSpacesContext).also { it.waitProjectsReady() }
val projectStatus = remoteServer.getProjectStatus()

if (projectStatus.joinLink.isEmpty()) throw IOException(
String.format(
"Connection link to the remote server not found in the DevWorkspace: %s",
dwName
val client = LinkedClientManager
.getInstance()
.startNewClient(
Lifetime.Eternal,
URI(projectStatus.joinLink),
"",
onConnected
)
)

val client = LinkedClientManager.getInstance().startNewClient(Lifetime.Eternal, URI(projectStatus.joinLink), "")
val forwarder = Pods(devSpacesContext.client).forward(remoteServer.pod, 5990, 5990)

client.run {
lifetime.onTermination(forwarder)
lifetime.onTermination(
Closeable {
val projectStatus = remoteServer.getProjectStatus()
if (Arrays.isNullOrEmpty(projectStatus.projects)) {
DevWorkspaces(devSpacesContext.client).stop(dwNamespace, dwName)
}
}
)
lifetime.onTermination { forwarder.close() }
lifetime.onTermination {
if (remoteServer.waitProjectsTerminated())
DevWorkspaces(devSpacesContext.client)
.stop(
devSpacesContext.devWorkspace.metadata.namespace,
devSpacesContext.devWorkspace.metadata.name
)
.also { onDevWorkspaceStopped() }
}
lifetime.onTermination { devSpacesContext.isConnected = false }
lifetime.onTermination(onDisconnected)
}

return client
}

@Throws(IOException::class, ApiException::class)
private fun startAndWaitDevWorkspace() {
if (!devSpacesContext.devWorkspace.spec.started) {
DevWorkspaces(devSpacesContext.client)
.start(
devSpacesContext.devWorkspace.metadata.namespace,
devSpacesContext.devWorkspace.metadata.name
)
}

if (!DevWorkspaces(devSpacesContext.client)
.waitPhase(
devSpacesContext.devWorkspace.metadata.namespace,
devSpacesContext.devWorkspace.metadata.name,
DevWorkspaces.RUNNING,
DevWorkspaces.RUNNING_TIMEOUT
)
) throw IOException(
String.format(
"DevWorkspace '%s' is not running after %d seconds",
devSpacesContext.devWorkspace.metadata.name,
DevWorkspaces.RUNNING_TIMEOUT
)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ package com.github.devspaces.gateway

import com.github.devspaces.gateway.openshift.DevWorkspaces
import com.github.devspaces.gateway.openshift.OpenShiftClientFactory
import com.github.devspaces.gateway.openshift.Utils
import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.ui.dsl.builder.Align.Companion.CENTER
import com.intellij.ui.dsl.builder.panel
Expand All @@ -32,7 +31,10 @@ private const val DW_NAME = "dwName"
*/
class DevSpacesConnectionProvider : GatewayConnectionProvider {

override suspend fun connect(parameters: Map<String, String>, requestor: ConnectionRequestor): GatewayConnectionHandle? {
override suspend fun connect(
parameters: Map<String, String>,
requestor: ConnectionRequestor
): GatewayConnectionHandle? {

Check warning on line 37 in src/main/kotlin/com/github/devspaces/gateway/DevSpacesConnectionProvider.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Redundant nullable return type

'connect' always returns non-null type
thisLogger().debug("Launched Dev Spaces connection provider", parameters)

val dwNamespace = parameters[DW_NAMESPACE]
Expand All @@ -49,15 +51,9 @@ class DevSpacesConnectionProvider : GatewayConnectionProvider {

val ctx = DevSpacesContext()
ctx.client = OpenShiftClientFactory().create()
ctx.devWorkspace = DevWorkspaces(ctx.client).get(dwNamespace, dwName)

// TODO: probably, we don't need to specify `dwNamespace` here
// as `ctx.client` should know it from the local `.kube/config`
val devWorkspaces = DevWorkspaces(ctx.client).list(dwNamespace) as Map<*, *>
val devWorkspaceItems = devWorkspaces["items"] as List<*>
val list = devWorkspaceItems.filter { (Utils.getValue(it, arrayOf("metadata", "name")) as String) == dwName }
ctx.devWorkspace = list[0]!!

val thinClient = DevSpacesConnection(ctx).connect()
val thinClient = DevSpacesConnection(ctx).connect({}, {}, {})

val connectionFrameComponent = panel {
indent {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
*/
package com.github.devspaces.gateway

import com.github.devspaces.gateway.openshift.DevWorkspace
import io.kubernetes.client.openapi.ApiClient

class DevSpacesContext() {
class DevSpacesContext {
lateinit var client: ApiClient
lateinit var devWorkspace: Any
lateinit var devWorkspace: DevWorkspace
var isConnected = false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright (c) 2024 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package com.github.devspaces.gateway.openshift

import java.util.Collections.emptyMap

data class DevWorkspace(

Check warning on line 16 in src/main/kotlin/com/github/devspaces/gateway/openshift/DevWorkspace.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

'equals()' and 'hashCode()' not paired

Class has `equals()` defined but does not define `hashCode()`
val metadata: DevWorkspaceObjectMeta,
val spec: DevWorkspaceSpec,
val status: DevWorkspaceStatus
) {
companion object {
fun from(map: Any?) = object {
val metadata = Utils.getValue(map, arrayOf("metadata")) ?: emptyMap<String, Any>()
val spec = Utils.getValue(map, arrayOf("spec")) ?: emptyMap<String, Any>()
val status = Utils.getValue(map, arrayOf("status")) ?: emptyMap<String, Any>()

val data = DevWorkspace(
DevWorkspaceObjectMeta.from(metadata),
DevWorkspaceSpec.from(spec),
DevWorkspaceStatus.from(status)
)
}.data
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as DevWorkspace

if (metadata.name != other.metadata.name) return false
if (metadata.namespace != other.metadata.namespace) return false

return true
}
}

data class DevWorkspaceObjectMeta(
val name: String,
val namespace: String
) {
companion object {
fun from(map: Any) = object {
val name = Utils.getValue(map, arrayOf("name"))
val namespace = Utils.getValue(map, arrayOf("namespace"))

val data = DevWorkspaceObjectMeta(
name as String,
namespace as String
)
}.data
}
}

data class DevWorkspaceSpec(
val started: Boolean
) {
companion object {
fun from(map: Any) = object {
val started = Utils.getValue(map, arrayOf("started")) ?: false

val data = DevWorkspaceSpec(
started as Boolean
)
}.data
}
}

data class DevWorkspaceStatus(
val phase: String
) {
companion object {
fun from(map: Any) = object {
val phase = Utils.getValue(map, arrayOf("phase")) ?: ""

val data = DevWorkspaceStatus(
phase as String
)
}.data
}
}

Loading

0 comments on commit 433c4d0

Please sign in to comment.