Skip to content

Commit 911769e

Browse files
committed
feat: impl'd describe for resources (redhat-developer#553)
Signed-off-by: Andre Dietisheim <[email protected]>
1 parent c84158e commit 911769e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+5346
-89
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2024 Red Hat, Inc.
3+
* Distributed under license by Red Hat, Inc. All rights reserved.
4+
* This program is made available under the terms of the
5+
* Eclipse Public License v2.0 which accompanies this distribution,
6+
* and is available at http://www.eclipse.org/legal/epl-v20.html
7+
*
8+
* Contributors:
9+
* Red Hat, Inc. - initial API and implementation
10+
******************************************************************************/
11+
package com.redhat.devtools.intellij.kubernetes.actions
12+
13+
import com.intellij.openapi.actionSystem.AnActionEvent
14+
import com.intellij.openapi.diagnostic.logger
15+
import com.redhat.devtools.intellij.common.actions.StructureTreeAction
16+
import com.redhat.devtools.intellij.kubernetes.editor.describe.DescriptionViewerFactory
17+
import com.redhat.devtools.intellij.kubernetes.model.Notification
18+
import io.fabric8.kubernetes.api.model.HasMetadata
19+
import io.fabric8.kubernetes.api.model.Pod
20+
import javax.swing.tree.TreePath
21+
22+
class DescribeResourceAction: StructureTreeAction() {
23+
24+
override fun actionPerformed(event: AnActionEvent?, path: TreePath?, selected: Any?) {
25+
// not called
26+
}
27+
28+
override fun actionPerformed(event: AnActionEvent?, path: Array<out TreePath>?, selected: Array<out Any>?) {
29+
val descriptor = selected?.get(0)?.getDescriptor() ?: return
30+
val project = descriptor.project ?: return
31+
val toDescribe: HasMetadata = descriptor.element as? HasMetadata? ?: return
32+
try {
33+
DescriptionViewerFactory.instance.openEditor(toDescribe, project)
34+
} catch (e: RuntimeException) {
35+
logger<DescribeResourceAction>().warn("Error opening editor ${toDescribe.metadata.name}", e)
36+
Notification().error(
37+
"Error opening editor ${toDescribe.metadata.name}",
38+
"Could not open editor for ${toDescribe.kind} '${toDescribe.metadata.name}'."
39+
)
40+
}
41+
}
42+
43+
override fun isVisible(selected: Array<out Any>?): Boolean {
44+
return selected?.size == 1
45+
&& isVisible(selected.firstOrNull())
46+
}
47+
48+
override fun isVisible(selected: Any?): Boolean {
49+
val element = selected?.getElement<HasMetadata>()
50+
return element is Pod
51+
}
52+
}

