This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 393f858de64a3420395e9162c4bab2a38ac6a64d
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Sep 12 15:36:48 2022 +0200

    camel-jbang - Add get vault command
---
 .../apache/camel/console/DevConsoleResolver.java   |  10 ++
 .../impl/engine/DefaultDevConsoleResolver.java     |   8 ++
 .../camel/cli/connector/LocalCliConnector.java     |  19 +++
 .../dsl/jbang/core/commands/CamelJBangMain.java    |   4 +-
 .../dsl/jbang/core/commands/process/ListVault.java | 159 +++++++++++++++++++++
 5 files changed, 199 insertions(+), 1 deletion(-)

diff --git 
a/core/camel-api/src/main/java/org/apache/camel/console/DevConsoleResolver.java 
b/core/camel-api/src/main/java/org/apache/camel/console/DevConsoleResolver.java
index bae9ca062b0..5d0ea960e8d 100644
--- 
a/core/camel-api/src/main/java/org/apache/camel/console/DevConsoleResolver.java
+++ 
b/core/camel-api/src/main/java/org/apache/camel/console/DevConsoleResolver.java
@@ -18,6 +18,8 @@ package org.apache.camel.console;
 
 import org.apache.camel.CamelContextAware;
 
+import java.util.Optional;
+
 /**
  * A pluggable strategy for resolving dev consoles in a loosely coupled manner
  */
@@ -31,4 +33,12 @@ public interface DevConsoleResolver extends 
CamelContextAware {
      */
     DevConsole resolveDevConsole(String id);
 
+    /**
+     * Lookup existing resolved {@link DevConsole}.
+     *
+     * @param  id the id of the {@link DevConsole}
+     * @return    the existing {@link DevConsole}, or <tt>null</tt> if not yet 
resolved or not found
+     */
+    Optional<DevConsole> lookupDevConsole(String id);
+
 }
diff --git 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultDevConsoleResolver.java
 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultDevConsoleResolver.java
index 16f15bd8be6..4823009f3db 100644
--- 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultDevConsoleResolver.java
+++ 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultDevConsoleResolver.java
@@ -21,9 +21,12 @@ import org.apache.camel.CamelContextAware;
 import org.apache.camel.ExtendedCamelContext;
 import org.apache.camel.NoFactoryAvailableException;
 import org.apache.camel.console.DevConsole;
+import org.apache.camel.console.DevConsoleRegistry;
 import org.apache.camel.console.DevConsoleResolver;
 import org.apache.camel.spi.FactoryFinder;
 
