You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ac...@apache.org on 2022/09/22 09:30:28 UTC

[camel] branch main updated (a85fc5ef228 -> 7e9693b7502)

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

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


    from a85fc5ef228 camel-jbang - Add get endpoint command
     new 167d1895ae0 camel-azure-key-vault - Add dev console for secrets
     new c461cef4010 camel-azure-key-vault - Add dev console for secrets
     new ff0906ff2a8 camel-azure-key-vault - Add dev console for secrets
     new 7e9693b7502 camel-azure-key-vault - Add dev console for secrets

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../camel-azure/camel-azure-key-vault/pom.xml      |   4 +
 .../org/apache/camel/dev-console/azure-secrets     |   2 +
 .../key/vault/AzureKeyVaultManagerDevConsole.java} |  61 ++--
 .../key/vault/EventhubsReloadTriggerTask.java      | 376 +++++++++++----------
 .../camel/cli/connector/LocalCliConnector.java     |   9 +
 .../dsl/jbang/core/commands/process/ListVault.java |  19 +-
 6 files changed, 256 insertions(+), 215 deletions(-)
 create mode 100644 components/camel-azure/camel-azure-key-vault/src/generated/resources/META-INF/services/org/apache/camel/dev-console/azure-secrets
 copy components/{camel-google/camel-google-secret-manager/src/main/java/org/apache/camel/component/google/secret/manager/GoogleSecretManagerDevConsole.java => camel-azure/camel-azure-key-vault/src/main/java/org/apache/camel/component/azure/key/vault/AzureKeyVaultManagerDevConsole.java} (71%)


[camel] 04/04: camel-azure-key-vault - Add dev console for secrets

Posted by ac...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 7e9693b750201bbb4ee865d02588b06628ceec07
Author: Andrea Cosentino <an...@gmail.com>
AuthorDate: Thu Sep 22 11:28:01 2022 +0200

    camel-azure-key-vault - Add dev console for secrets
---
 .../org/apache/camel/dev-console/azure-secrets        |  2 ++
 .../dsl/jbang/core/commands/process/ListVault.java    | 19 ++++++++++++++++++-
 2 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/components/camel-azure/camel-azure-key-vault/src/generated/resources/META-INF/services/org/apache/camel/dev-console/azure-secrets b/components/camel-azure/camel-azure-key-vault/src/generated/resources/META-INF/services/org/apache/camel/dev-console/azure-secrets
new file mode 100644
index 00000000000..451c64fd185
--- /dev/null
+++ b/components/camel-azure/camel-azure-key-vault/src/generated/resources/META-INF/services/org/apache/camel/dev-console/azure-secrets
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.azure.key.vault.AzureKeyVaultManagerDevConsole
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
index aee406651f5..64dd9b8661d 100644
--- 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
@@ -32,7 +32,7 @@ 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")
+         description = "List secrets from security vaults (AWS,GCP and Azure) used by running Camel integrations")
 public class ListVault extends ProcessBaseCommand {
 
     @CommandLine.Option(names = { "--sort" },
@@ -98,6 +98,23 @@ public class ListVault extends ProcessBaseCommand {
                                 }
                             }
                         }
+                        JsonObject azure = (JsonObject) vaults.get("azure-secrets");
+                        if (azure != null) {
+                            row.vault = "Azure";
+                            row.lastCheck = azure.getLongOrDefault("lastCheckTimestamp", 0);
+                            row.lastReload = azure.getLongOrDefault("lastReloadTimestamp", 0);
+                            JsonArray arr = (JsonArray) azure.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);
+                            }
+                        }
                     }
                 });
 


[camel] 03/04: camel-azure-key-vault - Add dev console for secrets

Posted by ac...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit ff0906ff2a8e33eed894fc5148900ed30861f5f6
Author: Andrea Cosentino <an...@gmail.com>
AuthorDate: Thu Sep 22 10:15:50 2022 +0200

    camel-azure-key-vault - Add dev console for secrets
