Skip to content
This repository was archived by the owner on Apr 23, 2025. It is now read-only.

Commit 590ff1d

Browse files
authored
feat: allow to switch project/namespace (#661) (#666)
* use missing active proj, display hint to choose/create other one * allow to create project in change project dialog * fix: 'please log in' if user is not authorized * added unit tests for kubernetes client ops in OdoCli Signed-off-by: Andre Dietisheim <[email protected]>
1 parent 5976084 commit 590ff1d

Some content is hidden

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

44 files changed

+1127
-247
lines changed
Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
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 org.jboss.tools.intellij.openshift.utils.odo;
12+
13+
import com.intellij.openapi.project.Project;
14+
import com.intellij.util.messages.MessageBus;
15+
import com.intellij.util.messages.MessageBusConnection;
16+
import io.fabric8.kubernetes.api.model.HasMetadata;
17+
import io.fabric8.kubernetes.api.model.Namespace;
18+
import io.fabric8.kubernetes.api.model.NamespaceList;
19+
import io.fabric8.kubernetes.api.model.ObjectMeta;
20+
import io.fabric8.kubernetes.api.model.Secret;
21+
import io.fabric8.kubernetes.api.model.SecretList;
22+
import io.fabric8.kubernetes.client.KubernetesClient;
23+
import io.fabric8.kubernetes.client.KubernetesClientException;
24+
import io.fabric8.kubernetes.client.dsl.MixedOperation;
25+
import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
26+
import io.fabric8.kubernetes.client.dsl.Resource;
27+
import io.fabric8.openshift.api.model.ProjectList;
28+
import io.fabric8.openshift.client.OpenShiftClient;
29+
import io.fabric8.openshift.client.dsl.ProjectOperation;
30+
import org.jboss.tools.intellij.openshift.utils.odo.OdoCli.TelemetryReport;
31+
import org.junit.Before;
32+
import org.junit.Test;
33+
34+
import java.io.IOException;
35+
import java.net.HttpURLConnection;
36+
import java.net.MalformedURLException;
37+
import java.net.URL;
38+
import java.util.Arrays;
39+
import java.util.HashMap;
40+
import java.util.List;
41+
import java.util.Map;
42+
import java.util.function.Function;
43+
import java.util.function.Supplier;
44+
45+
import static org.fest.assertions.Assertions.assertThat;
46+
import static org.mockito.ArgumentMatchers.anyString;
47+
import static org.mockito.Mockito.doReturn;
48+
import static org.mockito.Mockito.doThrow;
49+
import static org.mockito.Mockito.mock;
50+
51+
public class OdoKubernetesClientTest {
52+
53+
private KubernetesClient kubernetesClient;
54+
55+
private OpenShiftClient openShiftClient;
56+
57+
private Odo odo;
58+
59+
@Before
60+
public void before() throws MalformedURLException {
61+
this.kubernetesClient = mock(KubernetesClient.class);
62+
doReturn(new URL("https://localhost"))
63+
.when(kubernetesClient).getMasterUrl();
64+
this.openShiftClient = mock(OpenShiftClient.class);
65+
this.odo = createOdo(kubernetesClient, openShiftClient);
66+
}
67+
68+
@Test
69+
public void getCurrentNamespace_should_return_client_namespace() throws IOException {
70+
// given
71+
final String currentNamespace = "luke skywalker";
72+
doReturn(currentNamespace)
73+
.when(kubernetesClient).getNamespace();
74+
// when
75+
String namespace = odo.getCurrentNamespace();
76+
// then
77+
assertThat(namespace).isEqualTo(currentNamespace);
78+
}
79+
80+
@Test
81+
public void getCurrentNamespace_should_return_default_if_there_is_no_client_namespace() throws IOException {
82+
// given
83+
doReturn(null)
84+
.when(kubernetesClient).getNamespace();
85+
// when
86+
String namespace = odo.getCurrentNamespace();
87+
// then
88+
assertThat(namespace).isEqualTo("default");
89+
}
90+
91+
@Test
92+
public void getNamespaces_should_return_all_namespaces_if_kubernetes_cluster() throws IOException {
93+
// given
94+
List<Namespace> namespaces = Arrays.asList(
95+
mockResource("luke", Namespace.class),
96+
mockResource("obi wan", Namespace.class),
97+
mockResource("yoda", Namespace.class));
98+
NamespaceList namespacesList = mock(NamespaceList.class);
99+
doReturn(namespaces)
100+
.when(namespacesList).getItems();
101+
NonNamespaceOperation<Namespace, NamespaceList, Resource<Namespace>> namespacesOperation = namespaces(kubernetesClient); // client.namespaces()
102+
doReturn(namespacesList)
103+
.when(namespacesOperation).list();
104+
Odo odo = createOdo(kubernetesClient, null); // no openshift client -> kubernetes cluster
105+
// when
106+
List<String> namespaceNames = odo.getNamespaces();
107+
// then
108+
assertThat(namespaceNames).isEqualTo(Arrays.asList(
109+
"luke",
110+
"obi wan",
111+
"yoda"));
112+
}
113+
114+
@Test
115+
public void getNamespaces_should_return_all_projects_if_openshift_cluster() throws IOException {
116+
// given
117+
List<io.fabric8.openshift.api.model.Project> projects = Arrays.asList(
118+
mockResource("Palpatine", io.fabric8.openshift.api.model.Project.class),
119+
mockResource("Lord Dooku", io.fabric8.openshift.api.model.Project.class),
120+
mockResource("Darth Vader", io.fabric8.openshift.api.model.Project.class));
121+
ProjectList projectsList = mock(ProjectList.class);
122+
doReturn(projects)
123+
.when(projectsList).getItems();
124+
ProjectOperation projectsOperation = projects(openShiftClient); // client.projects()
125+
doReturn(projectsList)
126+
.when(projectsOperation).list();
127+
Odo odo = createOdo(kubernetesClient, openShiftClient); // openshift client -> openshift cluster
128+
// when
129+
List<String> projectNames = odo.getNamespaces();
130+
// then
131+
assertThat(projectNames).isEqualTo(Arrays.asList(
132+
"Palpatine",
133+
"Lord Dooku",
134+
"Darth Vader"));
135+
}
136+
137+
@Test
138+
public void isOpenShift_should_return_true_if_has_OpenShift_client() {
139+
// given
140+
Odo odo = createOdo(kubernetesClient, openShiftClient); // openshift client exists -> openshift cluster
141+
// when
142+
boolean found = odo.isOpenShift();
143+
// then
144+
assertThat(found).isTrue();
145+
}
146+
147+
@Test
148+
public void isOpenShift_should_return_false_if_has_no_OpenShift_client() {
149+
// given
150+
Odo odo = createOdo(kubernetesClient, null); // no openshift client -> kubernetes cluster
151+
// when
152+
boolean found = odo.isOpenShift();
153+
// then
154+
assertThat(found).isFalse();
155+
}
156+
157+
@Test
158+
public void namespaceExists_should_return_true_if_project_exists() {
159+
// given
160+
Odo odo = createOdo(kubernetesClient, openShiftClient); // openshift client exists -> openshift cluster
161+
io.fabric8.openshift.api.model.Project project = mockResource("yoda", io.fabric8.openshift.api.model.Project.class);
162+
get(project, withName(projects(openShiftClient))); // client.projects().withName().get()
163+
// when
164+
boolean found = odo.namespaceExists("yoda");
165+
// then
166+
assertThat(found).isTrue();
167+
}
168+
169+
@Test
170+
public void namespaceExists_should_return_false_if_project_doesnt_exist() {
171+
// given
172+
Odo odo = createOdo(kubernetesClient, openShiftClient); // openshift client exists -> openshift cluster
173+
get(null, withName(projects(openShiftClient))); // client.projects().withName().get()
174+
// when
175+
boolean found = odo.namespaceExists("yoda");
176+
// then
177+
assertThat(found).isFalse();
178+
}
179+
180+
@Test
181+
public void namespaceExists_should_return_true_if_namespace_exists() {
182+
// given
183+
Odo odo = createOdo(kubernetesClient, null); // openshift client doesnt exist-> kubernetes cluster
184+
Namespace namespace = mockResource("obi wan", Namespace.class);
185+
get(namespace, withName(namespaces(kubernetesClient))); // client.projects().withName().get()
186+
// when
187+
boolean found = odo.namespaceExists("obi wan");
188+
// then
189+
assertThat(found).isTrue();
190+
}
191+
192+
@Test
193+
public void namespaceExists_should_return_false_if_namespace_doesnt_exist() {
194+
// given
195+
Odo odo = createOdo(kubernetesClient, null); // openshift client doesnt exist-> kubernetes cluster
196+
get(null, withName(namespaces(kubernetesClient))); // client.projects().withName().get()
197+
// when
198+
boolean found = odo.namespaceExists("yoda");
199+
// then
200+
assertThat(found).isFalse();
201+
}
202+
203+
@Test
204+
public void isAuthorized_should_return_true_if_can_list_secrets() {
205+
// given
206+
secrets(kubernetesClient);
207+
Odo odo = createOdo(kubernetesClient, openShiftClient);
208+
// when
209+
boolean found = odo.isAuthorized();
210+
// then
211+
assertThat(found).isTrue();
212+
}
213+
214+
@Test
215+
public void isAuthorized_should_return_false_if_listing_secrets_throws_unauthorized() {
216+
// given
217+
MixedOperation<Secret, SecretList, Resource<Secret>> secretsOperation = secrets(kubernetesClient);
218+
doThrow(new KubernetesClientException("unauthorized", HttpURLConnection.HTTP_UNAUTHORIZED, null))
219+
.when(secretsOperation).list();
220+
Odo odo = createOdo(kubernetesClient, openShiftClient);
221+
// when
222+
boolean found = odo.isAuthorized();
223+
// then
224+
assertThat(found).isFalse();
225+
}
226+
227+
@Test
228+
public void isAuthorized_should_return_false_if_listing_secrets_throws_forbidden() {
229+
// given
230+
MixedOperation<Secret, SecretList, Resource<Secret>> secretsOperation = secrets(kubernetesClient);
231+
doThrow(new KubernetesClientException("forbidden", HttpURLConnection.HTTP_FORBIDDEN, null))
232+
.when(secretsOperation).list();
233+
Odo odo = createOdo(kubernetesClient, openShiftClient);
234+
// when
235+
boolean found = odo.isAuthorized();
236+
// then
237+
assertThat(found).isFalse();
238+
}
239+
240+
@Test(expected = KubernetesClientException.class)
241+
public void isAuthorized_should_throw_if_listing_secrets_throws_other_KubernetesException() {
242+
// given
243+
MixedOperation<Secret, SecretList, Resource<Secret>> secretsOperation = secrets(kubernetesClient);
244+
doThrow(new KubernetesClientException("not found", HttpURLConnection.HTTP_NOT_FOUND, null))
245+
.when(secretsOperation).list();
246+
Odo odo = createOdo(kubernetesClient, openShiftClient);
247+
// when
248+
boolean found = odo.isAuthorized();
249+
// then
250+
}
251+
252+
private OdoCli createOdo(KubernetesClient kubernetesClient, OpenShiftClient openShiftClient) {
253+
Project project = mock(Project.class);
254+
String command = "Star Wars";
255+
MessageBusConnection connection = mock(MessageBusConnection.class);
256+
MessageBus bus = mock(MessageBus.class);
257+
doReturn(connection)
258+
.when(bus).connect();
259+
Supplier<KubernetesClient> kubernetesClientFactory = () -> kubernetesClient;
260+
Function<KubernetesClient, OpenShiftClient> openShiftClientFactory = client -> openShiftClient;
261+
Function<String, Map<String, String>> envVarFactory = url -> new HashMap<>();
262+
TelemetryReport telemetryReport = mock(TelemetryReport.class);
263+
return new OdoCli(project, command, bus, kubernetesClientFactory, openShiftClientFactory, envVarFactory, telemetryReport);
264+
}
265+
266+
private static <R extends HasMetadata> R mockResource(String name, Class<R> clazz) {
267+
R resource = mock(clazz);
268+
ObjectMeta meta = mockMetadata(name);
269+
doReturn(meta)
270+
.when(resource).getMetadata();
271+
return resource;
272+
}
273+
274+
private static ObjectMeta mockMetadata(String name) {
275+
ObjectMeta meta = mock(ObjectMeta.class);
276+
doReturn(name)
277+
.when(meta).getName();
278+
return meta;
279+
}
280+
281+
private static NonNamespaceOperation<Namespace, NamespaceList, Resource<Namespace>> namespaces(KubernetesClient client) {
282+
NonNamespaceOperation<Namespace, NamespaceList, Resource<Namespace>> namespacesOperation = mock(NonNamespaceOperation.class);
283+
doReturn(namespacesOperation)
284+
.when(client).namespaces();
285+
return namespacesOperation;
286+
}
287+
288+
private static ProjectOperation projects(OpenShiftClient client) {
289+
ProjectOperation projectsOperation = mock(ProjectOperation.class);
290+
doReturn(projectsOperation)
291+
.when(client).projects();
292+
return projectsOperation;
293+
}
294+
295+
private static <R> Resource<R> withName(NonNamespaceOperation<?, ?, R> nonNamespaceOperation) {
296+
Resource<R> resource = mock(Resource.class);
297+
doReturn(resource)
298+
.when(nonNamespaceOperation).withName(anyString());
299+
return resource;
300+
}
301+
302+
private static <R> HasMetadata get(HasMetadata hasMetadata, Resource<R> resource) {
303+
doReturn(hasMetadata)
304+
.when(resource).get();
305+
return hasMetadata;
306+
}
307+
308+
private static MixedOperation<Secret, SecretList, Resource<Secret>> secrets(KubernetesClient client) {
309+
MixedOperation<Secret, SecretList, Resource<Secret>> secretsOperation = mock(MixedOperation.class);
310+
doReturn(secretsOperation)
311+
.when(client).secrets();
312+
return secretsOperation;
313+
}
314+
}

src/main/java/org/jboss/tools/intellij/openshift/actions/ActionUtils.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,29 @@
1010
******************************************************************************/
1111
package org.jboss.tools.intellij.openshift.actions;
1212

13+
import com.intellij.openapi.actionSystem.ActionManager;
1314
import com.intellij.openapi.actionSystem.AnActionEvent;
1415
import com.intellij.openapi.actionSystem.PlatformDataKeys;
1516
import com.intellij.openapi.progress.ProgressIndicator;
1617
import com.intellij.openapi.progress.ProgressManager;
1718
import com.intellij.openapi.progress.Task;
1819
import com.intellij.openapi.project.Project;
1920
import com.intellij.ui.treeStructure.Tree;
21+
import com.redhat.devtools.intellij.common.actions.StructureTreeAction;
2022
import org.jboss.tools.intellij.openshift.Constants;
23+
import org.jboss.tools.intellij.openshift.telemetry.TelemetrySender;
24+
import org.jboss.tools.intellij.openshift.telemetry.TelemetrySenderAware;
2125
import org.jboss.tools.intellij.openshift.tree.application.ApplicationsRootNode;
2226
import org.jboss.tools.intellij.openshift.tree.application.ApplicationsTreeStructure;
2327

24-
import javax.swing.*;
25-
import java.awt.*;
28+
import javax.swing.JTree;
29+
import java.awt.Component;
30+
import java.awt.Point;
31+
import java.awt.event.MouseEvent;
2632
import java.util.function.Consumer;
2733

34+
import static org.jboss.tools.intellij.openshift.telemetry.TelemetryService.PREFIX_ACTION;
35+
2836
public class ActionUtils {
2937

3038
private ActionUtils() {
@@ -67,4 +75,23 @@ public void run(ProgressIndicator progress) {
6775
});
6876
}
6977

78+
public static Point getLocation(AnActionEvent actionEvent) {
79+
if (actionEvent == null) {
80+
return null;
81+
}
82+
MouseEvent event = ((MouseEvent) actionEvent.getInputEvent());
83+
if (event == null) {
84+
return null;
85+
}
86+
return event.getLocationOnScreen();
87+
}
88+
89+
public static <T extends StructureTreeAction> T createAction(String id) {
90+
T action = (T) ActionManager.getInstance().getAction(id);
91+
if (action instanceof TelemetrySenderAware) {
92+
TelemetrySenderAware sender = (TelemetrySenderAware) action;
93+
sender.setTelemetrySender(new TelemetrySender(PREFIX_ACTION + sender.getTelemetryActionName()));
94+
}
95+
return action;
96+
}
7097
}

0 commit comments

Comments
 (0)