+import java.util.Optional;
+
 /**
  * Default dev console resolver that looks for dev consoles factories in
  * <b>META-INF/services/org/apache/camel/dev-console/</b>.
@@ -86,4 +89,9 @@ public class DefaultDevConsoleResolver implements 
DevConsoleResolver, CamelConte
         return devConsoleFactory.findOptionalClass(name).orElse(null);
     }
 
+    @Override
+    public Optional<DevConsole> lookupDevConsole(String id) {
+        DevConsoleRegistry dcr = 
camelContext.getExtension(DevConsoleRegistry.class);
+        return dcr.getConsole(id);
+    }
 }
diff --git 
a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
 
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
index d41694fe704..b40b9b3c8e0 100644
--- 
a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
+++ 
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
@@ -27,6 +27,7 @@ import java.lang.management.ThreadMXBean;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Optional;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
@@ -343,6 +344,10 @@ public class LocalCliConnector extends ServiceSupport 
implements CliConnector, C
             if (gc != null) {
                 root.put("gc", gc);
             }
+            JsonObject vaults = collectVaults();
+            if (!vaults.isEmpty()) {
+                root.put("vaults", vaults);
+            }
             LOG.trace("Updating status file: {}", statusFile);
             IOHelper.writeText(root.toJson(), statusFile);
         } catch (Throwable e) {
@@ -407,6 +412,20 @@ public class LocalCliConnector extends ServiceSupport 
implements CliConnector, C
         return null;
     }
 
+    private JsonObject collectVaults() {
+        JsonObject root = new JsonObject();
+        // aws-secrets is optional
+        Optional<DevConsole> dc = 
camelContext.adapt(ExtendedCamelContext.class)
+                .getDevConsoleResolver().lookupDevConsole("aws-secrets");
+        if (dc.isPresent()) {
+            JsonObject json = (JsonObject) 
dc.get().call(DevConsole.MediaType.JSON);
+            if (json != null) {
+                root.put("aws-secrets", json);
+            }
+        }
+        return root;
+    }
+
     @Override
     protected void doStop() throws Exception {
         // cleanup
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
index 042df15d887..f713df4d0f7 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
@@ -36,6 +36,7 @@ import 
org.apache.camel.dsl.jbang.core.commands.process.CamelTop;
 import org.apache.camel.dsl.jbang.core.commands.process.Hawtio;
 import org.apache.camel.dsl.jbang.core.commands.process.Jolokia;
 import org.apache.camel.dsl.jbang.core.commands.process.ListProcess;
+import org.apache.camel.dsl.jbang.core.commands.process.ListVault;
 import org.apache.camel.dsl.jbang.core.commands.process.StopProcess;
 import picocli.CommandLine;
 import picocli.CommandLine.Command;
@@ -53,7 +54,8 @@ public class CamelJBangMain implements Callable<Integer> {
                 .addSubcommand("stop", new CommandLine(new StopProcess(main)))
                 .addSubcommand("get", new CommandLine(new CamelStatus(main))
                         .addSubcommand("context", new CommandLine(new 
CamelContextStatus(main)))
-                        .addSubcommand("route", new CommandLine(new 
CamelRouteStatus(main))))
+                        .addSubcommand("route", new CommandLine(new 
CamelRouteStatus(main)))
+                        .addSubcommand("vault", new CommandLine(new 
ListVault(main))))
                 .addSubcommand("top", new CommandLine(new CamelTop(main))
                         .addSubcommand("context", new CommandLine(new 
CamelContextTop(main)))
                         .addSubcommand("route", new CommandLine(new 
CamelRouteTop(main))))
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListVault.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListVault.java
new file mode 100644
index 00000000000..a56b5eee4ab
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListVault.java
@@ -0,0 +1,159 @@
+/*
+ * 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.apache.camel.dsl.jbang.core.commands.process;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import com.github.freva.asciitable.AsciiTable;
+import com.github.freva.asciitable.Column;
+import com.github.freva.asciitable.HorizontalAlign;
+import com.github.freva.asciitable.OverflowBehaviour;
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.util.TimeUtils;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+
+@Command(name = "vault", aliases = { "vault", "vaults" },
+         description = "List secrets from security vaults (AWS) used by 
running Camel integrations")
+public class ListVault extends ProcessBaseCommand {
+
+    @CommandLine.Option(names = { "--sort" },
+                        description = "Sort by pid, name", defaultValue = 
"pid")
+    String sort;
+
+    public ListVault(CamelJBangMain main) {
+        super(main);
+    }
+
+    @Override
+    public Integer call() throws Exception {
+        List<Row> rows = new ArrayList<>();
+
+        List<Long> pids = findPids("*");
+        ProcessHandle.allProcesses()
+                .filter(ph -> pids.contains(ph.pid()))
+                .forEach(ph -> {
+                    JsonObject root = loadStatus(ph.pid());
+                    if (root != null) {
+                        Row row = new Row();
+                        row.pid = "" + ph.pid();
+                        JsonObject context = (JsonObject) root.get("context");
+                        row.name = context.getString("name");
+                        if ("CamelJBang".equals(row.name)) {
+                            row.name = extractName(root, ph);
+                        }
+                        JsonObject vaults = (JsonObject) root.get("vaults");
+                        if (vaults != null) {
+                            JsonObject aws = (JsonObject) 
vaults.get("aws-secrets");
+                            if (aws != null) {
+                                row.vault = "AWS";
+                                row.region = aws.getString("region");
+                                row.lastCheck = 
aws.getLongOrDefault("lastCheckTimestamp", 0);
+                                row.lastReload = 
aws.getLongOrDefault("lastReloadTimestamp", 0);
+                                JsonArray arr = (JsonArray) aws.get("secrets");
+                                for (int i = 0; i < arr.size(); i++) {
+                                    if (i > 0) {
+                                        // create a copy for 2+ secrets
+                                        row = row.copy();
+                                    }
+                                    JsonObject jo = (JsonObject) arr.get(i);
+                                    row.secret = jo.getString("name");
+                                    row.timestamp = 
jo.getLongOrDefault("timestamp", 0);
+                                    rows.add(row);
+                                }
+                            }
+                        }
+                    }
+                });
+
+        // sort rows
+        rows.sort(this::sortRow);
+
+        if (!rows.isEmpty()) {
+            System.out.println(AsciiTable.getTable(AsciiTable.NO_BORDERS, 
rows, Arrays.asList(
+                    new 
Column().header("PID").headerAlign(HorizontalAlign.CENTER).with(r -> r.pid),
+                    new 
Column().header("NAME").dataAlign(HorizontalAlign.LEFT).maxWidth(40, 
OverflowBehaviour.ELLIPSIS_RIGHT)
+                            .with(r -> r.name),
+                    new 
Column().header("VAULT").dataAlign(HorizontalAlign.LEFT).with(r -> r.vault),
+                    new 
Column().header("REGION").dataAlign(HorizontalAlign.LEFT).with(r -> r.region),
+                    new 
Column().header("CHECK").headerAlign(HorizontalAlign.LEFT).with(this::getCheckAgo),
+                    new 
Column().header("UPDATE").headerAlign(HorizontalAlign.LEFT).with(this::getReloadAgo),
+                    new 
Column().header("SECRET").dataAlign(HorizontalAlign.LEFT).maxWidth(40, 
OverflowBehaviour.ELLIPSIS_RIGHT)
+                            .with(r -> r.secret),
+                    new 
Column().header("AGE").headerAlign(HorizontalAlign.CENTER).with(this::getAgo))));
+        }
+
+        return 0;
+    }
+
+    protected int sortRow(Row o1, Row o2) {
+        switch (sort) {
+            case "pid":
+                return Long.compare(Long.parseLong(o1.pid), 
Long.parseLong(o2.pid));
+            case "name":
+                return o1.name.compareToIgnoreCase(o2.name);
+            default:
+                return 0;
+        }
+    }
+
+    private String getCheckAgo(Row r) {
+        if (r.lastCheck == 0) {
+            return "";
+        }
+        return TimeUtils.printSince(r.lastCheck);
+    }
+
+    private String getReloadAgo(Row r) {
+        if (r.lastReload == 0) {
+            return "";
+        }
+        return TimeUtils.printSince(r.lastReload);
+    }
+
+    private String getAgo(Row r) {
+        if (r.timestamp == 0) {
+            return "";
+        }
+        return TimeUtils.printSince(r.timestamp);
+    }
+
+    private static class Row implements Cloneable {
+        String pid;
+        String name;
+        String vault;
+        String region;
+        long lastCheck;
+        long lastReload;
+        String secret;
+        long timestamp;
+
+        Row copy() {
+            try {
+                return (Row) clone();
+            } catch (CloneNotSupportedException e) {
+                return null;
+            }
+        }
+
+    }
+
+}

Reply via email to