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; + } + } + + } + +}
