This is an automated email from the ASF dual-hosted git repository.
jhorvath pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new 7b241cf932 Update OKE ConfigMap with Vault details
new 894d91bf68 Merge pull request #6746 from jhorvath/vault-to-configmap
7b241cf932 is described below
commit 7b241cf932be7e0f32cb8c397667a83a51291a68
Author: Jan Horvath <[email protected]>
AuthorDate: Fri Nov 24 18:11:37 2023 +0100
Update OKE ConfigMap with Vault details
---
enterprise/cloud.oracle/nbproject/project.xml | 8 +
.../oracle/actions/AddDbConnectionToVault.java | 373 +++++++++++++++++----
.../cloud/oracle/devops/DevopsProjectService.java | 90 +++++
.../oracle/actions/AddDbConnectionToVaultTest.java | 83 ++++-
.../nbcode/integration/LspDevopsConfigFinder.java | 52 +++
.../modules/java/lsp/server/LspServerState.java | 9 +
.../modules/java/lsp/server/protocol/Server.java | 7 +
.../lsp/server/protocol/WorkspaceServiceImpl.java | 57 ++++
java/java.lsp.server/vscode/BUILD.md | 5 +
9 files changed, 620 insertions(+), 64 deletions(-)
diff --git a/enterprise/cloud.oracle/nbproject/project.xml
b/enterprise/cloud.oracle/nbproject/project.xml
index 2aee363fd3..c955b26745 100644
--- a/enterprise/cloud.oracle/nbproject/project.xml
+++ b/enterprise/cloud.oracle/nbproject/project.xml
@@ -82,6 +82,14 @@
<specification-version>2.18</specification-version>
</run-dependency>
</dependency>
+ <dependency>
+ <code-name-base>com.google.gson</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <specification-version>2.8.9</specification-version>
+ </run-dependency>
+ </dependency>
<dependency>
<code-name-base>org.netbeans.modules.db</code-name-base>
<build-prerequisite/>
diff --git
a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/actions/AddDbConnectionToVault.java
b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/actions/AddDbConnectionToVault.java
index ee2a170dd8..a5fac3c988 100644
---
a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/actions/AddDbConnectionToVault.java
+++
b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/actions/AddDbConnectionToVault.java
@@ -18,6 +18,19 @@
*/
package org.netbeans.modules.cloud.oracle.actions;
+import com.oracle.bmc.devops.DevopsClient;
+import com.oracle.bmc.devops.model.DeployArtifactSource;
+import com.oracle.bmc.devops.model.DeployArtifactSummary;
+import com.oracle.bmc.devops.model.InlineDeployArtifactSource;
+import com.oracle.bmc.devops.model.ProjectSummary;
+import com.oracle.bmc.devops.model.UpdateDeployArtifactDetails;
+import com.oracle.bmc.devops.requests.GetDeployArtifactRequest;
+import com.oracle.bmc.devops.requests.ListDeployArtifactsRequest;
+import com.oracle.bmc.devops.requests.ListProjectsRequest;
+import com.oracle.bmc.devops.requests.UpdateDeployArtifactRequest;
+import com.oracle.bmc.devops.responses.GetDeployArtifactResponse;
+import com.oracle.bmc.devops.responses.ListDeployArtifactsResponse;
+import com.oracle.bmc.devops.responses.ListProjectsResponse;
import com.oracle.bmc.identity.Identity;
import com.oracle.bmc.identity.IdentityClient;
import com.oracle.bmc.identity.model.Compartment;
@@ -35,17 +48,18 @@ import com.oracle.bmc.vault.model.UpdateSecretDetails;
import com.oracle.bmc.vault.requests.CreateSecretRequest;
import com.oracle.bmc.vault.requests.ListSecretsRequest;
import com.oracle.bmc.vault.requests.UpdateSecretRequest;
-import com.oracle.bmc.vault.responses.CreateSecretResponse;
import com.oracle.bmc.vault.responses.ListSecretsResponse;
import com.oracle.bmc.vault.responses.UpdateSecretResponse;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -56,6 +70,7 @@ import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
@@ -67,6 +82,8 @@ import static
org.netbeans.modules.cloud.oracle.OCIManager.getDefault;
import org.netbeans.modules.cloud.oracle.OCIProfile;
import org.netbeans.modules.cloud.oracle.OCISessionInitiator;
import org.netbeans.modules.cloud.oracle.compartment.CompartmentItem;
+import org.netbeans.modules.cloud.oracle.devops.DevopsProjectItem;
+import org.netbeans.modules.cloud.oracle.devops.DevopsProjectService;
import org.netbeans.modules.cloud.oracle.items.OCID;
import org.netbeans.modules.cloud.oracle.items.OCIItem;
import org.netbeans.modules.cloud.oracle.items.TenancyItem;
@@ -114,7 +131,15 @@ import org.openide.util.Pair;
"SecretExists=Secrets with name {0} already exists",
"NoProfile=There is not any OCI profile in the config",
"NoCompartment=There are no compartments in the Tenancy",
- "Password=Enter password for Database user {0}"
+ "Password=Enter password for Database user {0}",
+ "NoConfigMap=No ConfigMap found in the Devops project {0}",
+ "SelectDevopsProject=Select Devops Project",
+ "NoDevopsProjects=There are no Devops Projects in selected Compartment",
+ "ConfigmapUpdateFailed=Failed to update ConfigMap",
+ "CreatingSecret=Creating secret {0}",
+ "UpdatingSecret=Updating secret {0}",
+ "UpdatingVault=Updating {0} Vault",
+ "ReadingSecrets=Reading existing Secrets"
})
public class AddDbConnectionToVault implements ActionListener {
@@ -182,10 +207,11 @@ public class AddDbConnectionToVault implements
ActionListener {
return this;
}
- public void setValue(String selected) {
+ public void setValue(String value) {
for (OCIProfile profile : profiles) {
- if (profile.getId().equals(selected)) {
+ if (profile.getId().equals(value)) {
profile.getTenancy().ifPresent(t -> this.selected.set(t));
+ OCIManager.getDefault().setActiveProfile(profile);
break;
}
}
@@ -286,7 +312,7 @@ public class AddDbConnectionToVault implements
ActionListener {
@Override
public Step getNext() {
- return new KeyStep().prepare(selected);
+ return new KeyStep().prepare(getValue());
}
@Override
@@ -297,7 +323,7 @@ public class AddDbConnectionToVault implements
ActionListener {
@Override
public VaultItem getValue() {
if (onlyOneChoice()) {
- vaults.values().iterator().next();
+ selected = vaults.values().iterator().next();
}
return selected;
}
@@ -452,7 +478,7 @@ public class AddDbConnectionToVault implements
ActionListener {
}
}
-
+
class PasswordStep implements Step<Result, Result> {
private Result item;
@@ -468,7 +494,7 @@ public class AddDbConnectionToVault implements
ActionListener {
@Override
public NotifyDescriptor createInput() {
- return new NotifyDescriptor.InputLine("DEFAULT",
Bundle.Password(context.getUser())); //NOI18N
+ return new NotifyDescriptor.PasswordLine("DEFAULT",
Bundle.Password(context.getUser())); //NOI18N
}
@Override
@@ -478,7 +504,7 @@ public class AddDbConnectionToVault implements
ActionListener {
@Override
public Step getNext() {
- return null;
+ return new DevopsStep().prepare(item);
}
@Override
@@ -491,12 +517,80 @@ public class AddDbConnectionToVault implements
ActionListener {
return item;
}
}
-
+
+ class DevopsStep implements Step<Result, Result> {
+
+ private Result item;
+ private Map<String, DevopsProjectItem> devopsProjects;
+
+ @Override
+ public Step<Result, Result> prepare(Result item) {
+ this.item = item;
+ ProgressHandle h =
ProgressHandle.createHandle(Bundle.MSG_CollectingItems());
+ h.start();
+ h.progress(Bundle.MSG_CollectingItems_Text());
+ try {
+ List<String> devops =
DevopsProjectService.getDevopsProjectOcid();
+
+ Map<String, DevopsProjectItem> allProjectsInCompartment =
getDevopsProjects(item.vault.getCompartmentId());
+ Map<String, DevopsProjectItem> filtered =
allProjectsInCompartment.entrySet()
+ .stream()
+ .filter(e ->
devops.contains(e.getValue().getKey().getValue()))
+ .collect(Collectors
+ .toMap(Entry::getKey, Entry::getValue));
+ if (filtered.size() > 0) {
+ devopsProjects = filtered;
+ } else {
+ devopsProjects = allProjectsInCompartment;
+ }
+ if (devopsProjects.size() == 1) {
+ item.project = devopsProjects.values().iterator().next();
+ }
+
+ } finally {
+ h.finish();
+ }
+ return this;
+ }
+
+ @Override
+ public NotifyDescriptor createInput() {
+ if (devopsProjects.size() > 1) {
+ return createQuickPick(devopsProjects,
Bundle.SelectDevopsProject());
+ }
+ if (devopsProjects.isEmpty()) {
+ return new NotifyDescriptor.QuickPick("",
Bundle.NoDevopsProjects(), Collections.emptyList(), false);
+ }
+ throw new IllegalStateException("No data to create input"); //
NOI18N
+ }
+
+ @Override
+ public boolean onlyOneChoice() {
+ return devopsProjects.size() == 1;
+ }
+
+ @Override
+ public Step getNext() {
+ return null;
+ }
+
+ @Override
+ public void setValue(String projectName) {
+ item.project = devopsProjects.get(projectName);
+ }
+
+ @Override
+ public Result getValue() {
+ return item;
+ }
+ }
+
static class Result {
VaultItem vault;
KeyItem key;
String datasourceName;
String password;
+ DevopsProjectItem project;
private boolean update;
}
@@ -510,8 +604,9 @@ public class AddDbConnectionToVault implements
ActionListener {
NotifyDescriptor.ComposedInput.Callback createInput() {
return new NotifyDescriptor.ComposedInput.Callback() {
+ private int lastNumber = 0;
- private void showInput(Step step, NotifyDescriptor desc) {
+ private void readValue(Step step, NotifyDescriptor desc) {
String selected = null;
if (!step.onlyOneChoice()) {
if (desc instanceof NotifyDescriptor.QuickPick) {
@@ -528,31 +623,34 @@ public class AddDbConnectionToVault implements
ActionListener {
}
}
- NotifyDescriptor prepareInput(NotifyDescriptor.ComposedInput
input, int number) {
+ @Override
+ public NotifyDescriptor
createInput(NotifyDescriptor.ComposedInput input, int number) {
if (number == 1) {
- steps.get(0).prepare(null);
- return steps.get(0).createInput();
- }
- if (steps.size() > number) {
+ while (steps.size() > 1) {
+ steps.removeLast();
+ }
+ steps.getLast().prepare(null);
+ } else if (lastNumber > number) {
steps.removeLast();
+ while(steps.getLast().onlyOneChoice() && steps.size()
> 1) {
+ steps.removeLast();
+ }
+ lastNumber = number;
return steps.getLast().createInput();
+ } else {
+ readValue(steps.getLast(), input.getInputs()[number -
2]);
+ steps.add(steps.getLast().getNext());
}
- showInput(steps.getLast(), input.getInputs()[number - 2]);
- Step currentStep = steps.getLast().getNext();
- if (currentStep == null) {
- return null;
+ lastNumber = number;
+
+ while(steps.getLast() != null &&
steps.getLast().onlyOneChoice()) {
+ steps.add(steps.getLast().getNext());
}
-
- steps.add(currentStep);
- if (currentStep.onlyOneChoice()) {
- return prepareInput(input, number);
+ if (steps.getLast() == null) {
+ steps.removeLast();
+ return null;
}
- return currentStep.createInput();
- }
-
- @Override
- public NotifyDescriptor
createInput(NotifyDescriptor.ComposedInput input, int number) {
- return prepareInput(input, number);
+ return steps.getLast().createInput();
}
};
}
@@ -576,29 +674,33 @@ public class AddDbConnectionToVault implements
ActionListener {
}
private void addDbConnectionToVault(Result item) {
- VaultsClient client =
VaultsClient.builder().build(getDefault().getActiveProfile().getConfigProvider());
+ ProgressHandle h =
ProgressHandle.createHandle(Bundle.UpdatingVault(item.vault.getName()));
+ h.start();
+ h.progress(Bundle.ReadingSecrets());
+
+ try {
+ VaultsClient client =
VaultsClient.builder().build(getDefault().getActiveProfile().getConfigProvider());
- ListSecretsRequest listSecretsRequest = ListSecretsRequest.builder()
- .compartmentId(item.vault.getCompartmentId())
- .vaultId(item.vault.getKey().getValue())
- .limit(88)
- .build();
+ ListSecretsRequest listSecretsRequest =
ListSecretsRequest.builder()
+ .compartmentId(item.vault.getCompartmentId())
+ .vaultId(item.vault.getKey().getValue())
+ .limit(88)
+ .build();
- ListSecretsResponse secrets = client.listSecrets(listSecretsRequest);
+ ListSecretsResponse secrets =
client.listSecrets(listSecretsRequest);
Map<String, String> existingSecrets = secrets.getItems().stream()
.collect(Collectors.toMap(s -> s.getSecretName(), s ->
s.getId()));
- Map<String, String> values = new HashMap<String, String>() {
- {
- put("Username", context.getUser()); //NOI18N
- put("Password", item.password); //NOI18N
- put("OCID", (String)
context.getConnectionProperties().get("OCID")); //NOI18N
- put("wallet_Password", UUID.randomUUID().toString()); //NOI18N
- }
- };
+ Map<String, String> values = new HashMap<String, String>() {
+ {
+ put("Username", context.getUser()); //NOI18N
+ put("Password", item.password); //NOI18N
+ put("OCID", (String)
context.getConnectionProperties().get("OCID")); //NOI18N
+ put("wallet_Password", UUID.randomUUID().toString());
//NOI18N
+ }
+ };
- try {
for (Entry<String, String> entry : values.entrySet()) {
String secretName = "DATASOURCES_" + item.datasourceName + "_"
+ entry.getKey().toUpperCase(); //NOI18N
String base64Content =
Base64.getEncoder().encodeToString(entry.getValue().getBytes(StandardCharsets.UTF_8));
@@ -607,6 +709,7 @@ public class AddDbConnectionToVault implements
ActionListener {
.content(base64Content)
.stage(SecretContentDetails.Stage.Current).build();
if (existingSecrets.containsKey(secretName)) {
+ h.progress(Bundle.UpdatingSecret(secretName));
UpdateSecretDetails updateSecretDetails =
UpdateSecretDetails.builder()
.secretContent(contentDetails)
.build();
@@ -614,8 +717,14 @@ public class AddDbConnectionToVault implements
ActionListener {
.secretId(existingSecrets.get(secretName))
.updateSecretDetails(updateSecretDetails)
.build();
- UpdateSecretResponse response =
client.updateSecret(request);
+ try {
+ UpdateSecretResponse response =
client.updateSecret(request);
+ } catch (BmcException ex) {
+ // Update fails if the new value is same as the
current one. It is safe to ignore
+ LOG.log(Level.WARNING, "Update of secret failed", ex);
+ }
} else {
+ h.progress(Bundle.CreatingSecret(secretName));
CreateSecretDetails createDetails =
CreateSecretDetails.builder()
.secretName(secretName)
.secretContent(contentDetails)
@@ -629,17 +738,152 @@ public class AddDbConnectionToVault implements
ActionListener {
.builder()
.createSecretDetails(createDetails)
.build();
- CreateSecretResponse response =
client.createSecret(request);
+ client.createSecret(request);
}
}
- } catch (BmcException e) {
- NotifyDescriptor.Message msg = new
NotifyDescriptor.Message(e.getMessage());
+ // Add Vault to the ConfigMap artifact
+ DevopsClient devopsClient =
DevopsClient.builder().build(OCIManager.getDefault().getActiveProfile().getConfigProvider());
+ ListDeployArtifactsRequest request =
ListDeployArtifactsRequest.builder()
+ .projectId(item.project.getKey().getValue()).build();
+ ListDeployArtifactsResponse response =
devopsClient.listDeployArtifacts(request);
+ List<DeployArtifactSummary> artifacts =
response.getDeployArtifactCollection().getItems();
+ boolean found = false;
+ for (DeployArtifactSummary artifact : artifacts) {
+ if ((item.project.getName() +
"_oke_configmap").equals(artifact.getDisplayName())) { //NOI18N
+ h.progress("updating " + item.project.getName() +
"_oke_configmap"); //NOI18N
+ found = true;
+ GetDeployArtifactRequest artRequest =
GetDeployArtifactRequest.builder().deployArtifactId(artifact.getId()).build();
+ GetDeployArtifactResponse artResponse =
devopsClient.getDeployArtifact(artRequest);
+ DeployArtifactSource source =
artResponse.getDeployArtifact().getDeployArtifactSource();
+ if (source instanceof InlineDeployArtifactSource) {
+ byte[] content = ((InlineDeployArtifactSource)
source).getBase64EncodedContent();
+ String srcString = updateProperties(new
String(content, StandardCharsets.UTF_8),
+ item.vault.getCompartmentId(),
item.vault.getKey().getValue(), item.datasourceName);
+ byte[] base64Content =
Base64.getEncoder().encode(srcString.getBytes(StandardCharsets.UTF_8));
+ DeployArtifactSource updatedSource =
InlineDeployArtifactSource.builder()
+ .base64EncodedContent(base64Content).build();
+ UpdateDeployArtifactDetails updateArtifactDetails =
UpdateDeployArtifactDetails.builder()
+ .deployArtifactSource(updatedSource)
+ .build();
+ UpdateDeployArtifactRequest updateArtifactRequest =
UpdateDeployArtifactRequest.builder()
+
.updateDeployArtifactDetails(updateArtifactDetails)
+ .deployArtifactId(artifact.getId())
+ .build();
+
devopsClient.updateDeployArtifact(updateArtifactRequest);
+ }
+ }
+ }
+ if (!found) {
+ NotifyDescriptor.Message msg = new
NotifyDescriptor.Message(Bundle.NoConfigMap(item.project.getName()),
NotifyDescriptor.WARNING_MESSAGE);
+ DialogDisplayer.getDefault().notify(msg);
+ }
+ NotifyDescriptor.Message msg = new
NotifyDescriptor.Message(Bundle.SecretsCreated());
DialogDisplayer.getDefault().notify(msg);
- throw new RuntimeException(e);
+ } catch(ThreadDeath e) {
+ throw e;
+ } catch (Throwable e) {
+ h.finish();
+ NotifyDescriptor.Message msg = new
NotifyDescriptor.Message(e.getMessage(), NotifyDescriptor.WARNING_MESSAGE);
+ DialogDisplayer.getDefault().notify(msg);
+ } finally {
+ h.finish();
+ }
+ }
+
+ protected static String updateProperties(String configmap, String
compartmentOcid, String vaultOcid, String datasourceName) {
+ StringWriter output = new StringWriter();
+ String[] lines = configmap.split("\n");
+ int previousIndent = 0;
+ Map<Integer, String> path = new LinkedHashMap<>();
+ String propertiesName = null;
+ Map<String, String> properties = new LinkedHashMap<>();
+ for (int i = 0; i < lines.length; i++) {
+ String line = lines[i];
+ if (line.trim().startsWith("#") || line.isEmpty()) {
+ output.append(line);
+ output.append("\n");
+ continue;
+ }
+ int indent = 0;
+ while (line.charAt(indent) == ' ') {
+ indent++;
+ }
+ if (previousIndent > indent || (propertiesName != null &&
!line.contains("="))) {
+ final int f = indent;
+ path.entrySet().removeIf(entry -> entry.getKey() >= f);
+ if (propertiesName != null) {
+ int propIndent = previousIndent;
+ if (properties.size() == 0) {
+ propIndent = indent + 2;
+ }
+ output.append(
+ formatProperties(propertiesName, properties,
propIndent, compartmentOcid, vaultOcid, datasourceName));
+
+ properties.clear();
+ }
+ propertiesName = null;
+ if (line.trim().equals("---")) { //NOI18N
+ output.append(line);
+ output.append("\n");
+ continue;
+ }
+ }
+ if (propertiesName == null) {
+ if (line.indexOf(':') < 0) {
+ throw new IllegalStateException("Invalid ConfigMap
format"); //NOI18N
+ }
+ String k = line.substring(0, line.indexOf(':')).trim();
+ String v = line.substring(line.indexOf(':') + 1).trim();
+ if (k == null) {
+ throw new IllegalStateException();
+ }
+
+ path.put(indent, k);
+ output.append(line);
+ output.append("\n");
+ if (v.trim().equals("|")) {
+ propertiesName = k;
+ continue;
+ }
+ }
+ if (propertiesName != null && line.contains("=")) {
+ properties.put(line.substring(0, line.indexOf('=')).trim(),
+ line.substring(line.indexOf('=') + 1).trim());
+ }
+
+ previousIndent = indent;
}
- NotifyDescriptor.Message msg = new
NotifyDescriptor.Message(Bundle.SecretsCreated());
- DialogDisplayer.getDefault().notify(msg);
+ output.append(
+ formatProperties(propertiesName, properties, previousIndent,
compartmentOcid, vaultOcid, datasourceName));
+
+ return output.toString();
+ }
+
+ private static String formatProperties(String proprtiesName, Map<String,
String> prop, int indent, String compartmentId, String vaultId, String
datasourceName) {
+ StringBuilder output = new StringBuilder();
+ if (proprtiesName.startsWith("bootstrap")) { // NOI18N
+ prop.entrySet().removeIf(entry -> ((String)
entry.getKey()).startsWith("oci.vault.vaults")); // NOI18N
+ prop.put("oci.config.instance-principal.enabled", "true"); //
NOI18N
+ prop.put("micronaut.config-client.enabled", "true"); // NOI18N
+ prop.put("oci.vault.config.enabled", "true"); // NOI18N
+ prop.put("oci.vault.vaults[0].ocid", vaultId); // NOI18N
+ prop.put("oci.vault.vaults[0].compartment-ocid", compartmentId);
// NOI18N
+ } else if (proprtiesName.startsWith("application")) { // NOI18N
+ prop.put("datasources.default.dialect", "ORACLE"); // NOI18N
+ prop.put("datasources.default.ocid", "${DATASOURCES_" +
datasourceName + "_OCID}"); // NOI18N
+ prop.put("datasources.default.walletPassword", "${DATASOURCES_" +
datasourceName + "_WALLET_PASSWORD}"); // NOI18N
+ prop.put("datasources.default.username", "${DATASOURCES_" +
datasourceName + "_USERNAME}"); // NOI18N
+ prop.put("datasources.default.password", "${DATASOURCES_" +
datasourceName + "_PASSWORD}"); // NOI18N
+ }
+ for (Entry<String, String> entry : prop.entrySet()) {
+ output.append(new String(new char[indent]).replace('\0', ' '));
+ output.append(entry.getKey());
+ output.append("=");
+ output.append(entry.getValue());
+ output.append("\n");
+ }
+ return output.toString();
}
private static <T extends OCIItem> NotifyDescriptor.QuickPick
createQuickPick(Map<String, T> ociItems, String title) {
@@ -722,6 +966,23 @@ public class AddDbConnectionToVault implements
ActionListener {
abstract FlatCompartmentItem getItem(OCID compId);
}
+ protected static Map<String, DevopsProjectItem> getDevopsProjects(String
compartmentId) {
+ try (DevopsClient client = new
DevopsClient(OCIManager.getDefault().getConfigProvider());) {
+ ListProjectsRequest request =
ListProjectsRequest.builder().compartmentId(compartmentId).build();
+ ListProjectsResponse response = client.listProjects(request);
+
+ List<ProjectSummary> projects =
response.getProjectCollection().getItems();
+ for (ProjectSummary project : projects) {
+ project.getNotificationConfig().getTopicId();
+
+ }
+ return projects.stream()
+ .map(p -> new DevopsProjectItem(OCID.of(p.getId(),
"DevopsProject"), // NOI18N
+ p.getName()))
+ .collect(Collectors.toMap(DevopsProjectItem::getName,
Function.identity()));
+ }
+ }
+
protected static Map<String, VaultItem> getVaults(OCIItem parent) {
Map<String, VaultItem> items = new HashMap<>();
try {
@@ -738,15 +999,15 @@ public class AddDbConnectionToVault implements
ActionListener {
Map<String, KeyItem> items = new HashMap<>();
try {
if (parent instanceof VaultItem) {
- KeyNode.getKeys().apply((VaultItem) parent).forEach((db) ->
items.put(db.getName(), db));
+ KeyNode.getKeys().apply((VaultItem) parent).forEach(key ->
items.put(key.getName(), key));
}
} catch (BmcException e) {
- LOG.log(Level.SEVERE, "Unable to load vault list", e); //NOI18N
+ LOG.log(Level.SEVERE, "Unable to load key list", e); //NOI18N
}
return items;
}
- static Pattern p = Pattern.compile("[A-Z]*_([A-Z]*)_[A-Z]*"); //NOI18N
+ static Pattern p = Pattern.compile("[A-Z]*_([a-zA-Z0-9]*)_[A-Z]*");
//NOI18N
protected static String extractDatasourceName(String value) {
Matcher m = p.matcher(value);
diff --git
a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/devops/DevopsProjectService.java
b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/devops/DevopsProjectService.java
new file mode 100644
index 0000000000..77c0cf85e5
--- /dev/null
+++
b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/devops/DevopsProjectService.java
@@ -0,0 +1,90 @@
+/*
+ * 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.devops;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import java.io.FileNotFoundException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.openide.filesystems.FileObject;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+
+/**
+ * Finds OCI DevOps configuration if available.
+ *
+ * @author jhorvath
+ */
+public class DevopsProjectService {
+ private static DevopsConfigFinder finder = null;
+
+
+ public static List<String> getDevopsProjectOcid() {
+ List<FileObject> configs = getDefaultFinder().findDevopsConfig();
+ Gson gson = new Gson();
+ List<String> devopsOcid = new ArrayList<> ();
+ try {
+ for (FileObject config : configs) {
+ JsonObject json = gson.fromJson(new
InputStreamReader(configs.get(0).getInputStream()), JsonObject.class);
+ JsonArray services = json.getAsJsonArray("cloudServices");
//NOI18N
+ for (JsonElement service : services) {
+ JsonElement type = service.getAsJsonObject().get("type");
+ if (type != null && "oci".equals(type.getAsString())) {
//NOI18N
+ JsonObject data =
service.getAsJsonObject().getAsJsonObject("data"); //NOI18N
+ JsonObject context = data.getAsJsonObject("context");
//NOI18N
+
devopsOcid.add(context.get("devopsProject").getAsString()); //NOI18N
+ }
+ }
+ }
+ } catch (FileNotFoundException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ return devopsOcid;
+ }
+
+
+ private static DevopsConfigFinder getDefaultFinder() {
+ if (finder == null) {
+ finder = Lookup.getDefault().lookup(DevopsConfigFinder.class);
+ }
+ if (finder == null) {
+ finder = new DefaultDevopsConfigFinder();
+ }
+ return finder;
+ }
+
+ public interface DevopsConfigFinder {
+ List<FileObject> findDevopsConfig();
+ }
+
+ static class DefaultDevopsConfigFinder implements DevopsConfigFinder {
+
+ @Override
+ public List<FileObject> findDevopsConfig() {
+ return Collections.emptyList();
+ }
+
+ }
+
+}
diff --git
a/enterprise/cloud.oracle/test/unit/src/org/netbeans/modules/cloud/oracle/actions/AddDbConnectionToVaultTest.java
b/enterprise/cloud.oracle/test/unit/src/org/netbeans/modules/cloud/oracle/actions/AddDbConnectionToVaultTest.java
index cc9ccd1db4..9f79e00d2d 100644
---
a/enterprise/cloud.oracle/test/unit/src/org/netbeans/modules/cloud/oracle/actions/AddDbConnectionToVaultTest.java
+++
b/enterprise/cloud.oracle/test/unit/src/org/netbeans/modules/cloud/oracle/actions/AddDbConnectionToVaultTest.java
@@ -18,21 +18,16 @@
*/
package org.netbeans.modules.cloud.oracle.actions;
-import java.awt.event.ActionEvent;
-import java.util.Map;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
-import org.netbeans.modules.cloud.oracle.items.OCIItem;
-import org.netbeans.modules.cloud.oracle.vault.KeyItem;
-import org.netbeans.modules.cloud.oracle.vault.VaultItem;
/**
*
- * @author honza
+ * @author jhorvath
*/
public class AddDbConnectionToVaultTest {
@@ -56,7 +51,7 @@ public class AddDbConnectionToVaultTest {
}
/**
- * Test of actionPerformed method, of class AddDbConnectionToVault.
+ * Test of extractDatasourceName method, of class AddDbConnectionToVault.
*/
@Test
public void datasourceName() {
@@ -64,7 +59,79 @@ public class AddDbConnectionToVaultTest {
String ds = AddDbConnectionToVault.extractDatasourceName(input);
assertEquals("DEFAULT", ds);
+ input = "DATASOURCES_DEF3_USERNAME";
+ ds = AddDbConnectionToVault.extractDatasourceName(input);
+ assertEquals("DEF3", ds);
+ }
+
+ @Test
+ public void testConfigMap() {
+ String cm = "apiVersion: v1\n" +
+ "kind: ConfigMap\n" +
+ "metadata:\n" +
+ " name: demo-adb-vault\n" +
+ "data:\n" +
+ " bootstrap-oraclecloud.properties: |\n" +
+ " oci.config.instance-principal.enabled=false\n" +
+ " micronaut.config-client.enabled=fase\n" +
+ " oci.vault.config.enabled=false\n" +
+ " oci.vault.vaults[0].ocid=xxxx\n" +
+ " oci.vault.vaults[0].compartment-ocid=xxx\n" +
+ " application-oraclecloud.properties: |\n" +
+ " a=b";
+ String expected = "apiVersion: v1\n" +
+ "kind: ConfigMap\n" +
+ "metadata:\n" +
+ " name: demo-adb-vault\n" +
+ "data:\n" +
+ " bootstrap-oraclecloud.properties: |\n" +
+ " oci.config.instance-principal.enabled=true\n" +
+ " micronaut.config-client.enabled=true\n" +
+ " oci.vault.config.enabled=true\n" +
+ " oci.vault.vaults[0].ocid=cde\n" +
+ " oci.vault.vaults[0].compartment-ocid=abc\n" +
+ " application-oraclecloud.properties: |\n" +
+ " a=b\n" +
+ " datasources.default.dialect=ORACLE\n" +
+ " datasources.default.ocid=${DATASOURCES_DEFAULT_OCID}\n" +
+ "
datasources.default.walletPassword=${DATASOURCES_DEFAULT_WALLET_PASSWORD}\n" +
+ "
datasources.default.username=${DATASOURCES_DEFAULT_USERNAME}\n" +
+ "
datasources.default.password=${DATASOURCES_DEFAULT_PASSWORD}\n";
+ String result = AddDbConnectionToVault.updateProperties(cm, "abc",
"cde", "DEFAULT");
+ assertEquals(expected, result);
}
-
+ @Test
+ public void testConfigMap1() {
+ String cm = "apiVersion: v1\n" +
+ "kind: ConfigMap\n" +
+ "metadata:\n" +
+ " name: demo-adb-vault\n" +
+ "data:\n" +
+ " bootstrap-oraclecloud.properties: |\n" +
+ " # placeholder\n" +
+ " application-oraclecloud.properties: |\n" +
+ " a=b";
+ String expected = "apiVersion: v1\n" +
+ "kind: ConfigMap\n" +
+ "metadata:\n" +
+ " name: demo-adb-vault\n" +
+ "data:\n" +
+ " bootstrap-oraclecloud.properties: |\n" +
+ " # placeholder\n" +
+ " oci.config.instance-principal.enabled=true\n" +
+ " micronaut.config-client.enabled=true\n" +
+ " oci.vault.config.enabled=true\n" +
+ " oci.vault.vaults[0].ocid=cde\n" +
+ " oci.vault.vaults[0].compartment-ocid=abc\n" +
+ " application-oraclecloud.properties: |\n" +
+ " a=b\n" +
+ " datasources.default.dialect=ORACLE\n" +
+ " datasources.default.ocid=${DATASOURCES_ABC_OCID}\n" +
+ "
datasources.default.walletPassword=${DATASOURCES_ABC_WALLET_PASSWORD}\n" +
+ " datasources.default.username=${DATASOURCES_ABC_USERNAME}\n" +
+ " datasources.default.password=${DATASOURCES_ABC_PASSWORD}\n";
+ String result = AddDbConnectionToVault.updateProperties(cm, "abc",
"cde", "ABC");
+ assertEquals(expected, result);
+ }
}
diff --git
a/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/LspDevopsConfigFinder.java
b/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/LspDevopsConfigFinder.java
new file mode 100644
index 0000000000..fd2033e55c
--- /dev/null
+++
b/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/LspDevopsConfigFinder.java
@@ -0,0 +1,52 @@
+/*
+ * 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.nbcode.integration;
+
+import java.util.ArrayList;
+import java.util.List;
+import
org.netbeans.modules.cloud.oracle.devops.DevopsProjectService.DevopsConfigFinder;
+import org.netbeans.modules.java.lsp.server.LspServerState;
+import org.openide.filesystems.FileObject;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author jhorvath
+ */
+@ServiceProvider(service = DevopsConfigFinder.class, position = 1000)
+public class LspDevopsConfigFinder implements DevopsConfigFinder {
+
+ @Override
+ public List<FileObject> findDevopsConfig() {
+ List<FileObject> result = new ArrayList<> ();
+ LspServerState serverState =
Lookup.getDefault().lookup(LspServerState.class);
+ if (serverState != null) {
+ List<FileObject> folders = serverState.getClientWorkspaceFolders();
+ for (FileObject folder : folders) {
+ FileObject f = folder.getFileObject(".vscode/devops.json");
//NOI18N
+ if (f != null && f.isValid()) {
+ result.add(f);
+ }
+ }
+ }
+ return result;
+ }
+
+}
diff --git
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/LspServerState.java
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/LspServerState.java
index 2fcd7a3546..593767b674 100644
---
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/LspServerState.java
+++
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/LspServerState.java
@@ -100,4 +100,13 @@ public interface LspServerState {
* @return snapshot of folders.
*/
public List<FileObject> getAcceptedWorkspaceFolders();
+
+ /**
+ * Returns the set of workspace folders reported by the client. If a
folder from the list is recognized
+ * as a project, it will be also present in {@link #openedProjects()}
including all its subprojects.
+ * The list of client workspace folders contains just toplevel items in
client's workspace, as defined in
+ * LSP protocol.
+ * @return list of workspace folders
+ */
+ public List<FileObject> getClientWorkspaceFolders();
}
diff --git
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java
index b427a84993..1674055e8f 100644
---
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java
+++
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java
@@ -922,6 +922,8 @@ public final class Server {
initializeOptions();
+
workspaceService.setClientWorkspaceFolders(init.getWorkspaceFolders());
+
// but complete the InitializationRequest independently of the
project initialization.
return CompletableFuture.completedFuture(
finishInitialization(
@@ -1038,6 +1040,11 @@ public final class Server {
// no op: there's already a lot of noise in the log, and the
console log
// can be controlled by a commandline parameter to the NBLS.
}
+
+ @Override
+ public List<FileObject> getClientWorkspaceFolders() {
+ return workspaceService.getClientWorkspaceFolders();
+ }
}
public static final String NBLS_BUILD_WORKSPACE = "nbls.build.workspace";
diff --git
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java
index e337829640..0394de153e 100644
---
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java
+++
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java
@@ -169,10 +169,42 @@ public final class WorkspaceServiceImpl implements
WorkspaceService, LanguageCli
private final LspServerState server;
private NbCodeLanguageClient client;
+ /**
+ * List of workspace folders as reported by the client. Initialized in
`initialize` request,
+ * and then updated by didChangeWorkspaceFolder notifications.
+ */
+ private volatile List<FileObject> clientWorkspaceFolders =
Collections.emptyList();
+
WorkspaceServiceImpl(LspServerState server) {
this.server = server;
}
+ /**
+ * Returns the set of workspace folders reported by the client. If a
folder from the list is recognized
+ * as a project, it will be also present in {@link #openedProjects()}
including all its subprojects.
+ * The list of client workspace folders contains just toplevel items in
client's workspace, as defined in
+ * LSP protocol.
+ * @return list of workspace folders
+ */
+ public List<FileObject> getClientWorkspaceFolders() {
+ return new ArrayList<>(clientWorkspaceFolders);
+ }
+
+ public void setClientWorkspaceFolders(List<WorkspaceFolder>
clientWorkspaceFolders) {
+ if (clientWorkspaceFolders == null) {
+ return;
+ }
+ List<FileObject> newWorkspaceFolders = new
ArrayList<>(this.clientWorkspaceFolders);
+ try {
+ for (WorkspaceFolder clientWorkspaceFolder :
clientWorkspaceFolders) {
+
newWorkspaceFolders.add(Utils.fromUri(clientWorkspaceFolder.getUri()));
+ }
+ this.clientWorkspaceFolders = newWorkspaceFolders;
+ } catch (MalformedURLException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+
@Override
public CompletableFuture<Object> executeCommand(ExecuteCommandParams
params) {
String command = Utils.decodeCommand(params.getCommand(),
client.getNbCodeCapabilities());
@@ -1368,6 +1400,8 @@ public final class WorkspaceServiceImpl implements
WorkspaceService, LanguageCli
})
@Override
public void didChangeWorkspaceFolders(DidChangeWorkspaceFoldersParams
params) {
+ // the client > server notification stream is sequential
+ List<FileObject> newWorkspaceFolders = new
ArrayList<>(this.clientWorkspaceFolders);
List<FileObject> refreshProjectFolders = new ArrayList<>();
for (WorkspaceFolder wkspFolder : params.getEvent().getAdded()) {
String uri = wkspFolder.getUri();
@@ -1375,12 +1409,35 @@ public final class WorkspaceServiceImpl implements
WorkspaceService, LanguageCli
FileObject f = Utils.fromUri(uri);
if (f != null) {
refreshProjectFolders.add(f);
+ // avoid duplicates
+ if (!newWorkspaceFolders.contains(f)) {
+ LOG.log(Level.FINE, "Adding client workspace folder
{0}", f);
+ newWorkspaceFolders.add(f);
+ }
}
} catch (MalformedURLException ex) {
// expected, perhaps some client-specific URL scheme ?
LOG.fine("Workspace folder URI could not be converted into
fileobject: {0}");
}
}
+
+ if (params.getEvent().getRemoved() != null) {
+ for (WorkspaceFolder wsf : params.getEvent().getRemoved()) {
+ String uri = wsf.getUri();
+ try {
+ FileObject f = Utils.fromUri(uri);
+ if (f != null) {
+ LOG.log(Level.FINE, "Removing client workspace folder
{0}", f);
+ newWorkspaceFolders.remove(f);
+ }
+ } catch (MalformedURLException ex) {
+ // was never added
+ }
+ }
+ }
+ // the client > server notification stream is sequential; no need to
sync
+ this.clientWorkspaceFolders = newWorkspaceFolders;
+
if (!refreshProjectFolders.isEmpty()) {
server.asyncOpenSelectedProjects(refreshProjectFolders,
true).thenAccept((projects) -> {
// report initialization of a project / projects
diff --git a/java/java.lsp.server/vscode/BUILD.md
b/java/java.lsp.server/vscode/BUILD.md
index dcdca1d898..95611cc484 100644
--- a/java/java.lsp.server/vscode/BUILD.md
+++ b/java/java.lsp.server/vscode/BUILD.md
@@ -131,6 +131,11 @@ and specify suitable debug arguments to start _standalone
NBLS_ instance:
vscode$ npm run nbcode -- --jdkhome /jdk
-J-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
```
+To add extra modules while debugging the NetBeans part
+```bash
+vscode$ npm run nbcode --
-J-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8000
-J-Dnetbeans.extra.dirs=/path/to/extension
+```
+
Connect to the process with Java debugger, setup all breakpoints. Then launch
the VS Code extension (which connects to the already running _standalone NBLS_
Java process):
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists