Skip to content

Commit 9d6e8fa

Browse files
committed
dont display notification outside #update, set status instead & only show push notif (#570)
Signed-off-by: Andre Dietisheim <[email protected]>
1 parent a08fc14 commit 9d6e8fa

34 files changed

+2038
-1212
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ open class ClusterResource protected constructor(
5050
private val initialResource: HasMetadata = resource
5151
protected open var updatedResource: HasMetadata? = null
5252
private var isDeleted: Boolean = false
53+
private var isPushed: Boolean = false
5354
private var closed: Boolean = false
5455
protected open val watchListeners = WatchListeners(
5556
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2023 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.diagnostic.logger
14+
import com.redhat.devtools.intellij.kubernetes.model.IResourceModel
15+
import com.redhat.devtools.intellij.kubernetes.model.IResourceModelListener
16+
import com.redhat.devtools.intellij.kubernetes.model.context.IActiveContext
17+
import com.redhat.devtools.intellij.kubernetes.model.util.areEqual
18+
import com.redhat.devtools.intellij.kubernetes.model.util.hasGenerateName
19+
import com.redhat.devtools.intellij.kubernetes.model.util.hasName
20+
import com.redhat.devtools.intellij.kubernetes.model.util.isSameResource
21+
import com.redhat.devtools.intellij.kubernetes.model.util.toKindAndName
22+
import com.redhat.devtools.intellij.kubernetes.model.util.toMessage
23+
import io.fabric8.kubernetes.api.model.HasMetadata
24+
import io.fabric8.kubernetes.client.KubernetesClient
25+
import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil
26+
import io.fabric8.kubernetes.client.utils.Serialization
27+
import java.util.concurrent.locks.ReentrantLock
28+
import kotlin.concurrent.withLock
29+
30+
open class EditorResource(
31+
/** for testing purposes */
32+
private var resource: HasMetadata,
33+
private val resourceModel: IResourceModel,
34+
private val resourceChangedListener: IResourceModelListener?,
35+
// for mocking purposes
36+
private val clusterResourceFactory: (resource: HasMetadata, context: IActiveContext<out HasMetadata, out KubernetesClient>?) -> ClusterResource? =
37+
ClusterResource.Factory::create
38+
) {
39+
var disposed: Boolean = false
40+
/** for testing purposes */
41+
private var state: EditorResourceState? = null
42+
/** for testing purposes */
43+
protected open val clusterResource: ClusterResource? = createClusterResource(resource)
44+
45+
private var lastPushedPulled: HasMetadata? = Serialization.clone(resource)
46+
private var resourceVersion: String? = resource.metadata.resourceVersion
47+
48+
private val resourceChangeMutex = ReentrantLock()
49+
50+
/**
51+
* Sets the resource to this instance. Only modified versions of the same resource are processed.
52+
* Will do nothing if the given resource is a different resource in name, namespace, kind etc.
53+
*
54+
* @param new the new resource that should be set to this editor resource
55+
*
56+
* @see isSameResource
57+
*/
58+
fun setResource(new: HasMetadata) {
59+
resourceChangeMutex.withLock {
60+
val existing = this.resource
61+
if (new.isSameResource(existing)
62+
&& !areEqual(new, existing)) {
63+
this.resource = new
64+
}
65+
}
66+
}
67+
68+
/**
69+
* Returns the resource that is edited.
70+
*
71+
* @return the resource that is edited
72+
*/
73+
fun getResource(): HasMetadata {
74+
return resourceChangeMutex.withLock {
75+
resource
76+
}
77+
}
78+
79+
fun getResourceOnCluster(): HasMetadata? {
80+
return clusterResource?.pull(true)
81+
}
82+
83+
/** for testing purposes */
84+
protected open fun setState(state: EditorResourceState?) {
85+
resourceChangeMutex.withLock {
86+
this.state = state
87+
}
88+
}
89+
90+
/**
91+
* Returns the state of this editor resource.
92+
*
93+
* @return the state of this editor resource.
94+
*/
95+
fun getState(): EditorResourceState {
96+
return resourceChangeMutex.withLock {
97+
val state = getState(resource, state)
98+
this.state = state
99+
state
100+
}
101+
}
102+
103+
private fun getState(resource: HasMetadata, existingState: EditorResourceState?): EditorResourceState {
104+
val isOutdated = isOutdatedVersion()
105+
val existsOnCluster = existsOnCluster()
106+
return when {
107+
existingState is Error ->
108+
existingState
109+
110+
!isConnected() ->
111+
Error("Error contacting cluster. Make sure it's reachable, current context set, etc.", null as String?)
112+
113+
!hasName(resource)
114+
&& !hasGenerateName(resource) ->
115+
Error("Resource has neither name nor generateName.", null as String?)
116+
117+
isDeleted() ->
118+
DeletedOnCluster()
119+
120+
!existsOnCluster
121+
|| isModified(resource) ->
122+
Modified(
123+
existsOnCluster,
124+
isOutdated
125+
)
126+
127+
isOutdated ->
128+
Outdated()
129+
130+
existingState != null ->
131+
existingState
132+
133+
else ->
134+
Identical()
135+
}
136+
}
137+
138+
fun pull(): EditorResourceState {
139+
val state = try {
140+
val cluster = clusterResource
141+
?: return Error("Could not pull ${toKindAndName(resource)}", "cluster not connected.")
142+
val pulled = cluster.pull(true)
143+
?: return Error("Could not pull ${toKindAndName(resource)}", "resource not found on cluster.")
144+
setResource(pulled)
145+
setResourceVersion(KubernetesResourceUtil.getResourceVersion(pulled))
146+
setLastPushedPulled(pulled)
147+
Pulled()
148+
} catch (e: Exception) {
149+
logger<ResourceEditor>().warn(e)
150+
Error(
151+
"Could not pull ${toKindAndName(resource)}",
152+
toMessage(e.cause)
153+
)
154+
}
155+
setState(state)
156+
return state
157+
}
158+
159+
fun push(): EditorResourceState {
160+
val state = try {
161+
val cluster = clusterResource
162+
?: return Error(
163+
"Could not push ${toKindAndName(resource)} to cluster.",
164+
"Not connected."
165+
)
166+
val updatedResource = cluster.push(resource)
167+
setResourceVersion(KubernetesResourceUtil.getResourceVersion(updatedResource))
168+
setLastPushedPulled(resource) // store resource that is pushed, not resource returned from cluster
169+
createPushedState(resource, cluster.exists())
170+
} catch (e: Exception) {
171+
Error("Could not push ${toKindAndName(resource)} to cluster.", e)
172+
}
173+
setState(state)
174+
return state
175+
}
176+
177+
private fun createPushedState(resource: HasMetadata?, exists: Boolean): EditorResourceState {
178+
return if (resource != null) {
179+
if (exists) {
180+
Updated()
181+
} else {
182+
Created()
183+
}
184+
} else {
185+
Error(
186+
"Could not push resource to cluster.",
187+
"No resource present."
188+
)
189+
}
190+
}
191+
192+
private fun setResourceVersion(version: String?) {
193+
resourceChangeMutex.withLock {
194+
this.resourceVersion = version
195+
}
196+
}
197+
198+
/** for testing purposes */
199+
protected open fun getResourceVersion(): String? {
200+
return resourceChangeMutex.withLock {
201+
this.resourceVersion
202+
}
203+
}
204+
205+
/** for testing purposes */
206+
protected open fun setLastPushedPulled(resource: HasMetadata?) {
207+
resourceChangeMutex.withLock {
208+
lastPushedPulled = resource
209+
}
210+
}
211+
212+
/** for testing purposes */
213+
protected open fun getLastPushedPulled(): HasMetadata? {
214+
return resourceChangeMutex.withLock {
215+
lastPushedPulled
216+
}
217+
}
218+
219+
fun isOutdatedVersion(): Boolean {
220+
val version = resourceChangeMutex.withLock {
221+
resourceVersion
222+
}
223+
return true == clusterResource?.isOutdatedVersion(version)
224+
}
225+
226+
/**
227+
* Returns `true` if the given resource has changes that don't exist in the resource
228+
* that was last pulled/pushed from/to the cluster.
229+
* The following properties are not taken into account:
230+
* * [io.fabric8.kubernetes.api.model.ObjectMeta.resourceVersion]
231+
* * [io.fabric8.kubernetes.api.model.ObjectMeta.uid]
232+
*
233+
* @return true if the resource is not equal to the resource that was pulled from the cluster
234+
*/
235+
private fun isModified(resource: HasMetadata): Boolean {
236+
return !areEqual(resource, getLastPushedPulled())
237+
}
238+
239+
private fun isDeleted(): Boolean {
240+
return true == clusterResource?.isDeleted()
241+
}
242+
243+
private fun existsOnCluster(): Boolean {
244+
return true == clusterResource?.exists()
245+
}
246+
247+
private fun isConnected(): Boolean {
248+
return clusterResource != null
249+
}
250+
251+
fun watch() {
252+
clusterResource?.watch()
253+
}
254+
255+
fun stopWatch() {
256+
clusterResource?.stopWatch()
257+
}
258+
259+
fun dispose() {
260+
if (disposed) {
261+
return
262+
}
263+
this.disposed = true
264+
clusterResource?.close()
265+
setLastPushedPulled(null)
266+
setResourceVersion(null)
267+
}
268+
269+
private fun createClusterResource(resource: HasMetadata): ClusterResource? {
270+
val context = resourceModel.getCurrentContext()
271+
return if (context != null) {
272+
val clusterResource = clusterResourceFactory.invoke(
273+
resource,
274+
context
275+
)
276+
277+
val resourceChangeListener = resourceChangedListener
278+
if (resourceChangeListener != null) {
279+
clusterResource?.addListener(resourceChangeListener)
280+
}
281+
clusterResource?.watch()
282+
clusterResource
283+
} else {
284+
null
285+
}
286+
}
287+
288+
}

0 commit comments

Comments
 (0)