src/main/kotlin/com/redhat/devtools/intellij/kubernetes/console/ConsoleTab.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ abstract class ConsoleTab<T : ConsoleView, W : Any?>(
6565
var i = 0
6666
do {
6767
val container = model.getElementAt(i).container
68-
if (isRunning(getStatus(container, pod.status))) {
68+
if (isRunning(container.getStatus(pod.status))) {
6969
return i
7070
}
7171
} while (++i < model.size)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2024 Red Hat, Inc.
3+
* Distributed under license by Red Hat, Inc. All rights reserved.
4+
* This program is made available under the terms of the
5+
* Eclipse Public License v2.0 which accompanies this distribution,
6+
* and is available at http://www.eclipse.org/legal/epl-v20.html
7+
*
8+
* Contributors:
9+
* Red Hat, Inc. - initial API and implementation
10+
******************************************************************************/
11+
package com.redhat.devtools.intellij.kubernetes.editor
12+
13+
import com.intellij.openapi.fileEditor.impl.EditorTabTitleProvider
14+
import com.intellij.openapi.fileEditor.impl.UniqueNameEditorTabTitleProvider
15+
import com.intellij.openapi.project.Project
16+
import com.intellij.openapi.vfs.VirtualFile
17+
import com.redhat.devtools.intellij.kubernetes.editor.describe.DescriptionViewerTabTitleProvider
18+
19+
open class KubernetesEditorsTabTitleProvider(
20+
private val fallback: EditorTabTitleProvider = UniqueNameEditorTabTitleProvider()
21+
) : EditorTabTitleProvider {
22+
23+
override fun getEditorTabTitle(project: Project, file: VirtualFile): String? {
24+
return ResourceEditorTabTitleProvider().getEditorTabTitle(project, file)
25+
?: DescriptionViewerTabTitleProvider().getEditorTabTitle(project, file)
26+
?: fallback.getEditorTabTitle(project, file)
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2021 Red Hat, Inc.
2+
* Copyright (c) 2024 Red Hat, Inc.
33
* Distributed under license by Red Hat, Inc. All rights reserved.
44
* This program is made available under the terms of the
55
* Eclipse Public License v2.0 which accompanies this distribution,
@@ -11,54 +11,45 @@
1111
package com.redhat.devtools.intellij.kubernetes.editor
1212

1313
import com.intellij.openapi.fileEditor.impl.EditorTabTitleProvider
14-
import com.intellij.openapi.fileEditor.impl.UniqueNameEditorTabTitleProvider
1514
import com.intellij.openapi.project.Project
1615
import com.intellij.openapi.vfs.VirtualFile
1716
import com.redhat.devtools.intellij.common.validation.KubernetesResourceInfo
1817
import com.redhat.devtools.intellij.kubernetes.editor.util.isKubernetesResource
1918

20-
open class ResourceEditorTabTitleProvider(
21-
private val fallback: EditorTabTitleProvider = UniqueNameEditorTabTitleProvider()
22-
) : EditorTabTitleProvider {
23-
24-
companion object {
25-
const val TITLE_UNKNOWN_CLUSTERRESOURCE = "Unknown Cluster Resource"
26-
const val TITLE_UNKNOWN_NAME = "unknown name"
27-
}
28-
29-
override fun getEditorTabTitle(project: Project, file: VirtualFile): String? {
30-
return if (isTemporary(file)) {
31-
val resourceInfo = getKubernetesResourceInfo(file, project)
32-
if (resourceInfo != null
33-
&& isKubernetesResource(resourceInfo)
34-
) {
35-
getTitleFor(resourceInfo)
36-
} else {
37-
TITLE_UNKNOWN_CLUSTERRESOURCE
38-
}
39-
} else {
40-
fallback.getEditorTabTitle(project, file)
41-
}
42-
}
43-
44-
private fun getTitleFor(info: KubernetesResourceInfo): String {
45-
val name = info.name ?: TITLE_UNKNOWN_NAME
46-
val namespace = info.namespace
47-
return if (namespace == null) {
48-
name
49-
} else {
50-
"$name@$namespace"
51-
}
52-
}
53-
54-
/* for testing purposes */
55-
protected open fun getKubernetesResourceInfo(file: VirtualFile, project: Project): KubernetesResourceInfo? {
56-
return com.redhat.devtools.intellij.kubernetes.editor.util.getKubernetesResourceInfo(file, project)
57-
}
58-
59-
/* for testing purposes */
60-
protected open fun isTemporary(file: VirtualFile): Boolean {
61-
return ResourceFile.isTemporary(file)
62-
}
19+
open class ResourceEditorTabTitleProvider: EditorTabTitleProvider {
20+
companion object {
21+
const val TITLE_UNKNOWN_CLUSTERRESOURCE = "Unknown Cluster Resource"
22+
const val TITLE_UNKNOWN_NAME = "unknown name"
23+
}
24+
25+
override fun getEditorTabTitle(project: Project, file: VirtualFile): String? {
26+
if (!isResourceFile(file)) {
27+
return null
28+
}
29+
30+
val resourceInfo = getKubernetesResourceInfo(file, project)
31+
return if (resourceInfo != null
32+
&& isKubernetesResource(resourceInfo)
33+
) {
34+
getTitleFor(resourceInfo)
35+
} else {
36+
TITLE_UNKNOWN_CLUSTERRESOURCE
37+
}
38+
}
39+
40+
private fun getTitleFor(info: KubernetesResourceInfo): String {
41+
val name = info.name ?: TITLE_UNKNOWN_NAME
42+
val namespace = info.namespace ?: return name
43+
return "$name@$namespace"
44+
}
45+
46+
protected open fun isResourceFile(file: VirtualFile): Boolean {
47+
return ResourceFile.isResourceFile(file)
48+
}
49+
50+
/* for testing purposes */
51+
protected open fun getKubernetesResourceInfo(file: VirtualFile, project: Project): KubernetesResourceInfo? {
52+
return com.redhat.devtools.intellij.kubernetes.editor.util.getKubernetesResourceInfo(file, project)
53+
}
6354

6455
}

src/main/kotlin/com/redhat/devtools/intellij/kubernetes/editor/ResourceFile.kt

+4
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ open class ResourceFile protected constructor(
103103
&& virtualFile.path.startsWith(TEMP_FOLDER.toString())
104104
}
105105

106+
fun isResourceFile(virtualFile: VirtualFile?): Boolean {
107+
return isTemporary(virtualFile)
108+
}
109+
106110
private fun isYamlOrJson(file: VirtualFile): Boolean {
107111
if (true == file.extension?.isBlank()) {
108112
return false
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2024 Red Hat, Inc.
3+
* Distributed under license by Red Hat, Inc. All rights reserved.
4+
* This program is made available under the terms of the
5+
* Eclipse Public License v2.0 which accompanies this distribution,
6+
* and is available at http://www.eclipse.org/legal/epl-v20.html
7+
*
8+
* Contributors:
9+
* Red Hat, Inc. - initial API and implementation
10+
******************************************************************************/
11+
package com.redhat.devtools.intellij.kubernetes.editor.describe
12+
13+
import com.redhat.devtools.intellij.kubernetes.editor.describe.paragraphs.Chapter
14+
15+
abstract class Description: Chapter("Document") {
16+
17+
abstract fun toText(): String
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2024 Red Hat, Inc.
3+
* Distributed under license by Red Hat, Inc. All rights reserved.
4+
* This program is made available under the terms of the
5+
* Eclipse Public License v2.0 which accompanies this distribution,
6+
* and is available at http://www.eclipse.org/legal/epl-v20.html
7+
*
8+
* Contributors:
9+
* Red Hat, Inc. - initial API and implementation
10+
******************************************************************************/
11+
package com.redhat.devtools.intellij.kubernetes.editor.describe
12+
13+
import com.redhat.devtools.intellij.kubernetes.editor.describe.paragraphs.NamedSequence
14+
import com.redhat.devtools.intellij.kubernetes.editor.describe.paragraphs.NamedValue
15+
import com.redhat.devtools.intellij.kubernetes.editor.describe.paragraphs.Paragraph
16+
import java.time.DateTimeException
17+
import java.time.Duration
18+
import java.time.LocalDateTime
19+
import java.time.ZonedDateTime
20+
import java.time.format.DateTimeFormatter
21+
22+
object DescriptionConstants {
23+
24+
object Labels {
25+
const val NAME = "Name"
26+
const val NAMESPACE = "Namespace"
27+
}
28+
29+
object Values {
30+
const val NONE = "<none>"
31+
const val UNSET = "<unset>"
32+
}
33+
}
34+
35+
fun createValueOrSequence(title: String, items: List<String>?): Paragraph? {
36+
return if (items.isNullOrEmpty()) {
37+
null
38+
} else if (items.size == 1) {
39+
NamedValue(title, items.first())
40+
} else {
41+
NamedSequence(title, items)
42+
}
43+
}
44+
45+
fun createValues(map: Map<String, String>?): List<NamedValue> {
46+
if (map.isNullOrEmpty()) {
47+
return emptyList()
48+
}
49+
return map.entries.map { entry -> NamedValue(entry.key, entry.value) }
50+
}
51+
52+
fun toString(items: List<String>?): String? {
53+
return items?.joinToString("\n")
54+
}
55+
56+
fun toRFC1123Date(dateTime: String?): String? {
57+
if (dateTime == null) {
58+
return null
59+
}
60+
val parsed = LocalDateTime.parse(dateTime, DateTimeFormatter.ISO_ZONED_DATE_TIME)
61+
val zoned = parsed.atOffset(ZonedDateTime.now().offset)
62+
return DateTimeFormatter.RFC_1123_DATE_TIME.format(zoned)
63+
}
64+
65+
fun toRFC1123DateOrUnrecognized(dateTime: String?): String? {
66+
return try {
67+
toRFC1123Date(dateTime)
68+
} catch (e: DateTimeException) {
69+
"Unrecognized Date: $dateTime"
70+
}
71+
}
72+
73+
/**
74+
* Returns a human-readable form of the given date/time since the given date/time.
75+
* Returns `null` if the given dateTime is not understood.
76+
* The logic is copied from k8s.io/apimachinery/util/duration/ duration/HumanDuration.
77+
*
78+
* @see [k8s.io/apimachinery/util/duration/duration/HumanDuration](https://github.com/kubernetes/apimachinery/blob/d7e1c5311169d5ece2db0ae0118066859aa6f7d8/pkg/util/duration/duration.go#L48)
79+
* @see
80+
*/
81+
fun toHumanReadableDurationSince(dateTime: String?, since: LocalDateTime): String? {
82+
if (dateTime == null) {
83+
return null
84+
}
85+
return try {
86+
val parsed = LocalDateTime.parse(dateTime, DateTimeFormatter.ISO_ZONED_DATE_TIME)
87+
val difference = if (since.isBefore(parsed)) {
88+
Duration.between(since, parsed)
89+
} else {
90+
Duration.between(parsed, since)
91+
}
92+
val seconds = difference.toSeconds()
93+
return when {
94+
seconds < 60 * 2 ->
95+
// < 2 minutes
96+
"${seconds}s"
97+
98+
seconds < 60 * 10 ->
99+
// < 10 minutes
100+
"${difference.toMinutesPart()}m${difference.toSecondsPart()}s"
101+
102+
seconds < 60 * 60 * 3 ->
103+
// < 3 hours
104+
"${difference.toMinutes()}m"
105+
106+
seconds < 60 * 60 * 8 ->
107+
// < 8 hours
108+
"${difference.toHoursPart()}h"
109+
110+
seconds < 60 * 60 * 48 ->
111+
// < 48 hours
112+
"${difference.toHours()}h${difference.toMinutesPart()}m"
113+
114+
seconds < 60 * 60 * 24 * 8 -> {
115+
// < 192 hours
116+
if (difference.toHoursPart() == 0) {
117+
"${difference.toDaysPart()}d"
118+
} else {
119+
"${difference.toDaysPart()}d${difference.toHoursPart()}h"
120+
}
121+
}
122+
123+
seconds < 60 * 60 * 24 * 365 * 2 ->
124+
// < 2 years
125+
"${difference.toDaysPart()}d"
126+
127+
seconds < 60 * 60 * 24 * 365 * 8 -> {
128+
// < 8 years
129+
val years = difference.toDaysPart() / 365
130+
"${years}y${difference.toDaysPart() % 365}d"
131+
}
132+
133+
else ->
134+
"${difference.toDaysPart() / 365}y"
135+
}
136+
} catch (e: DateTimeException) {
137+
null
138+
}
139+
}

0 commit comments

Comments
 (0)