Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Namespace selection in the dialog #323

Merged
1 change: 1 addition & 0 deletions changelog.d/+namespace-selection.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The target selection dialog now allows for switching between available namespaces.
144 changes: 131 additions & 13 deletions modules/core/src/main/kotlin/com/metalbear/mirrord/MirrordApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package com.metalbear.mirrord

import com.google.gson.Gson
import com.google.gson.JsonObject
import com.google.gson.JsonSyntaxException
import com.google.gson.annotations.SerializedName
import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.execution.wsl.WSLCommandLineOptions
Expand Down Expand Up @@ -146,12 +147,97 @@ private const val MIRRORD_FOR_TEAMS_INVITE_AFTER = 100
*/
private const val MIRRORD_FOR_TEAMS_INVITE_EVERY = 30

/**
* Name of the environment variable used to trigger rich output of `mirrord ls`.
*/
private const val MIRRORD_LS_RICH_OUTPUT_ENV = "MIRRORD_LS_RICH_OUTPUT"

/**
* Interact with mirrord CLI using this API.
*/
class MirrordApi(private val service: MirrordProjectService, private val projectEnvVars: Map<String, String>?) {
private class MirrordLsTask(cli: String, projectEnvVars: Map<String, String>?) : MirrordCliTask<List<String>>(cli, "ls", null, projectEnvVars) {
override fun compute(project: Project, process: Process, setText: (String) -> Unit): List<String> {
/**
* New format of found target returned from `mirrord ls`.
*/
data class FoundTarget(
/**
* Path to the target, e.g `pod/my-pod`.
*/
val path: String,
/**
* Whether this target can be selected.
*/
val available: Boolean
)

/**
* New format of `mirrord ls`, enabled by setting MIRRORD_LS_RICH_OUTPUT_ENV to `true`.
*/
private data class RichOutput(
/**
* Targets found in the namespace.
*/
val targets: Array<FoundTarget>,
/**
* Namespace where the lookup was done.
*/
@SerializedName("current_namespace") val currentNamespace: String,
/**
* All namespaces available to the user.
*/
val namespaces: Array<String>
) {
/**
* Generated by IntelliJ.
*
* If it's not overrode, we get a warning, because this class has an Array field.
*/
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as RichOutput

if (!targets.contentEquals(other.targets)) return false
if (currentNamespace != other.currentNamespace) return false
if (!namespaces.contentEquals(other.namespaces)) return false

return true
}

/**
* Generated by IntelliJ.
*
* If it's not overrode, we get a warning, because this class has an Array field.
*/
override fun hashCode(): Int {
var result = targets.contentHashCode()
result = 31 * result + currentNamespace.hashCode()
Razz4780 marked this conversation as resolved.
Show resolved Hide resolved
result = 31 * result + namespaces.contentHashCode()
return result
}
}

/**
* Output of `mirrord ls`.
*/
class MirrordLsOutput(
/**
* List of found targets.
*/
val targets: List<FoundTarget>,
/**
* Namespace where the lookup was done.
*/
val currentNamespace: String?,
/**
* All namespaces avaiable to the user.
*/
val namespaces: List<String>?
)

private class MirrordLsTask(cli: String, projectEnvVars: Map<String, String>?) : MirrordCliTask<MirrordLsOutput>(cli, "ls", null, projectEnvVars) {
override fun compute(project: Project, process: Process, setText: (String) -> Unit): MirrordLsOutput {
setText("mirrord is listing targets...")

process.waitFor()
Expand All @@ -163,24 +249,48 @@ class MirrordApi(private val service: MirrordProjectService, private val project
val data = process.inputStream.bufferedReader().readText()
MirrordLogger.logger.debug("parsing mirrord ls output: $data")

val pods = SafeParser().parse(data, Array<String>::class.java).toMutableList()
val output = try {
val richOutput = SafeParser().parse(data, RichOutput::class.java)
MirrordLsOutput(richOutput.targets.toList(), richOutput.currentNamespace, richOutput.namespaces.toList())
} catch (error: Throwable) {
if (error.cause != null && error.cause is JsonSyntaxException) {
val simpleOutput = SafeParser().parse(data, Array<String>::class.java)
MirrordLsOutput(
simpleOutput.map { FoundTarget(it, true) },
null,
null
)
} else {
throw error
}
}

if (pods.isEmpty()) {
project.service<MirrordProjectService>().notifier.notifySimple("No mirrord target available in the configured namespace. You can run targetless, or set a different target namespace or kubeconfig in the mirrord configuration file.", NotificationType.INFORMATION)
if (output.targets.isEmpty()) {
project
.service<MirrordProjectService>()
.notifier
.notifySimple(
"No mirrord target available in the configured namespace. " +
"You can run targetless, or set a different target namespace " +
"or kubeconfig in the mirrord configuration file.",
NotificationType.INFORMATION
)
}

return pods
return output
}
}

/**
* Runs `mirrord ls` to get the list of available targets.
* Displays a modal progress dialog.
*
* @return list of pods
* @return available targets
*/
fun listPods(cli: String, configFile: String?, wslDistribution: WSLDistribution?): List<String> {
val task = MirrordLsTask(cli, projectEnvVars).apply {
fun listTargets(cli: String, configFile: String?, wslDistribution: WSLDistribution?, namespace: String?): MirrordLsOutput {
val envVars = projectEnvVars.orEmpty() + (MIRRORD_LS_RICH_OUTPUT_ENV to "true")
val task = MirrordLsTask(cli, envVars).apply {
this.namespace = namespace
this.configFile = configFile
this.wslDistribution = wslDistribution
this.output = "json"
Expand Down Expand Up @@ -354,11 +464,12 @@ class MirrordApi(private val service: MirrordProjectService, private val project
*
* @return environment for the user's application
*/
fun exec(cli: String, target: String?, configFile: String?, executable: String?, wslDistribution: WSLDistribution?): MirrordExecution {
fun exec(cli: String, target: MirrordExecDialog.UserSelection, configFile: String?, executable: String?, wslDistribution: WSLDistribution?): MirrordExecution {
bumpRunCounter()

val task = MirrordExtTask(cli, projectEnvVars).apply {
this.target = target
this.target = target.target
this.namespace = target.namespace
this.configFile = configFile
this.executable = executable
this.wslDistribution = wslDistribution
Expand All @@ -376,11 +487,12 @@ class MirrordApi(private val service: MirrordProjectService, private val project
return result
}

fun containerExec(cli: String, target: String?, configFile: String?, wslDistribution: WSLDistribution?): MirrordContainerExecution {
fun containerExec(cli: String, target: MirrordExecDialog.UserSelection, configFile: String?, wslDistribution: WSLDistribution?): MirrordContainerExecution {
bumpRunCounter()

val task = MirrordContainerExtTask(cli, projectEnvVars).apply {
this.target = target
this.target = target.target
this.namespace = target.namespace
this.configFile = configFile
this.wslDistribution = wslDistribution
}
Expand Down Expand Up @@ -431,6 +543,7 @@ class MirrordApi(private val service: MirrordProjectService, private val project
*/
private abstract class MirrordCliTask<T>(private val cli: String, private val command: String, private val args: List<String>?, private val projectEnvVars: Map<String, String>?) {
var target: String? = null
var namespace: String? = null
var configFile: String? = null
var executable: String? = null
var wslDistribution: WSLDistribution? = null
Expand All @@ -451,11 +564,16 @@ private abstract class MirrordCliTask<T>(private val cli: String, private val co
addParameter(it)
}

namespace?.let {
environment.put("MIRRORD_TARGET_NAMESPACE", it)
}

configFile?.let {
val formattedPath = wslDistribution?.getWslPath(it) ?: it
addParameter("-f")
addParameter(formattedPath)
}

executable?.let {
addParameter("-e")
addParameter(it)
Expand Down
Loading