---
 .../java/org/apache/camel/cli/connector/LocalCliConnector.java   | 9 +++++++++
 1 file changed, 9 insertions(+)

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 627fdc81a44..787aa03d560 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
@@ -447,6 +447,15 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C
                 root.put("gcp-secrets", json);
             }
         }
+        // azure-secrets is optional
+        Optional<DevConsole> dcAzure = camelContext.adapt(ExtendedCamelContext.class)
+                .getDevConsoleResolver().lookupDevConsole("azure-secrets");
+        if (dcAzure.isPresent()) {
+            JsonObject json = (JsonObject) dcAzure.get().call(DevConsole.MediaType.JSON);
+            if (json != null) {
+                root.put("azure-secrets", json);
+            }
+        }
         return root;
     }
 


[camel] 01/04: camel-azure-key-vault - Add dev console for secrets

Posted by ac...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 167d1895ae0fdefa282b79b7e69f85c26704172f
Author: Andrea Cosentino <an...@gmail.com>
AuthorDate: Thu Sep 22 10:13:54 2022 +0200

    camel-azure-key-vault - Add dev console for secrets
---
 .../camel-azure/camel-azure-key-vault/pom.xml      |   4 +
 .../key/vault/EventhubsReloadTriggerTask.java      | 376 +++++++++++----------
 2 files changed, 201 insertions(+), 179 deletions(-)

diff --git a/components/camel-azure/camel-azure-key-vault/pom.xml b/components/camel-azure/camel-azure-key-vault/pom.xml
index 5f02311ad27..cb5d0aa85ad 100644
--- a/components/camel-azure/camel-azure-key-vault/pom.xml
+++ b/components/camel-azure/camel-azure-key-vault/pom.xml
@@ -40,6 +40,10 @@
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-support</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-console</artifactId>
+        </dependency>
 
         <!-- azure sdk -->
         <dependency>
