consum
Config config = prepareConfig(cluster.getConfig(), cluster);
try (KubernetesClient client = new KubernetesClientBuilder().withConfig(config).build();) {
consumer.accept(client);
- } catch (Exception e) {
- Exceptions.printStackTrace(e);
+ } catch (Throwable t) {
+ throw t;
}
}
diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/k8s/PortForwardAction.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/k8s/PortForwardAction.java
new file mode 100644
index 000000000000..ba5e5dcfdc7f
--- /dev/null
+++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/k8s/PortForwardAction.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.cloud.oracle.assets.k8s;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import org.netbeans.modules.cloud.oracle.compute.PodItem;
+import org.openide.awt.ActionID;
+import org.openide.awt.ActionRegistration;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Jan Horvath
+ */
+@ActionID(
+ category = "Tools",
+ id = "org.netbeans.modules.cloud.oracle.actions.PortForwardAction"
+)
+@ActionRegistration(
+ displayName = "#PortForward",
+ asynchronous = true
+)
+
+@NbBundle.Messages({
+ "PortForward=Start port forwarding"
+})
+public class PortForwardAction implements ActionListener {
+ private final PodItem pod;
+
+ public PortForwardAction(PodItem pod) {
+ this.pod = pod;
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ PortForwards.getDefault().startPortForward(pod);
+ }
+
+}
diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/k8s/PortForwards.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/k8s/PortForwards.java
new file mode 100644
index 000000000000..54857d17136c
--- /dev/null
+++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/k8s/PortForwards.java
@@ -0,0 +1,235 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.cloud.oracle.assets.k8s;
+
+import io.fabric8.kubernetes.api.model.Pod;
+import io.fabric8.kubernetes.client.LocalPortForward;
+import io.fabric8.kubernetes.client.PortForward;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.stream.Collectors;
+import org.netbeans.api.progress.ProgressHandle;
+import org.netbeans.modules.cloud.oracle.NotificationUtils;
+import org.netbeans.modules.cloud.oracle.compute.PodItem;
+import org.netbeans.modules.cloud.oracle.compute.PortForwardItem;
+import org.openide.util.Exceptions;
+import org.openide.util.NbBundle;
+
+/**
+ * Manages port forwarding for Kubernetes Pods.
+ *
+ * This class provides methods to start, list, and stop port forwards
+ * for specific Pods. It maintains the state of active port forwards
+ * and ensures thread-safe operations.
+ *
+ * Example Usage:
+ *
+ * PortForwards manager = PortForwards.getDefault();
+ * manager.startPortForward(podItem);
+ * List forwards = manager.getActivePortForwards("pod-name");
+ * manager.closePortForward("pod-name");
+ *
+ *
+ * @author Jan Horvath
+ */
+@NbBundle.Messages({
+ "PortNotFree=Port {0} is occupied by another process and cannot be used.",
+ "Forwarding=→ {0}"
+})
+public class PortForwards {
+
+ private static PortForwards instance = null;
+ private final Map> activePortForwards;
+ private final Map latches;
+ private final PropertyChangeSupport propertyChangeSupport;
+
+ private PortForwards() {
+ activePortForwards = new ConcurrentHashMap<>();
+ latches = new ConcurrentHashMap<>();
+ propertyChangeSupport = new PropertyChangeSupport(this);
+ }
+
+ public static synchronized PortForwards getDefault() {
+ if (instance == null) {
+ instance = new PortForwards();
+ }
+ return instance;
+ }
+
+ /**
+ * Adds a {@code PropertyChangeListener} to listen for changes in port forwarding for a specific Pod.
+ *
+ * @param pod The {@code PodItem} for which the listener is interested.
+ * @param listener The listener to be added.
+ */
+ public void addPropertyChangeListener(PodItem pod, PropertyChangeListener listener) {
+ propertyChangeSupport.addPropertyChangeListener(pod.getName(), listener);
+ }
+
+ /**
+ * Removes a {@code PropertyChangeListener} for a specific Pod.
+ *
+ * @param pod The {@code PodItem} for which the listener should be removed.
+ * @param listener The listener to be removed.
+ */
+ public void removePropertyChangeListener(PodItem pod, PropertyChangeListener listener) {
+ propertyChangeSupport.removePropertyChangeListener(pod.getName(), listener);
+ }
+
+ /**
+ * Notifies listeners of a property change for a specific Pod.
+ *
+ * @param pod The {@code PodItem} for which the change occurred.
+ * @param oldValue The old value of the property.
+ * @param newValue The new value of the property.
+ */
+ private void firePropertyChange(PodItem pod, Object oldValue, Object newValue) {
+ propertyChangeSupport.firePropertyChange(pod.getName(), oldValue, newValue);
+ }
+
+ /**
+ * Starts port forwarding for a specified Kubernetes Pod.
+ *
+ *
+ * This method retrieves the Pod's ports and forwards them to the local
+ * machine. If any port is unavailable, the operation is halted for that
+ * port and a notification is displayed.
+ *
+ * @param podItem The {@code PodItem} representing the Pod for which port
+ * forwarding should start.
+ */
+ public void startPortForward(PodItem podItem) {
+ if (activePortForwards.containsKey(podItem.getName())) {
+ NotificationUtils.showMessage("Port forwarding already active for: " + podItem.getName());
+ return;
+ }
+ CountDownLatch latch = new CountDownLatch(1);
+ ProgressHandle handle = ProgressHandle.createHandle(Bundle.Forwarding(podItem.getName()), () -> {
+ latch.countDown();
+ return true;
+ });
+ handle.start();
+ CompletableFuture future = CompletableFuture.runAsync(() -> {
+ KubernetesUtils.runWithClient(podItem.getCluster(), client -> {
+ Pod pod = client.pods().inNamespace(podItem.getNamespace()).withName(podItem.getName()).get();
+
+ if (pod == null) {
+ System.out.println("Pod not found: " + podItem.getName());
+ return;
+ }
+ List ports = pod.getSpec().getContainers().stream()
+ .flatMap(container -> container.getPorts().stream())
+ .map(port -> port.getContainerPort())
+ .collect(Collectors.toList());
+ List forwardItems = new ArrayList<>();
+ List forwards = new ArrayList<>();
+ if (ports.isEmpty()) {
+ NotificationUtils.showMessage("No ports found for pod: " + podItem.getName());
+ return;
+ }
+ try {
+ for (Integer port : ports) {
+ if (!isPortAvailable(port)) {
+ NotificationUtils.showErrorMessage(Bundle.PortNotFree(port));
+ break;
+ }
+ LocalPortForward fwd = client.pods()
+ .inNamespace(pod.getMetadata().getNamespace())
+ .withName(pod.getMetadata().getName())
+ .portForward(port, port);
+ forwards.add(fwd);
+ forwardItems.add(new PortForwardItem(podItem, fwd.getLocalPort(), port));
+
+ NotificationUtils.showMessage(Bundle.ForwardingPorts(port.toString(), podItem.getName()));
+ }
+ latches.put(podItem.getName(), latch);
+ List oldValue = activePortForwards.put(podItem.getName(), forwardItems);
+ firePropertyChange(podItem, oldValue, forwardItems);
+ latch.await();
+ for (PortForward forward : forwards) {
+ forward.close();
+ }
+ activePortForwards.remove(podItem.getName());
+ firePropertyChange(podItem, forwardItems, oldValue);
+ } catch (InterruptedException | IOException ex) {
+ Exceptions.printStackTrace(ex);
+ } catch (IllegalStateException e) {
+ NotificationUtils.showErrorMessage(e.getMessage());
+ } finally {
+ handle.finish();
+ }
+
+ });
+ });
+ }
+
+ /**
+ * Retrieves the list of active port forwards for the specified Pod.
+ *
+ * @param podName The name of the Pod.
+ * @return A list of active {@code PortForward} instances for the Pod, or an
+ * empty list if none are found.
+ */
+ public List getActivePortForwards(String podName) {
+ return activePortForwards.getOrDefault(podName, Collections.emptyList());
+ }
+
+ /**
+ * Stops and closes all active port forwards for the specified Pod.
+ *
+ *
+ * Resources associated with the port forwarding are released, and the port
+ * forward is removed from the active list.
+ *
+ * @param podName The name of the Pod for which port forwarding should be
+ * stopped.
+ */
+ public void closePortForward(String podName) {
+ CountDownLatch latch = latches.get(podName);
+ if (latch != null) {
+ latch.countDown();
+ }
+ activePortForwards.remove(podName);
+ latches.remove(podName);
+ }
+
+ /**
+ * Checks if a TCP port is available.
+ *
+ * @param port The port number to check.
+ * @return true if the port is available, false otherwise.
+ */
+ private static boolean isPortAvailable(int port) {
+ try (ServerSocket serverSocket = new ServerSocket(port)) {
+ serverSocket.setReuseAddress(true);
+ return true;
+ } catch (IOException e) {
+ return false;
+ }
+ }
+}
diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/RunInClusterAction.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/k8s/RunInClusterAction.java
similarity index 87%
rename from enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/RunInClusterAction.java
rename to enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/k8s/RunInClusterAction.java
index c3762e9600fb..05893ed4b0f9 100644
--- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/RunInClusterAction.java
+++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/k8s/RunInClusterAction.java
@@ -16,8 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.netbeans.modules.cloud.oracle.assets;
+package org.netbeans.modules.cloud.oracle.assets.k8s;
+import org.netbeans.modules.cloud.oracle.assets.k8s.KubernetesUtils;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder;
import io.fabric8.kubernetes.api.model.apps.DeploymentList;
@@ -29,14 +30,22 @@
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectInformation;
import org.netbeans.api.project.ProjectUtils;
-import static org.netbeans.modules.cloud.oracle.assets.ConfigMapProvider.CONFIG_VOLUME_NAME;
-import static org.netbeans.modules.cloud.oracle.assets.ConfigMapProvider.ENVIRONMENT;
-import static org.netbeans.modules.cloud.oracle.assets.ConfigMapProvider.VOLUME_MOUNT_PATH;
+import org.netbeans.modules.cloud.oracle.NotificationUtils;
+import org.netbeans.modules.cloud.oracle.assets.CloudAssets;
+import org.netbeans.modules.cloud.oracle.assets.RootNode;
+import org.netbeans.modules.cloud.oracle.assets.Steps;
+import static org.netbeans.modules.cloud.oracle.assets.k8s.ConfigMapProvider.CONFIG_VOLUME_NAME;
+import static org.netbeans.modules.cloud.oracle.assets.k8s.ConfigMapProvider.ENVIRONMENT;
+import static org.netbeans.modules.cloud.oracle.assets.k8s.ConfigMapProvider.VOLUME_MOUNT_PATH;
import org.netbeans.modules.cloud.oracle.compute.ClusterItem;
+import org.netbeans.modules.cloud.oracle.compute.ClusterNode;
+import org.netbeans.modules.cloud.oracle.developer.ContainerRepositoryNode;
import org.netbeans.modules.cloud.oracle.developer.ContainerTagItem;
import org.netbeans.modules.cloud.oracle.steps.ProjectStep;
import org.openide.awt.ActionID;
import org.openide.awt.ActionRegistration;
+import org.openide.nodes.Node;
+import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;
@@ -142,7 +151,7 @@ private void runInCluster() {
.addToLabels(APP, projectName)
.endMetadata()
.withNewSpec()
- .withReplicas(3)
+ .withReplicas(2)
.withNewSelector()
.addToMatchLabels(APP, projectName)
.endSelector()
@@ -188,8 +197,18 @@ private void runInCluster() {
.create();
}
});
+ } catch(Throwable t) {
+ NotificationUtils.showErrorMessage(t.getMessage());
} finally {
h.finish();
}
}
+
+ private void refresh() {
+ for (Node child : RootNode.instance().getChildren().getNodes()) {
+ if (child instanceof ClusterNode) {
+ ((ClusterNode) child).refresh();
+ }
+ }
+ }
}
diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/SetClusterNamespaceAction.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/k8s/SetClusterNamespaceAction.java
similarity index 87%
rename from enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/SetClusterNamespaceAction.java
rename to enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/k8s/SetClusterNamespaceAction.java
index bf0ba3581c38..5d2708ce88e0 100644
--- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/SetClusterNamespaceAction.java
+++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/k8s/SetClusterNamespaceAction.java
@@ -16,28 +16,20 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.netbeans.modules.cloud.oracle.assets;
+package org.netbeans.modules.cloud.oracle.assets.k8s;
import io.fabric8.kubernetes.api.model.Namespace;
import io.fabric8.kubernetes.api.model.NamespaceList;
-import io.fabric8.kubernetes.api.model.apps.Deployment;
-import io.fabric8.kubernetes.api.model.apps.DeploymentList;
-import io.fabric8.kubernetes.client.dsl.MixedOperation;
import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
import io.fabric8.kubernetes.client.dsl.Resource;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
-import java.util.LinkedList;
import java.util.List;
import org.netbeans.api.progress.ProgressHandle;
-import org.netbeans.api.project.Project;
+import org.netbeans.modules.cloud.oracle.assets.AbstractStep;
+import org.netbeans.modules.cloud.oracle.assets.Steps;
import org.netbeans.modules.cloud.oracle.compute.ClusterItem;
-import org.netbeans.modules.cloud.oracle.items.OCIItem;
-import org.netbeans.modules.cloud.oracle.items.TenancyItem;
-import org.netbeans.modules.cloud.oracle.steps.ProjectStep;
-import org.openide.DialogDescriptor;
-import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.awt.ActionID;
import org.openide.awt.ActionRegistration;
diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/k8s/ShowPodLogsAction.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/k8s/ShowPodLogsAction.java
new file mode 100644
index 000000000000..f446c3a3344f
--- /dev/null
+++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/k8s/ShowPodLogsAction.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.cloud.oracle.assets.k8s;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import org.netbeans.api.io.InputOutput;
+import org.netbeans.modules.cloud.oracle.compute.PodItem;
+import org.netbeans.spi.io.InputOutputProvider;
+import org.openide.awt.ActionID;
+import org.openide.awt.ActionRegistration;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Jan Horvath
+ */
+@ActionID(
+ category = "Tools",
+ id = "org.netbeans.modules.cloud.oracle.actions.ShowPodLogsAction"
+)
+@ActionRegistration(
+ displayName = "#PodLogs",
+ asynchronous = true
+)
+
+@NbBundle.Messages({
+ "PodLogs=Start port forwarding"
+})
+public class ShowPodLogsAction implements ActionListener {
+
+ private PodItem pod;
+
+ public ShowPodLogsAction(PodItem pod) {
+ this.pod = pod;
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+// KubernetesLoaders.startPortForward(pod);
+
+ InputOutputProvider, ?, ?, ?> newSpiDef
+ = Lookup.getDefault().lookup(InputOutputProvider.class);
+ Object io = newSpiDef.getIO("test io", true, Lookup.EMPTY);
+// newSpiDef.getOut(io);
+ }
+
+}
diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/ClusterNode.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/ClusterNode.java
index 87e1c14c5734..c0bd77194416 100644
--- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/ClusterNode.java
+++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/ClusterNode.java
@@ -26,7 +26,6 @@
import org.netbeans.modules.cloud.oracle.OCINode;
import org.netbeans.modules.cloud.oracle.compartment.CompartmentItem;
import org.netbeans.modules.cloud.oracle.items.OCID;
-import org.openide.nodes.Children;
import org.openide.util.NbBundle;
import com.oracle.bmc.containerengine.ContainerEngineClient;
@@ -43,7 +42,7 @@ public class ClusterNode extends OCINode {
private static final String CLUSTER_ICON = "org/netbeans/modules/cloud/oracle/resources/cluster.svg"; // NOI18N
public ClusterNode(ClusterItem cluster) {
- super(cluster, Children.LEAF);
+ super(cluster, new PodChildFactory(cluster));
setName(cluster.getName());
setDisplayName(cluster.getName());
setIconBaseWithExtension(CLUSTER_ICON);
@@ -87,5 +86,10 @@ public static ChildrenProvider.SessionAware getClu
.collect(Collectors.toList());
};
}
+
+ @Override
+ public void refresh() {
+ super.refresh();
+ }
}
diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/PodChildFactory.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/PodChildFactory.java
new file mode 100644
index 000000000000..4acffc3b5669
--- /dev/null
+++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/PodChildFactory.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.cloud.oracle.compute;
+
+import java.util.List;
+import org.netbeans.modules.cloud.oracle.RefreshableKeys;
+import org.openide.nodes.ChildFactory;
+import org.openide.nodes.Node;
+
+/**
+ *
+ * @author Jan Horvath
+ */
+public class PodChildFactory extends ChildFactory implements RefreshableKeys {
+
+ Pods pods;
+
+ public PodChildFactory(ClusterItem cluster) {
+ pods = Pods.from(cluster);
+ pods.addPropertyChangeListener(evt -> refreshKeys());
+ }
+
+ @Override
+ protected boolean createKeys(List toPopulate) {
+ toPopulate.addAll(pods.getItems());
+ return true;
+ }
+
+ @Override
+ protected Node[] createNodesForKey(PodItem key) {
+ return new Node[]{
+ new PodNode(key)
+ };
+ }
+
+ @Override
+ public void refreshKeys() {
+ refresh(false);
+ }
+
+
+}
diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/PodItem.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/PodItem.java
new file mode 100644
index 000000000000..2733f34989e8
--- /dev/null
+++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/PodItem.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.cloud.oracle.compute;
+
+import java.util.Objects;
+
+/**
+ *
+ * @author Jan Horvath
+ */
+public class PodItem {
+ final ClusterItem cluster;
+ final String namespace;
+ final String name;
+
+ public PodItem(ClusterItem cluster, String namespace, String name) {
+ this.cluster = cluster;
+ this.namespace = namespace;
+ this.name = name;
+ }
+
+ public ClusterItem getCluster() {
+ return cluster;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 7;
+ hash = 29 * hash + Objects.hashCode(this.cluster);
+ hash = 29 * hash + Objects.hashCode(this.namespace);
+ hash = 29 * hash + Objects.hashCode(this.name);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final PodItem other = (PodItem) obj;
+ if (!Objects.equals(this.namespace, other.namespace)) {
+ return false;
+ }
+ if (!Objects.equals(this.name, other.name)) {
+ return false;
+ }
+ return Objects.equals(this.cluster, other.cluster);
+ }
+
+}
diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/PodNode.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/PodNode.java
new file mode 100644
index 000000000000..31db777807e4
--- /dev/null
+++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/PodNode.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.cloud.oracle.compute;
+
+import org.openide.nodes.AbstractNode;
+import org.openide.nodes.Children;
+import org.openide.util.lookup.Lookups;
+
+/**
+ *
+ * @author Jan Horvath
+ */
+public class PodNode extends AbstractNode {
+
+ private static final String POD_ICON = "org/netbeans/modules/cloud/oracle/resources/pod.svg"; // NOI18N
+
+ public PodNode(PodItem pod) {
+ super(Children.create(new PortForwardsChildFactory(pod), true), Lookups.singleton(pod));
+ setName(pod.getName());
+ setIconBaseWithExtension(POD_ICON);
+ }
+
+
+}
diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/Pods.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/Pods.java
new file mode 100644
index 000000000000..00885f6dce38
--- /dev/null
+++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/Pods.java
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.cloud.oracle.compute;
+
+import io.fabric8.kubernetes.api.model.Pod;
+import io.fabric8.kubernetes.client.Watch;
+import io.fabric8.kubernetes.client.Watcher;
+import io.fabric8.kubernetes.client.WatcherException;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectInformation;
+import org.netbeans.api.project.ProjectUtils;
+import org.netbeans.modules.cloud.oracle.assets.OpenProjectsFinder;
+import org.netbeans.modules.cloud.oracle.assets.k8s.KubernetesUtils;
+import org.netbeans.modules.cloud.oracle.assets.k8s.PortForwards;
+import org.openide.util.Exceptions;
+import org.openide.util.RequestProcessor;
+
+/**
+ *
+ * @author Jan Horvath
+ */
+public class Pods {
+ private final RequestProcessor RP = new RequestProcessor(Pods.class);
+ private final ClusterItem cluster;
+ private Set names = null;
+ private final List pods = new ArrayList<> ();
+ private transient PropertyChangeSupport changeSupport = null;
+ private CountDownLatch latch = null;
+
+ private Pods(ClusterItem cluster) {
+ this.cluster = cluster;
+ }
+
+ public static Pods from(ClusterItem cluster) {
+ Pods instance = new Pods(cluster);
+ instance.init();
+ return instance;
+ }
+
+ public List getItems() {
+ return Collections.unmodifiableList(pods);
+ }
+
+ private void addPod(PodItem pod) {
+ pods.add(pod);
+ changeSupport.firePropertyChange("pods", pods.size(), pods.size() - 1);
+
+ }
+
+ private void removePod(PodItem pod) {
+ pods.remove(pod);
+ PortForwards.getDefault().closePortForward(pod.getName());
+ changeSupport.firePropertyChange("pods", pods.size(), pods.size() + 1);
+ }
+
+ private void removeAll() {
+ pods.clear();
+ changeSupport.firePropertyChange("pods", pods.size(), pods.size() + 1);
+ }
+
+ private void init() {
+
+ try {
+ changeSupport = new PropertyChangeSupport(this);
+ cluster.addChangeListener((PropertyChangeEvent evt) -> {
+ if ("namespace".equals(evt.getPropertyName())) {
+ watchNamespace((String) evt.getNewValue());
+ }
+ });
+ CompletableFuture projectsFuture = OpenProjectsFinder.getDefault().findTopLevelProjects();
+ projectsFuture.thenApply(projects -> {
+ List projectNames = new ArrayList<>();
+ for (int i = 0; i < projects.length; i++) {
+ ProjectInformation pi = ProjectUtils.getInformation(projects[i]);
+ projectNames.add(pi.getDisplayName());
+ }
+ return projectNames;
+ }).thenAccept(projectNames -> {
+ names = new HashSet<> (projectNames);
+
+ }).get();
+ watchNamespace(cluster.getNamespace());
+ } catch (InterruptedException | ExecutionException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+
+ private void watchNamespace(String namespace) {
+ removeAll();
+ if (latch != null) {
+ latch.countDown();
+ }
+ latch = new CountDownLatch(1);
+ addWatcher(cluster, namespace, new Watcher() {
+ @Override
+ public void eventReceived(Watcher.Action action, Pod t) {
+ String app = t.getMetadata().getLabels().get("app");
+ if (!names.contains(app)) {
+ return;
+ }
+ if (action == Watcher.Action.ADDED) {
+ addPod(new PodItem(cluster, t.getMetadata().getNamespace(), t.getMetadata().getName()));
+ } else if (action == Watcher.Action.DELETED) {
+ removePod(new PodItem(cluster, t.getMetadata().getNamespace(), t.getMetadata().getName()));
+ }
+ }
+
+ @Override
+ public void onClose(WatcherException we) {
+ System.out.println("close");
+ latch.countDown();
+ }
+ });
+ }
+
+ public void addPropertyChangeListener(PropertyChangeListener listener) {
+ changeSupport.addPropertyChangeListener(listener);
+ }
+
+ public void removePropertyChangeListener(PropertyChangeListener listener) {
+ changeSupport.removePropertyChangeListener(listener);
+ }
+
+ public void addWatcher(ClusterItem cluster, String namespace, Watcher watcher) {
+ RP.post(() -> {
+ KubernetesUtils.runWithClient(cluster, client -> {
+ try {
+ Watch watch = client.pods()
+ .inNamespace(namespace)
+ .watch(watcher);
+ latch.await();
+ watch.close();
+ } catch (InterruptedException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ });
+ });
+ }
+
+}
diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/PortForwardItem.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/PortForwardItem.java
new file mode 100644
index 000000000000..ffbccc84d8dc
--- /dev/null
+++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/PortForwardItem.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.cloud.oracle.compute;
+
+import java.util.Collections;
+import java.util.Map;
+import org.netbeans.modules.cloud.oracle.items.ContextValuesProvider;
+
+/**
+ *
+ * @author Jan Horvath
+ */
+public class PortForwardItem implements ContextValuesProvider {
+ final PodItem pod;
+ final int portLocal;
+ final int portRemote;
+
+ public PortForwardItem(PodItem pod, int portLocal, int portRemote) {
+ this.pod = pod;
+ this.portLocal = portLocal;
+ this.portRemote = portRemote;
+ }
+
+ public PodItem getPod() {
+ return pod;
+ }
+
+ public int getPortLocal() {
+ return portLocal;
+ }
+
+ public int getPortRemote() {
+ return portRemote;
+ }
+
+ @Override
+ public Map getContextValues() {
+ return Collections.singletonMap("portForward", "http://localhost:" + portLocal); //NOI18N
+ }
+
+
+}
diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/PortForwardNode.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/PortForwardNode.java
new file mode 100644
index 000000000000..f57461a56351
--- /dev/null
+++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/PortForwardNode.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.cloud.oracle.compute;
+
+import org.openide.nodes.AbstractNode;
+import org.openide.nodes.Children;
+import org.openide.util.NbBundle;
+import org.openide.util.lookup.Lookups;
+
+/**
+ *
+ * @author Jan Horvath
+ */
+/**
+ * Node representing all active port forwards for a given PodItem.
+ */
+@NbBundle.Messages({
+ "PortForwardDisplayName=→ Local {0} → Pod {1}"
+})
+public class PortForwardNode extends AbstractNode {
+ private static final String PORT_FORWARD_ICON = "org/netbeans/modules/cloud/oracle/resources/port_forward.svg"; // NOI18N
+ private final PortForwardItem portForward;
+
+ /**
+ * Creates a new node for an active port forward.
+ *
+ * @param portForward The PortForward instance to represent.
+ */
+ public PortForwardNode(PortForwardItem portForward) {
+ super(Children.LEAF, Lookups.singleton(portForward));
+ this.portForward = portForward;
+ setDisplayName(Bundle.PortForwardDisplayName(
+ String.valueOf(portForward.portLocal),
+ String.valueOf(portForward.portRemote)
+
+ ));
+// setIconBaseWithExtension(PORT_FORWARD_ICON);
+ }
+
+}
diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/PortForwardsChildFactory.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/PortForwardsChildFactory.java
new file mode 100644
index 000000000000..03c76c332463
--- /dev/null
+++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/compute/PortForwardsChildFactory.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.cloud.oracle.compute;
+
+import java.util.List;
+import org.netbeans.modules.cloud.oracle.RefreshableKeys;
+import org.netbeans.modules.cloud.oracle.assets.k8s.PortForwards;
+import org.openide.nodes.Node;
+
+/**
+ * Factory to create child nodes for active port forwards of a PodItem.
+ */
+class PortForwardsChildFactory extends org.openide.nodes.ChildFactory implements RefreshableKeys {
+
+ private final PodItem podItem;
+
+ /**
+ * Creates a new factory for child nodes of port forwards.
+ *
+ * @param podItem The PodItem to fetch port forwards for.
+ */
+ public PortForwardsChildFactory(PodItem podItem) {
+ this.podItem = podItem;
+ PortForwards.getDefault().addPropertyChangeListener(podItem, evt -> {
+ if (podItem.getName().equals(evt.getPropertyName())) {
+ refreshKeys();
+ }
+ });
+ }
+
+ @Override
+ protected boolean createKeys(List toPopulate) {
+ List portForwards = PortForwards.getDefault().getActivePortForwards(podItem.getName());
+ if (portForwards != null) {
+ toPopulate.addAll(portForwards);
+ }
+ return true;
+ }
+
+ @Override
+ protected Node createNodeForKey(PortForwardItem portForward) {
+ return new PortForwardNode(portForward);
+ }
+
+ @Override
+ public void refreshKeys() {
+ refresh(false);
+ }
+
+}
diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/items/ContextValuesProvider.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/items/ContextValuesProvider.java
new file mode 100644
index 000000000000..a8360006b828
--- /dev/null
+++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/items/ContextValuesProvider.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.cloud.oracle.items;
+
+import java.util.Map;
+
+/**
+ * Supplies key/value pairs to determine and assign appropriate context menu items in the UI.
+ *
+ * @author Jan Horvath
+ */
+public interface ContextValuesProvider {
+
+ public Map getContextValues();
+
+}
diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/resources/pod.svg b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/resources/pod.svg
new file mode 100644
index 000000000000..98403a24370c
--- /dev/null
+++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/resources/pod.svg
@@ -0,0 +1,133 @@
+
+
+
+
+
+
diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/resources/port_forward.svg b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/resources/port_forward.svg
new file mode 100644
index 000000000000..98403a24370c
--- /dev/null
+++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/resources/port_forward.svg
@@ -0,0 +1,133 @@
+
+
+
+
+
+
diff --git a/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/LspAssetsDecorationProvider.java b/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/LspAssetsDecorationProvider.java
index 6a3daa7bd5c7..068240aec3a0 100644
--- a/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/LspAssetsDecorationProvider.java
+++ b/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/LspAssetsDecorationProvider.java
@@ -19,6 +19,7 @@
package org.netbeans.modules.nbcode.integration;
import java.net.URL;
+import java.util.Map;
import java.util.Optional;
import java.util.logging.Logger;
import org.netbeans.modules.cloud.oracle.adm.URLProvider;
@@ -26,9 +27,11 @@
import org.netbeans.modules.cloud.oracle.bucket.BucketItem;
import org.netbeans.modules.cloud.oracle.compute.ClusterItem;
import org.netbeans.modules.cloud.oracle.compute.ComputeInstanceItem;
+import org.netbeans.modules.cloud.oracle.compute.PortForwardItem;
import org.netbeans.modules.cloud.oracle.database.DatabaseItem;
import org.netbeans.modules.cloud.oracle.developer.ContainerRepositoryItem;
import org.netbeans.modules.cloud.oracle.developer.ContainerTagItem;
+import org.netbeans.modules.cloud.oracle.items.ContextValuesProvider;
import org.netbeans.modules.cloud.oracle.items.OCIItem;
import org.netbeans.modules.cloud.oracle.vault.SecretItem;
import org.netbeans.modules.java.lsp.server.explorer.NodeLookupContextValues;
@@ -56,6 +59,7 @@ public class LspAssetsDecorationProvider implements TreeDataProvider.Factory {
public static final String CTXVALUE_PREFIX_SECRET_LIFECYCLE_STATE = "lifecycleState:"; // NOI18N
public static final String CTXVALUE_PREFIX_CLUSTER_NAMESPACE = "clusterNamespace:"; // NOI18N
public static final String CTXVALUE_PREFIX_CONSOLE_URL = "consoleUrl:"; // NOI18N
+ public static final String CTXVALUE_PREFIX_PORT_FORWARD_URL = "portForward:"; // NOI18N
@Override
public synchronized TreeDataProvider createProvider(String treeId) {
@@ -73,63 +77,70 @@ public TreeItemData createDecorations(Node n, boolean expanded) {
boolean set = false;
OCIItem item = n.getLookup().lookup(OCIItem.class);
- if (item == null) {
- return null;
- }
- if (item instanceof URLProvider) {
- URL consoleURL = ((URLProvider) item).getURL();
- if (consoleURL != null) {
- d.addContextValues(CTXVALUE_PREFIX_CONSOLE_URL + consoleURL.toExternalForm());
- set = true;
+ if (item != null) {
+ if (item instanceof URLProvider) {
+ URL consoleURL = ((URLProvider) item).getURL();
+ if (consoleURL != null) {
+ d.addContextValues(CTXVALUE_PREFIX_CONSOLE_URL + consoleURL.toExternalForm());
+ set = true;
+ }
}
- }
- refName = CloudAssets.getDefault().getReferenceName(item);
- if (refName != null) {
- d.addContextValues(CTXVALUE_PREFIX_REFERENCE_NAME + refName);
- set = true;
- }
- if (item instanceof ClusterItem) {
- String namespace = ((ClusterItem) item).getNamespace();
- if (namespace != null) {
- d.addContextValues(CTXVALUE_PREFIX_CLUSTER_NAMESPACE + namespace);
+ refName = CloudAssets.getDefault().getReferenceName(item);
+ if (refName != null) {
+ d.addContextValues(CTXVALUE_PREFIX_REFERENCE_NAME + refName);
set = true;
}
- }
- if (item instanceof ComputeInstanceItem) {
- String publicIp = ((ComputeInstanceItem) item).getPublicIp();
- if (publicIp != null) {
- d.addContextValues(CTXVALUE_PREFIX_PUBLIC_IP + publicIp);
+ if (item instanceof ClusterItem) {
+ String namespace = ((ClusterItem) item).getNamespace();
+ if (namespace != null) {
+ d.addContextValues(CTXVALUE_PREFIX_CLUSTER_NAMESPACE + namespace);
+ set = true;
+ }
+ }
+ if (item instanceof ComputeInstanceItem) {
+ String publicIp = ((ComputeInstanceItem) item).getPublicIp();
+ if (publicIp != null) {
+ d.addContextValues(CTXVALUE_PREFIX_PUBLIC_IP + publicIp);
+ set = true;
+ }
+ }
+ if (item instanceof ContainerRepositoryItem) {
+ ContainerRepositoryItem repo = (ContainerRepositoryItem) item;
+ d.addContextValues(CTXVALUE_PREFIX_IMAGE_COUNT + repo.getImageCount());
+ d.addContextValues(CTXVALUE_PREFIX_REPOSITORY_PUBLIC + repo.getIsPublic());
set = true;
}
- }
- if (item instanceof ContainerRepositoryItem) {
- ContainerRepositoryItem repo = (ContainerRepositoryItem) item;
- d.addContextValues(CTXVALUE_PREFIX_IMAGE_COUNT + repo.getImageCount());
- d.addContextValues(CTXVALUE_PREFIX_REPOSITORY_PUBLIC + repo.getIsPublic());
- set = true;
- }
- if (item instanceof ContainerTagItem) {
- String imageUrl = ((ContainerTagItem) item).getUrl();
- Optional instance = CloudAssets.getDefault().getAssignedItems().stream().filter(i -> i.getClass().equals(ComputeInstanceItem.class)).findFirst();
- if (instance.isPresent()) {
- d.addContextValues(CTXVALUE_PREFIX_PUBLIC_IP + ((ComputeInstanceItem) instance.get()).getPublicIp());
- } else {
- ClusterItem cluster = CloudAssets.getDefault().getItem(ClusterItem.class);
- if (cluster != null) {
- d.addContextValues(CTXVALUE_PREFIX_CLUSTER_NAME + cluster.getName());
+ if (item instanceof ContainerTagItem) {
+ String imageUrl = ((ContainerTagItem) item).getUrl();
+ Optional instance = CloudAssets.getDefault().getAssignedItems().stream().filter(i -> i.getClass().equals(ComputeInstanceItem.class)).findFirst();
+ if (instance.isPresent()) {
+ d.addContextValues(CTXVALUE_PREFIX_PUBLIC_IP + ((ComputeInstanceItem) instance.get()).getPublicIp());
+ } else {
+ ClusterItem cluster = CloudAssets.getDefault().getItem(ClusterItem.class);
+ if (cluster != null) {
+ d.addContextValues(CTXVALUE_PREFIX_CLUSTER_NAME + cluster.getName());
+ }
}
+ d.addContextValues(CTXVALUE_PREFIX_IMAGE_URL + imageUrl);
+ set = true;
+ }
+ if (item instanceof BucketItem
+ || item instanceof DatabaseItem) {
+ d.addContextValues(CTXVALUE_CAP_REFERENCE_NAME);
+ set = true;
}
- d.addContextValues(CTXVALUE_PREFIX_IMAGE_URL + imageUrl);
- set = true;
- }
- if (item instanceof BucketItem
- || item instanceof DatabaseItem) {
- d.addContextValues(CTXVALUE_CAP_REFERENCE_NAME);
- set = true;
- }
- if (item instanceof SecretItem) {
- d.addContextValues(CTXVALUE_PREFIX_SECRET_LIFECYCLE_STATE + ((SecretItem) item).getLifecycleState());
+ if (item instanceof SecretItem) {
+ d.addContextValues(CTXVALUE_PREFIX_SECRET_LIFECYCLE_STATE + ((SecretItem) item).getLifecycleState());
+ set = true;
+ }
+ }
+
+ ContextValuesProvider context = n.getLookup().lookup(ContextValuesProvider.class);
+ if (context != null) {
+ for (Map.Entry entry : context.getContextValues().entrySet()) {
+ d.addContextValues(entry.getKey() + ":" + entry.getValue());
+ }
set = true;
}
return set ? d : null;
diff --git a/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/cloud-cookies.contextValues b/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/cloud-cookies.contextValues
index ec8d31fbfa0c..1e4498166fc4 100644
--- a/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/cloud-cookies.contextValues
+++ b/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/cloud-cookies.contextValues
@@ -24,6 +24,8 @@ org.netbeans.modules.cloud.oracle.devops.DevopsProjectItem
org.netbeans.modules.cloud.oracle.assets.SuggestedItem
org.netbeans.modules.cloud.oracle.compute.ClusterItem
org.netbeans.modules.cloud.oracle.compute.ComputeInstanceItem
+org.netbeans.modules.cloud.oracle.compute.PodItem
+org.netbeans.modules.cloud.oracle.compute.PortForwardItem
org.netbeans.modules.cloud.oracle.bucket.BucketItem
org.netbeans.modules.cloud.oracle.vault.VaultItem
org.netbeans.modules.cloud.oracle.developer.ContainerRepositoryItem
diff --git a/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/layer.xml b/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/layer.xml
index bf032a302b99..311976aa37bd 100644
--- a/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/layer.xml
+++ b/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/layer.xml
@@ -64,7 +64,7 @@
-
+
diff --git a/java/java.lsp.server/vscode/package.json b/java/java.lsp.server/vscode/package.json
index 89a105f37e1f..e4504bd6ac30 100644
--- a/java/java.lsp.server/vscode/package.json
+++ b/java/java.lsp.server/vscode/package.json
@@ -529,6 +529,10 @@
"command": "nbls.cloud.openConsole",
"title": "Open in OCI Console"
},
+ {
+ "command": "nbls.cloud.openInBrowser",
+ "title": "Open in Browser"
+ },
{
"command": "nbls.cloud.imageUrl.copy",
"title": "Copy pull command"
@@ -641,6 +645,11 @@
"title": "Set a Cluster Namespace",
"icon": "$(edit)"
},
+ {
+ "command": "nbls:Tools:org.netbeans.modules.cloud.oracle.actions.PortForwardAction",
+ "title": "Start port forwarding",
+ "icon": "$(arrow-small-right)"
+ },
{
"command": "nbls:Tools:org.netbeans.modules.cloud.oracle.actions.RemoveFromProject",
"title": "Remove from Oracle Cloud Assets",
@@ -881,6 +890,10 @@
"command": "nbls.cloud.openConsole",
"when": "false"
},
+ {
+ "command": "nbls.cloud.openInBrowser",
+ "when": "false"
+ },
{
"command": "nbls.cloud.imageUrl.copy",
"when": "false"
@@ -937,6 +950,10 @@
"command": "nbls:Tools:org.netbeans.modules.cloud.oracle.actions.SetClusterNamespace",
"when": "false"
},
+ {
+ "command": "nbls:Tools:org.netbeans.modules.cloud.oracle.actions.PortForwardAction",
+ "when": "false"
+ },
{
"command": "nbls:Tools:org.netbeans.modules.cloud.oracle.actions.RemoveFromProject",
"when": "false"
@@ -1138,6 +1155,11 @@
"when": "viewItem =~ /consoleUrl:.*/",
"group": "oci@2000"
},
+ {
+ "command": "nbls.cloud.openInBrowser",
+ "when": "viewItem =~ /portForward:.*/",
+ "group": "oci@2000"
+ },
{
"command": "nbls.cloud.imageUrl.copy",
"when": "viewItem =~ /imageUrl:.*/",
@@ -1198,9 +1220,14 @@
"when": "viewItem =~ /clusterNamespace:.*/ && view == cloud.assets",
"group": "inline@14"
},
+ {
+ "command": "nbls:Tools:org.netbeans.modules.cloud.oracle.actions.PortForwardAction",
+ "when": "viewItem =~ /class:oracle.compute.PodItem/",
+ "group": "inline@14"
+ },
{
"command": "nbls:Tools:org.netbeans.modules.cloud.oracle.actions.RemoveFromProject",
- "when": "viewItem =~ /^(?!.*(Suggested|ContainerTagItem)).*/ && view == cloud.assets",
+ "when": "viewItem =~ /^(?!.*(Suggested|ContainerTagItem|PodItem|PortForwardItem)).*/ && view == cloud.assets",
"group": "inline@17"
},
{
@@ -1210,7 +1237,7 @@
},
{
"command": "nbls:Tools:org.netbeans.modules.cloud.oracle.actions.CloudRefresh",
- "when": "viewItem =~ /^(?!.*(ContainerTagItem|Suggested)).*class:oracle.*$/",
+ "when": "viewItem =~ /^(?!.*(ContainerTagItem|Suggested|PortForwardItem)).*class:oracle.*$/",
"group": "inline@13"
},
{
@@ -1332,6 +1359,14 @@
"uriExpression": "nbres:/org/netbeans/modules/cloud/oracle/resources/cluster.svg",
"codeicon": "compass"
},
+ {
+ "uriExpression": "nbres:/org/netbeans/modules/cloud/oracle/resources/pod.svg",
+ "codeicon": "vm"
+ },
+ {
+ "uriExpression": "nbres:/org/netbeans/modules/cloud/oracle/resources/port_forward.svg",
+ "codeicon": "arrow-small-right"
+ },
{
"uriExpression": "nbres:/org/netbeans/modules/cloud/oracle/resources/vault.svg",
"codeicon": "lock"
diff --git a/java/java.lsp.server/vscode/src/extension.ts b/java/java.lsp.server/vscode/src/extension.ts
index 88ba0196a863..5505ea413496 100644
--- a/java/java.lsp.server/vscode/src/extension.ts
+++ b/java/java.lsp.server/vscode/src/extension.ts
@@ -912,6 +912,14 @@ export function activate(context: ExtensionContext): VSNetBeansAPI {
}
));
+ context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.cloud.openInBrowser',
+ async (node) => {
+ const portForward = getValueAfterPrefix(node.contextValue, 'portForward:');
+ const url = vscode.Uri.parse(portForward);
+ vscode.env.openExternal(url);
+ }
+ ));
+
context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.cloud.imageUrl.copy',
async (node) => {
const imageUrl = getValueAfterPrefix(node.contextValue, 'imageUrl:');
@@ -977,6 +985,21 @@ export function activate(context: ExtensionContext): VSNetBeansAPI {
});
}
+let queryDocument: vscode.TextDocument | undefined;
+
+async function getRestDocument(): Promise {
+ if (queryDocument) {
+ const allDocuments = vscode.workspace.textDocuments;
+ if (!allDocuments.includes(queryDocument)) {
+ queryDocument = undefined;
+ }
+ }
+ if (!queryDocument) {
+ queryDocument = await vscode.workspace.openTextDocument({ language: 'http' });
+ }
+ return queryDocument;
+}
+
/**
* Pending maintenance (install) task, activations should be chained after it.
*/