diff --git a/components/camel-azure/camel-azure-key-vault/src/main/java/org/apache/camel/component/azure/key/vault/EventhubsReloadTriggerTask.java b/components/camel-azure/camel-azure-key-vault/src/main/java/org/apache/camel/component/azure/key/vault/EventhubsReloadTriggerTask.java
index 0c8949ba805..fee5f1170e7 100644
--- a/components/camel-azure/camel-azure-key-vault/src/main/java/org/apache/camel/component/azure/key/vault/EventhubsReloadTriggerTask.java
+++ b/components/camel-azure/camel-azure-key-vault/src/main/java/org/apache/camel/component/azure/key/vault/EventhubsReloadTriggerTask.java
@@ -52,187 +52,205 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * Period task which checks if Azure Key Vaults secrets has been updated and can trigger Camel to be reloaded.
+ * Period task which checks if Azure Key Vaults secrets has been updated and can
+ * trigger Camel to be reloaded.
  */
 @PeriodicTask("azure-secret-refresh")
 public class EventhubsReloadTriggerTask extends ServiceSupport implements CamelContextAware, Runnable {
 
-    private static final Logger LOG = LoggerFactory.getLogger(EventhubsReloadTriggerTask.class);
-
-    private static final String BLOB_SERVICE_URI_SEGMENT = ".blob.core.windows.net";
-    private static final String SECRET_VERSION_ADD = "Microsoft.KeyVault.SecretNewVersionCreated";
-
-    private CamelContext camelContext;
-    private boolean reloadEnabled = true;
-    private String secrets;
-    private EventProcessorClient eventProcessorClient;
-    private KeyVaultPropertiesFunction propertiesFunction;
-    private final Map<String, Instant> updates = new HashMap<>();
-
-    public EventhubsReloadTriggerTask() {
-    }
-
-    @Override
-    public CamelContext getCamelContext() {
-        return camelContext;
-    }
-
-    @Override
-    public void setCamelContext(CamelContext camelContext) {
-        this.camelContext = camelContext;
-    }
-
-    public boolean isReloadEnabled() {
-        return reloadEnabled;
-    }
-
-    /**
-     * Whether Camel should be reloaded on Azure secret updated
-     */
-    public void setReloadEnabled(boolean reloadEnabled) {
-        this.reloadEnabled = reloadEnabled;
-    }
-
-    /**
-     * A map of the updated secrets with the latest updated time.
-     */
-    public Map<String, Instant> getUpdates() {
-        return Collections.unmodifiableMap(updates);
-    }
-
-    @Override
-    protected void doStart() throws Exception {
-        super.doStart();
-
-        // auto-detect secrets in-use
-        PropertiesComponent pc = camelContext.getPropertiesComponent();
-        PropertiesFunction pf = pc.getPropertiesFunction("azure");
-        if (pf instanceof KeyVaultPropertiesFunction) {
-            propertiesFunction = (KeyVaultPropertiesFunction) pf;
-            LOG.debug("Auto-detecting secrets from properties-function: {}", pf.getName());
-        }
-        // specific secrets
-        secrets = camelContext.getVaultConfiguration().azure().getSecrets();
-        if (ObjectHelper.isEmpty(secrets) && propertiesFunction == null) {
-            throw new IllegalArgumentException("Secrets must be configured on Azure vault configuration");
-        }
-
-        String eventhubConnectionString = null;
-        String blobAccessKey = null;
-        String blobAccountName = null;
-        String blobContainerName = null;
-        AzureVaultConfiguration azureVaultConfiguration = getCamelContext().getVaultConfiguration().azure();
-        if (ObjectHelper.isNotEmpty(azureVaultConfiguration)) {
-            eventhubConnectionString = azureVaultConfiguration.getEventhubConnectionString();
-            blobAccessKey = azureVaultConfiguration.getBlobAccessKey();
-            blobAccountName = azureVaultConfiguration.getBlobAccountName();
-            blobContainerName = azureVaultConfiguration.getBlobContainerName();
-        }
-        if (ObjectHelper.isNotEmpty(eventhubConnectionString) && ObjectHelper.isNotEmpty(blobAccessKey)
-                && ObjectHelper.isNotEmpty(blobAccountName) && ObjectHelper.isNotEmpty(blobContainerName)) {
-            BlobContainerAsyncClient c = new BlobContainerClientBuilder()
-                    .endpoint(String.format(Locale.ROOT, "https://%s" + BLOB_SERVICE_URI_SEGMENT, blobAccountName))
-                    .containerName(blobContainerName)
-                    .credential(new StorageSharedKeyCredential(blobAccountName, blobAccessKey))
-                    .buildAsyncClient();
-
-            EventProcessorClientBuilder eventProcessorClientBuilder = new EventProcessorClientBuilder()
-                    .checkpointStore(new BlobCheckpointStore(c))
-                    .consumerGroup("$Default")
-                    .connectionString(eventhubConnectionString)
-                    .processEvent(this::onEventListener)
-                    .processError(this::onErrorListener)
-                    .transportType(AmqpTransportType.AMQP);
-
-            eventProcessorClient = eventProcessorClientBuilder.buildEventProcessorClient();
-            eventProcessorClient.start();
-        } else {
-            throw new RuntimeCamelException(
-                    "Using the Azure Key Vault Secret refresh task requires setting eventhubs connection String, Blob Account Name, Blob Access Key and Blob Container Name  as application properties ");
-        }
-    }
-
-    @Override
-    protected void doShutdown() throws Exception {
-        super.doShutdown();
-
-        if (eventProcessorClient != null) {
-            try {
-                eventProcessorClient.stop();
-            } catch (Exception e) {
-                // ignore
-            }
-            eventProcessorClient = null;
-        }
-
-        updates.clear();
-    }
-
-    @Override
-    public void run() {
-    }
-
-    protected boolean matchSecret(String name) {
-        Set<String> set = new HashSet<>();
-        if (secrets != null) {
-            Collections.addAll(set, secrets.split(","));
-        }
-        if (propertiesFunction != null) {
-            set.addAll(propertiesFunction.getSecrets());
-        }
-
-        for (String part : set) {
-            boolean result = name.contains(part) || PatternHelper.matchPattern(name, part);
-            LOG.trace("Matching secret id: {}={} -> {}", name, part, result);
-            if (result) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    @Override
-    public String toString() {
-        return "Azure Secrets Refresh Task";
-    }
-
-    protected void onEventListener(final EventContext eventContext) {
-        boolean triggerReloading = false;
-
-        ObjectMapper mapper = new ObjectMapper();
-        JsonNode actualObj = null;
-        try {
-            actualObj = mapper.readTree(eventContext.getEventData().getBodyAsString());
-        } catch (JsonMappingException e) {
-            e.printStackTrace();
-        } catch (JsonProcessingException e) {
-            e.printStackTrace();
-        }
-        for (int i = 0; i < actualObj.size(); i++) {
-            String secret = actualObj.get(i).get("subject").textValue();
-            String eventType = actualObj.get(i).get("eventType").textValue();
-            if (ObjectHelper.isNotEmpty(secret) && ObjectHelper.isNotEmpty(eventType)) {
-                if (eventType.equalsIgnoreCase(SECRET_VERSION_ADD)) {
-                    if (matchSecret(secret)) {
-                        if (isReloadEnabled()) {
-                            LOG.info("Update for Azure secret: {} detected, triggering CamelContext reload", secret);
-                            triggerReloading = true;
-                        }
-                    }
-                }
-            }
-        }
-        if (triggerReloading) {
-            ContextReloadStrategy reload = camelContext.hasService(ContextReloadStrategy.class);
-            if (reload != null) {
-                // trigger reload
-                reload.onReload(this);
-            }
-        }
-    }
-
-    public void onErrorListener(final ErrorContext errorContext) {
-        //NOOP
-    }
+	private static final Logger LOG = LoggerFactory.getLogger(EventhubsReloadTriggerTask.class);
+
+	private static final String BLOB_SERVICE_URI_SEGMENT = ".blob.core.windows.net";
+	private static final String SECRET_VERSION_ADD = "Microsoft.KeyVault.SecretNewVersionCreated";
+
+	private CamelContext camelContext;
+	private boolean reloadEnabled = true;
+	private String secrets;
+	private EventProcessorClient eventProcessorClient;
+	private KeyVaultPropertiesFunction propertiesFunction;
+	private volatile Instant lastCheckTime;
+	private volatile Instant lastReloadTime;
+	private final Map<String, Instant> updates = new HashMap<>();
+
+	public EventhubsReloadTriggerTask() {
+	}
+
+	@Override
+	public CamelContext getCamelContext() {
+		return camelContext;
+	}
+
+	@Override
+	public void setCamelContext(CamelContext camelContext) {
+		this.camelContext = camelContext;
+	}
+
+	public boolean isReloadEnabled() {
+		return reloadEnabled;
+	}
+
+	/**
+	 * Whether Camel should be reloaded on Azure secret updated
+	 */
+	public void setReloadEnabled(boolean reloadEnabled) {
+		this.reloadEnabled = reloadEnabled;
+	}
+
+	/**
+	 * A map of the updated secrets with the latest updated time.
+	 */
+	public Map<String, Instant> getUpdates() {
+		return Collections.unmodifiableMap(updates);
+	}
+
+	/**
+	 * Last time this task checked GCP for updated secrets.
+	 */
+	public Instant getLastCheckTime() {
+		return lastCheckTime;
+	}
+
+	/**
+	 * Last time GCP secrets update triggered reload.
+	 */
+	public Instant getLastReloadTime() {
+		return lastReloadTime;
+	}
+
+	@Override
+	protected void doStart() throws Exception {
+		super.doStart();
+
+		// auto-detect secrets in-use
+		PropertiesComponent pc = camelContext.getPropertiesComponent();
+		PropertiesFunction pf = pc.getPropertiesFunction("azure");
+		if (pf instanceof KeyVaultPropertiesFunction) {
+			propertiesFunction = (KeyVaultPropertiesFunction) pf;
+			LOG.debug("Auto-detecting secrets from properties-function: {}", pf.getName());
+		}
+		// specific secrets
+		secrets = camelContext.getVaultConfiguration().azure().getSecrets();
+		if (ObjectHelper.isEmpty(secrets) && propertiesFunction == null) {
+			throw new IllegalArgumentException("Secrets must be configured on Azure vault configuration");
+		}
+
+		String eventhubConnectionString = null;
+		String blobAccessKey = null;
+		String blobAccountName = null;
+		String blobContainerName = null;
+		AzureVaultConfiguration azureVaultConfiguration = getCamelContext().getVaultConfiguration().azure();
+		if (ObjectHelper.isNotEmpty(azureVaultConfiguration)) {
+			eventhubConnectionString = azureVaultConfiguration.getEventhubConnectionString();
+			blobAccessKey = azureVaultConfiguration.getBlobAccessKey();
+			blobAccountName = azureVaultConfiguration.getBlobAccountName();
+			blobContainerName = azureVaultConfiguration.getBlobContainerName();
+		}
+		if (ObjectHelper.isNotEmpty(eventhubConnectionString) && ObjectHelper.isNotEmpty(blobAccessKey)
+				&& ObjectHelper.isNotEmpty(blobAccountName) && ObjectHelper.isNotEmpty(blobContainerName)) {
+			BlobContainerAsyncClient c = new BlobContainerClientBuilder()
+					.endpoint(String.format(Locale.ROOT, "https://%s" + BLOB_SERVICE_URI_SEGMENT, blobAccountName))
+					.containerName(blobContainerName)
+					.credential(new StorageSharedKeyCredential(blobAccountName, blobAccessKey)).buildAsyncClient();
+
+			EventProcessorClientBuilder eventProcessorClientBuilder = new EventProcessorClientBuilder()
+					.checkpointStore(new BlobCheckpointStore(c)).consumerGroup("$Default")
+					.connectionString(eventhubConnectionString).processEvent(this::onEventListener)
+					.processError(this::onErrorListener).transportType(AmqpTransportType.AMQP);
+
+			eventProcessorClient = eventProcessorClientBuilder.buildEventProcessorClient();
+			eventProcessorClient.start();
+		} else {
+			throw new RuntimeCamelException(
+					"Using the Azure Key Vault Secret refresh task requires setting eventhubs connection String, Blob Account Name, Blob Access Key and Blob Container Name  as application properties ");
+		}
+	}
+
+	@Override
+	protected void doShutdown() throws Exception {
+		super.doShutdown();
+
+		if (eventProcessorClient != null) {
+			try {
+				eventProcessorClient.stop();
+			} catch (Exception e) {
+				// ignore
+			}
+			eventProcessorClient = null;
+		}
+
+		updates.clear();
+	}
+
+	@Override
+	public void run() {
+		lastCheckTime = Instant.now();
+	}
+
+	protected boolean matchSecret(String name) {
+		Set<String> set = new HashSet<>();
+		if (secrets != null) {
+			Collections.addAll(set, secrets.split(","));
+		}
+		if (propertiesFunction != null) {
+			set.addAll(propertiesFunction.getSecrets());
+		}
+
+		for (String part : set) {
+			boolean result = name.contains(part) || PatternHelper.matchPattern(name, part);
+			LOG.trace("Matching secret id: {}={} -> {}", name, part, result);
+			if (result) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	@Override
+	public String toString() {
+		return "Azure Secrets Refresh Task";
+	}
+
+	protected void onEventListener(final EventContext eventContext) {
+		boolean triggerReloading = false;
+
+		ObjectMapper mapper = new ObjectMapper();
+		JsonNode actualObj = null;
+		try {
+			actualObj = mapper.readTree(eventContext.getEventData().getBodyAsString());
+		} catch (JsonMappingException e) {
+			e.printStackTrace();
+		} catch (JsonProcessingException e) {
+			e.printStackTrace();
+		}
+		for (int i = 0; i < actualObj.size(); i++) {
+			String secret = actualObj.get(i).get("subject").textValue();
+			String eventType = actualObj.get(i).get("eventType").textValue();
+			if (ObjectHelper.isNotEmpty(secret) && ObjectHelper.isNotEmpty(eventType)) {
+				if (eventType.equalsIgnoreCase(SECRET_VERSION_ADD)) {
+					if (matchSecret(secret)) {
+						if (ObjectHelper.isNotEmpty(eventContext.getEventData().getEnqueuedTime())) {
+							updates.put(secret, eventContext.getEventData().getEnqueuedTime());
+						}
+						if (isReloadEnabled()) {
+							LOG.info("Update for Azure secret: {} detected, triggering CamelContext reload", secret);
+							triggerReloading = true;
+						}
+					}
+				}
+			}
+		}
+		if (triggerReloading) {
+			ContextReloadStrategy reload = camelContext.hasService(ContextReloadStrategy.class);
+			if (reload != null) {
+				// trigger reload
+				lastReloadTime = Instant.now();
+				reload.onReload(this);
+			}
+		}
+	}
+
+	public void onErrorListener(final ErrorContext errorContext) {
+		// NOOP
+	}
 }


[camel] 02/04: camel-azure-key-vault - Add dev console for secrets

Posted by ac...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit c461cef40109fb5a77ced1ff512caa26995e0d2e
Author: Andrea Cosentino <an...@gmail.com>
AuthorDate: Thu Sep 22 10:14:15 2022 +0200

    camel-azure-key-vault - Add dev console for secrets
---
 .../key/vault/AzureKeyVaultManagerDevConsole.java  | 145 +++++++++++++++++++++
 1 file changed, 145 insertions(+)

diff --git a/components/camel-azure/camel-azure-key-vault/src/main/java/org/apache/camel/component/azure/key/vault/AzureKeyVaultManagerDevConsole.java b/components/camel-azure/camel-azure-key-vault/src/main/java/org/apache/camel/component/azure/key/vault/AzureKeyVaultManagerDevConsole.java
new file mode 100644
index 00000000000..b82aaa1cbbd
--- /dev/null
+++ b/components/camel-azure/camel-azure-key-vault/src/main/java/org/apache/camel/component/azure/key/vault/AzureKeyVaultManagerDevConsole.java
@@ -0,0 +1,145 @@
+/*
+ * 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.component.azure.key.vault;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.impl.console.AbstractDevConsole;
+import org.apache.camel.spi.PeriodTaskScheduler;
+import org.apache.camel.spi.PropertiesFunction;
+import org.apache.camel.spi.annotations.DevConsole;
+import org.apache.camel.util.TimeUtils;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.vault.AzureVaultConfiguration;
+
+@DevConsole("azure-secrets")
+public class AzureKeyVaultManagerDevConsole extends AbstractDevConsole {
+
+    private KeyVaultPropertiesFunction propertiesFunction;
+    private EventhubsReloadTriggerTask secretsRefreshTask;
+
+    public AzureKeyVaultManagerDevConsole() {
+        super("camel", "azure-secrets", "Azure Key Vault Secrets", "Azure Key Vault Secret Manager");
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        super.doStart();
+
+        if (getCamelContext().getPropertiesComponent().hasPropertiesFunction("azure")) {
+            PropertiesFunction pf = getCamelContext().getPropertiesComponent().getPropertiesFunction("azure");
+            if (pf instanceof KeyVaultPropertiesFunction) {
+                propertiesFunction = (KeyVaultPropertiesFunction) pf;
+            }
+        }
+         AzureVaultConfiguration azure = getCamelContext().getVaultConfiguration().getAzureVaultConfiguration();
+        if (azure != null && azure.isRefreshEnabled()) {
+            PeriodTaskScheduler scheduler = getCamelContext().adapt(ExtendedCamelContext.class).getPeriodTaskScheduler();
+            secretsRefreshTask = scheduler.getTaskByType(EventhubsReloadTriggerTask.class);
+        }
+    }
+
+    @Override
+    protected String doCallText(Map<String, Object> options) {
+        StringBuilder sb = new StringBuilder();
+
+        if (propertiesFunction != null) {
+            sb.append("Azure Key Vault Secret Manager:");
+            sb.append("\n    Login: Client Id/Client Secret");
+            AzureVaultConfiguration azure = getCamelContext().getVaultConfiguration().getAzureVaultConfiguration();
+            if (azure != null) {
+                sb.append(String.format("\n    Refresh Enabled: %s", azure.isRefreshEnabled()));
+                sb.append(String.format("\n    Refresh Period: %s", azure.getRefreshPeriod()));
+            }
+            if (secretsRefreshTask != null) {
+                Instant last = secretsRefreshTask.getLastCheckTime();
+                String s = last != null ? TimeUtils.printSince(last.toEpochMilli()) : "none";
+                sb.append(String.format("\n    Last Check: %s", s));
+                last = secretsRefreshTask.getLastReloadTime();
+                s = last != null ? TimeUtils.printSince(last.toEpochMilli()) : "none";
+                sb.append(String.format("\n    Last Reload: %s", s));
+            }
+            sb.append("\n\nSecrets in use:");
+
+            List<String> sorted = new ArrayList<>(propertiesFunction.getSecrets());
+            Collections.sort(sorted);
+
+            for (String sec : sorted) {
+                Instant last = secretsRefreshTask != null ? secretsRefreshTask.getUpdates().get(sec) : null;
+                String age = last != null ? TimeUtils.printSince(last.toEpochMilli()) : null;
+                if (age != null) {
+                    sb.append(String.format("\n    %s (age: %s)", sec, age));
+                } else {
+                    sb.append(String.format("\n    %s", sec));
+                }
+            }
+        }
+
+        return sb.toString();
+    }
+
+    @Override
+    protected JsonObject doCallJson(Map<String, Object> options) {
+        JsonObject root = new JsonObject();
+        if (propertiesFunction != null) {
+            root.put("login", "Client Id/Client Secret");
+            AzureVaultConfiguration azure = getCamelContext().getVaultConfiguration().getAzureVaultConfiguration();
+            if (azure != null) {
+                root.put("refreshEnabled", azure.isRefreshEnabled());
+                root.put("refreshPeriod", azure.getRefreshPeriod());
+            }
+            if (secretsRefreshTask != null) {
+                Instant last = secretsRefreshTask.getLastCheckTime();
+                if (last != null) {
+                    long timestamp = last.toEpochMilli();
+                    root.put("lastCheckTimestamp", timestamp);
+                    root.put("lastCheckAge", TimeUtils.printSince(timestamp));
+                }
+                last = secretsRefreshTask.getLastReloadTime();
+                if (last != null) {
+                    long timestamp = last.toEpochMilli();
+                    root.put("lastReloadTimestamp", timestamp);
+                    root.put("lastReloadAge", TimeUtils.printSince(timestamp));
+                }
+            }
+            JsonArray arr = new JsonArray();
+            root.put("secrets", arr);
+
+            List<String> sorted = new ArrayList<>(propertiesFunction.getSecrets());
+            Collections.sort(sorted);
+
+            for (String sec : sorted) {
+                JsonObject jo = new JsonObject();
+                jo.put("name", sec);
+                Instant last = secretsRefreshTask != null ? secretsRefreshTask.getUpdates().get(sec) : null;
+                if (last != null) {
+                    long timestamp = last.toEpochMilli();
+                    jo.put("timestamp", timestamp);
+                    jo.put("age", TimeUtils.printSince(timestamp));
+                }
+                arr.add(jo);
+            }
+        }
+        return root;
+    }
+}